Merge "Fix, MountService now only sends one onShutDownComplete"
diff --git a/cmds/idmap/Android.mk b/cmds/idmap/Android.mk
new file mode 100644
index 0000000..ffa83f2
--- /dev/null
+++ b/cmds/idmap/Android.mk
@@ -0,0 +1,28 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := idmap.cpp create.cpp scan.cpp inspect.cpp
+
+LOCAL_SHARED_LIBRARIES := liblog libutils libandroidfw
+
+LOCAL_MODULE := idmap
+
+LOCAL_C_INCLUDES := external/zlib
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_EXECUTABLE)
diff --git a/cmds/idmap/create.cpp b/cmds/idmap/create.cpp
new file mode 100644
index 0000000..ae35f7b
--- /dev/null
+++ b/cmds/idmap/create.cpp
@@ -0,0 +1,222 @@
+#include "idmap.h"
+
+#include <UniquePtr.h>
+#include <androidfw/AssetManager.h>
+#include <androidfw/ResourceTypes.h>
+#include <androidfw/ZipFileRO.h>
+#include <utils/String8.h>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+
+using namespace android;
+
+namespace {
+ int get_zip_entry_crc(const char *zip_path, const char *entry_name, uint32_t *crc)
+ {
+ UniquePtr<ZipFileRO> zip(ZipFileRO::open(zip_path));
+ if (zip.get() == NULL) {
+ return -1;
+ }
+ ZipEntryRO entry = zip->findEntryByName(entry_name);
+ if (entry == NULL) {
+ return -1;
+ }
+ if (!zip->getEntryInfo(entry, NULL, NULL, NULL, NULL, NULL, (long*)crc)) {
+ return -1;
+ }
+ zip->releaseEntry(entry);
+ return 0;
+ }
+
+ int open_idmap(const char *path)
+ {
+ int fd = TEMP_FAILURE_RETRY(open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644));
+ if (fd == -1) {
+ ALOGD("error: open %s: %s\n", path, strerror(errno));
+ goto fail;
+ }
+ if (fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0) {
+ ALOGD("error: fchmod %s: %s\n", path, strerror(errno));
+ goto fail;
+ }
+ if (TEMP_FAILURE_RETRY(flock(fd, LOCK_EX | LOCK_NB)) != 0) {
+ ALOGD("error: flock %s: %s\n", path, strerror(errno));
+ goto fail;
+ }
+
+ return fd;
+fail:
+ if (fd != -1) {
+ close(fd);
+ unlink(path);
+ }
+ return -1;
+ }
+
+ int write_idmap(int fd, const uint32_t *data, size_t size)
+ {
+ if (lseek(fd, SEEK_SET, 0) < 0) {
+ return -1;
+ }
+ size_t bytesLeft = size;
+ while (bytesLeft > 0) {
+ ssize_t w = TEMP_FAILURE_RETRY(write(fd, data + size - bytesLeft, bytesLeft));
+ if (w < 0) {
+ fprintf(stderr, "error: write: %s\n", strerror(errno));
+ return -1;
+ }
+ bytesLeft -= w;
+ }
+ return 0;
+ }
+
+ bool is_idmap_stale_fd(const char *target_apk_path, const char *overlay_apk_path, int idmap_fd)
+ {
+ static const size_t N = ResTable::IDMAP_HEADER_SIZE_BYTES;
+ struct stat st;
+ if (fstat(idmap_fd, &st) == -1) {
+ return true;
+ }
+ if (st.st_size < N) {
+ // file is empty or corrupt
+ return true;
+ }
+
+ char buf[N];
+ ssize_t bytesLeft = N;
+ if (lseek(idmap_fd, SEEK_SET, 0) < 0) {
+ return true;
+ }
+ for (;;) {
+ ssize_t r = TEMP_FAILURE_RETRY(read(idmap_fd, buf + N - bytesLeft, bytesLeft));
+ if (r < 0) {
+ return true;
+ }
+ bytesLeft -= r;
+ if (bytesLeft == 0) {
+ break;
+ }
+ if (r == 0) {
+ // "shouldn't happen"
+ return true;
+ }
+ }
+
+ uint32_t cached_target_crc, cached_overlay_crc;
+ String8 cached_target_path, cached_overlay_path;
+ if (!ResTable::getIdmapInfo(buf, N, &cached_target_crc, &cached_overlay_crc,
+ &cached_target_path, &cached_overlay_path)) {
+ return true;
+ }
+
+ if (cached_target_path != target_apk_path) {
+ return true;
+ }
+ if (cached_overlay_path != overlay_apk_path) {
+ return true;
+ }
+
+ uint32_t actual_target_crc, actual_overlay_crc;
+ if (get_zip_entry_crc(target_apk_path, AssetManager::RESOURCES_FILENAME,
+ &actual_target_crc) == -1) {
+ return true;
+ }
+ if (get_zip_entry_crc(overlay_apk_path, AssetManager::RESOURCES_FILENAME,
+ &actual_overlay_crc) == -1) {
+ return true;
+ }
+
+ return cached_target_crc != actual_target_crc || cached_overlay_crc != actual_overlay_crc;
+ }
+
+ bool is_idmap_stale_path(const char *target_apk_path, const char *overlay_apk_path,
+ const char *idmap_path)
+ {
+ struct stat st;
+ if (stat(idmap_path, &st) == -1) {
+ // non-existing idmap is always stale; on other errors, abort idmap generation
+ return errno == ENOENT;
+ }
+
+ int idmap_fd = TEMP_FAILURE_RETRY(open(idmap_path, O_RDONLY));
+ if (idmap_fd == -1) {
+ return false;
+ }
+ bool is_stale = is_idmap_stale_fd(target_apk_path, overlay_apk_path, idmap_fd);
+ close(idmap_fd);
+ return is_stale;
+ }
+
+ int create_idmap(const char *target_apk_path, const char *overlay_apk_path,
+ uint32_t **data, size_t *size)
+ {
+ uint32_t target_crc, overlay_crc;
+ if (get_zip_entry_crc(target_apk_path, AssetManager::RESOURCES_FILENAME,
+ &target_crc) == -1) {
+ return -1;
+ }
+ if (get_zip_entry_crc(overlay_apk_path, AssetManager::RESOURCES_FILENAME,
+ &overlay_crc) == -1) {
+ return -1;
+ }
+
+ AssetManager am;
+ bool b = am.createIdmap(target_apk_path, overlay_apk_path, target_crc, overlay_crc,
+ data, size);
+ return b ? 0 : -1;
+ }
+
+ int create_and_write_idmap(const char *target_apk_path, const char *overlay_apk_path,
+ int fd, bool check_if_stale)
+ {
+ if (check_if_stale) {
+ if (!is_idmap_stale_fd(target_apk_path, overlay_apk_path, fd)) {
+ // already up to date -- nothing to do
+ return 0;
+ }
+ }
+
+ uint32_t *data = NULL;
+ size_t size;
+
+ if (create_idmap(target_apk_path, overlay_apk_path, &data, &size) == -1) {
+ return -1;
+ }
+
+ if (write_idmap(fd, data, size) == -1) {
+ free(data);
+ return -1;
+ }
+
+ free(data);
+ return 0;
+ }
+}
+
+int idmap_create_path(const char *target_apk_path, const char *overlay_apk_path,
+ const char *idmap_path)
+{
+ if (!is_idmap_stale_path(target_apk_path, overlay_apk_path, idmap_path)) {
+ // already up to date -- nothing to do
+ return EXIT_SUCCESS;
+ }
+
+ int fd = open_idmap(idmap_path);
+ if (fd == -1) {
+ return EXIT_FAILURE;
+ }
+
+ int r = create_and_write_idmap(target_apk_path, overlay_apk_path, fd, false);
+ close(fd);
+ if (r != 0) {
+ unlink(idmap_path);
+ }
+ return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+int idmap_create_fd(const char *target_apk_path, const char *overlay_apk_path, int fd)
+{
+ return create_and_write_idmap(target_apk_path, overlay_apk_path, fd, true) == 0 ?
+ EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/cmds/idmap/idmap.cpp b/cmds/idmap/idmap.cpp
new file mode 100644
index 0000000..46c0edc
--- /dev/null
+++ b/cmds/idmap/idmap.cpp
@@ -0,0 +1,237 @@
+#include "idmap.h"
+
+#include <private/android_filesystem_config.h> // for AID_SYSTEM
+
+#include <stdlib.h>
+#include <string.h>
+
+namespace {
+ const char *usage = "NAME\n\
+ idmap - create or display idmap files\n\
+\n\
+SYNOPSIS \n\
+ idmap --help \n\
+ idmap --fd target overlay fd \n\
+ idmap --path target overlay idmap \n\
+ idmap --scan dir-to-scan target-to-look-for target dir-to-hold-idmaps \n\
+ idmap --inspect idmap \n\
+\n\
+DESCRIPTION \n\
+ Idmap files play an integral part in the runtime resource overlay framework. An idmap \n\
+ file contains a mapping of resource identifiers between overlay package and its target \n\
+ package; this mapping is used during resource lookup. Idmap files also act as control \n\
+ files by their existence: if not present, the corresponding overlay package is ignored \n\
+ when the resource context is created. \n\
+\n\
+ Idmap files are stored in /data/resource-cache. For each pair (target package, overlay \n\
+ package), there exists exactly one idmap file, or none if the overlay should not be used. \n\
+\n\
+NOMENCLATURE \n\
+ target: the original, non-overlay, package. Each target package may be associated with \n\
+ any number of overlay packages. \n\
+\n\
+ overlay: an overlay package. Each overlay package is associated with exactly one target \n\
+ package, specified in the overlay's manifest using the <overlay target=\"...\"/> \n\
+ tag. \n\
+\n\
+OPTIONS \n\
+ --help: display this help \n\
+\n\
+ --fd: create idmap for target package 'target' (path to apk) and overlay package 'overlay' \n\
+ (path to apk); write results to file descriptor 'fd' (integer). This invocation \n\
+ version is intended to be used by a parent process with higher privileges to call \n\
+ idmap in a controlled way: the parent will open a suitable file descriptor, fork, \n\
+ drop its privileges and exec. This tool will continue execution without the extra \n\
+ privileges, but still have write access to a file it could not have opened on its \n\
+ own. \n\
+\n\
+ --path: create idmap for target package 'target' (path to apk) and overlay package \n\
+ 'overlay' (path to apk); write results to 'idmap' (path). \n\
+\n\
+ --scan: non-recursively search directory 'dir-to-scan' (path) for overlay packages with \n\
+ target package 'target-to-look-for' (package name) present at 'target' (path to \n\
+ apk). For each overlay package found, create an idmap file in 'dir-to-hold-idmaps' \n\
+ (path). \n\
+\n\
+ --inspect: decode the binary format of 'idmap' (path) and display the contents in a \n\
+ debug-friendly format. \n\
+\n\
+EXAMPLES \n\
+ Create an idmap file: \n\
+\n\
+ $ adb shell idmap --path /system/app/target.apk \\ \n\
+ /vendor/overlay/overlay.apk \\ \n\
+ /data/resource-cache/vendor@overlay@overlay.apk@idmap \n\
+\n\
+ Display an idmap file: \n\
+\n\
+ $ adb shell idmap --inspect /data/resource-cache/vendor@overlay@overlay.apk@idmap \n\
+ SECTION ENTRY VALUE OFFSET COMMENT \n\
+ IDMAP HEADER magic 0x706d6469 0x0 \n\
+ base crc 0x484aa77f 0x1 \n\
+ overlay crc 0x03c66fa5 0x2 \n\
+ base path .......... 0x03-0x42 /system/app/target.apk \n\
+ overlay path .......... 0x43-0x82 /vendor/overlay/overlay.apk \n\
+ DATA HEADER types count 0x00000003 0x83 \n\
+ padding 0x00000000 0x84 \n\
+ type offset 0x00000004 0x85 absolute offset 0x87, xml \n\
+ type offset 0x00000007 0x86 absolute offset 0x8a, string \n\
+ DATA BLOCK entry count 0x00000001 0x87 \n\
+ entry offset 0x00000000 0x88 \n\
+ entry 0x7f020000 0x89 xml/integer \n\
+ DATA BLOCK entry count 0x00000002 0x8a \n\
+ entry offset 0x00000000 0x8b \n\
+ entry 0x7f030000 0x8c string/str \n\
+ entry 0x7f030001 0x8d string/str2 \n\
+\n\
+ In this example, the overlay package provides three alternative resource values:\n\
+ xml/integer, string/str and string/str2.\n\
+\n\
+NOTES \n\
+ This tool and its expected invocation from installd is modelled on dexopt.";
+
+ bool verify_directory_readable(const char *path)
+ {
+ return access(path, R_OK | X_OK) == 0;
+ }
+
+ bool verify_directory_writable(const char *path)
+ {
+ return access(path, W_OK) == 0;
+ }
+
+ bool verify_file_readable(const char *path)
+ {
+ return access(path, R_OK) == 0;
+ }
+
+ bool verify_root_or_system()
+ {
+ uid_t uid = getuid();
+ gid_t gid = getgid();
+
+ return (uid == 0 && gid == 0) || (uid == AID_SYSTEM && gid == AID_SYSTEM);
+ }
+
+ int maybe_create_fd(const char *target_apk_path, const char *overlay_apk_path,
+ const char *idmap_str)
+ {
+ // anyone (not just root or system) may do --fd -- the file has
+ // already been opened by someone else on our behalf
+
+ char *endptr;
+ int idmap_fd = strtol(idmap_str, &endptr, 10);
+ if (*endptr != '\0') {
+ fprintf(stderr, "error: failed to parse file descriptor argument %s\n", idmap_str);
+ return -1;
+ }
+
+ if (!verify_file_readable(target_apk_path)) {
+ ALOGD("error: failed to read apk %s: %s\n", target_apk_path, strerror(errno));
+ return -1;
+ }
+
+ if (!verify_file_readable(overlay_apk_path)) {
+ ALOGD("error: failed to read apk %s: %s\n", overlay_apk_path, strerror(errno));
+ return -1;
+ }
+
+ return idmap_create_fd(target_apk_path, overlay_apk_path, idmap_fd);
+ }
+
+ int maybe_create_path(const char *target_apk_path, const char *overlay_apk_path,
+ const char *idmap_path)
+ {
+ if (!verify_root_or_system()) {
+ fprintf(stderr, "error: permission denied: not user root or user system\n");
+ return -1;
+ }
+
+ if (!verify_file_readable(target_apk_path)) {
+ ALOGD("error: failed to read apk %s: %s\n", target_apk_path, strerror(errno));
+ return -1;
+ }
+
+ if (!verify_file_readable(overlay_apk_path)) {
+ ALOGD("error: failed to read apk %s: %s\n", overlay_apk_path, strerror(errno));
+ return -1;
+ }
+
+ return idmap_create_path(target_apk_path, overlay_apk_path, idmap_path);
+ }
+
+ int maybe_scan(const char *overlay_dir, const char *target_package_name,
+ const char *target_apk_path, const char *idmap_dir)
+ {
+ if (!verify_root_or_system()) {
+ fprintf(stderr, "error: permission denied: not user root or user system\n");
+ return -1;
+ }
+
+ if (!verify_directory_readable(overlay_dir)) {
+ ALOGD("error: no read access to %s: %s\n", overlay_dir, strerror(errno));
+ return -1;
+ }
+
+ if (!verify_file_readable(target_apk_path)) {
+ ALOGD("error: failed to read apk %s: %s\n", target_apk_path, strerror(errno));
+ return -1;
+ }
+
+ if (!verify_directory_writable(idmap_dir)) {
+ ALOGD("error: no write access to %s: %s\n", idmap_dir, strerror(errno));
+ return -1;
+ }
+
+ return idmap_scan(overlay_dir, target_package_name, target_apk_path, idmap_dir);
+ }
+
+ int maybe_inspect(const char *idmap_path)
+ {
+ // anyone (not just root or system) may do --inspect
+ if (!verify_file_readable(idmap_path)) {
+ ALOGD("error: failed to read idmap %s: %s\n", idmap_path, strerror(errno));
+ return -1;
+ }
+ return idmap_inspect(idmap_path);
+ }
+}
+
+int main(int argc, char **argv)
+{
+#if 0
+ {
+ char buf[1024];
+ buf[0] = '\0';
+ for (int i = 0; i < argc; ++i) {
+ strncat(buf, argv[i], sizeof(buf) - 1);
+ strncat(buf, " ", sizeof(buf) - 1);
+ }
+ ALOGD("%s:%d: uid=%d gid=%d argv=%s\n", __FILE__, __LINE__, getuid(), getgid(), buf);
+ }
+#endif
+
+ if (argc == 2 && !strcmp(argv[1], "--help")) {
+ printf("%s\n", usage);
+ return 0;
+ }
+
+ if (argc == 5 && !strcmp(argv[1], "--fd")) {
+ return maybe_create_fd(argv[2], argv[3], argv[4]);
+ }
+
+ if (argc == 5 && !strcmp(argv[1], "--path")) {
+ return maybe_create_path(argv[2], argv[3], argv[4]);
+ }
+
+ if (argc == 6 && !strcmp(argv[1], "--scan")) {
+ return maybe_scan(argv[2], argv[3], argv[4], argv[5]);
+ }
+
+ if (argc == 3 && !strcmp(argv[1], "--inspect")) {
+ return maybe_inspect(argv[2]);
+ }
+
+ fprintf(stderr, "Usage: don't use this (cf dexopt usage).\n");
+ return EXIT_FAILURE;
+}
diff --git a/cmds/idmap/idmap.h b/cmds/idmap/idmap.h
new file mode 100644
index 0000000..f507dd8
--- /dev/null
+++ b/cmds/idmap/idmap.h
@@ -0,0 +1,34 @@
+#ifndef _IDMAP_H_
+#define _IDMAP_H_
+
+#define LOG_TAG "idmap"
+
+#include <utils/Log.h>
+
+#include <errno.h>
+#include <stdio.h>
+
+#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
+
+int idmap_create_path(const char *target_apk_path, const char *overlay_apk_path,
+ const char *idmap_path);
+
+int idmap_create_fd(const char *target_apk_path, const char *overlay_apk_path, int fd);
+
+// Regarding target_package_name: the idmap_scan implementation should
+// be able to extract this from the manifest in target_apk_path,
+// simplifying the external API.
+int idmap_scan(const char *overlay_dir, const char *target_package_name,
+ const char *target_apk_path, const char *idmap_dir);
+
+int idmap_inspect(const char *idmap_path);
+
+#endif // _IDMAP_H_
diff --git a/cmds/idmap/inspect.cpp b/cmds/idmap/inspect.cpp
new file mode 100644
index 0000000..a59f5d3
--- /dev/null
+++ b/cmds/idmap/inspect.cpp
@@ -0,0 +1,291 @@
+#include "idmap.h"
+
+#include <androidfw/AssetManager.h>
+#include <androidfw/ResourceTypes.h>
+#include <utils/String8.h>
+
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+using namespace android;
+
+#define NEXT(b, i, o) do { if (buf.next(&i, &o) < 0) { return -1; } } while (0)
+
+namespace {
+ static const uint32_t IDMAP_MAGIC = 0x706d6469;
+ static const size_t PATH_LENGTH = 256;
+ static const uint32_t IDMAP_HEADER_SIZE = (3 + 2 * (PATH_LENGTH / sizeof(uint32_t)));
+
+ void printe(const char *fmt, ...);
+
+ class IdmapBuffer {
+ private:
+ char *buf_;
+ size_t len_;
+ mutable size_t pos_;
+ public:
+ IdmapBuffer() : buf_((char *)MAP_FAILED), len_(0), pos_(0) {}
+
+ ~IdmapBuffer() {
+ if (buf_ != MAP_FAILED) {
+ munmap(buf_, len_);
+ }
+ }
+
+ int init(const char *idmap_path)
+ {
+ struct stat st;
+ int fd;
+
+ if (stat(idmap_path, &st) < 0) {
+ printe("failed to stat idmap '%s': %s\n", idmap_path, strerror(errno));
+ return -1;
+ }
+ len_ = st.st_size;
+ if ((fd = TEMP_FAILURE_RETRY(open(idmap_path, O_RDONLY))) < 0) {
+ printe("failed to open idmap '%s': %s\n", idmap_path, strerror(errno));
+ return -1;
+ }
+ if ((buf_ = (char*)mmap(NULL, len_, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
+ close(fd);
+ printe("failed to mmap idmap: %s\n", strerror(errno));
+ return -1;
+ }
+ close(fd);
+ return 0;
+ }
+
+ int next(uint32_t *i, uint32_t *offset) const
+ {
+ if (!buf_) {
+ printe("failed to read next uint32_t: buffer not initialized\n");
+ return -1;
+ }
+ if (pos_ + 4 > len_) {
+ printe("failed to read next uint32_t: end of buffer reached at pos=0x%08x\n",
+ pos_);
+ return -1;
+ }
+ *offset = pos_ / sizeof(uint32_t);
+ char a = buf_[pos_++];
+ char b = buf_[pos_++];
+ char c = buf_[pos_++];
+ char d = buf_[pos_++];
+ *i = (d << 24) | (c << 16) | (b << 8) | a;
+ return 0;
+ }
+
+ int nextPath(char *b, uint32_t *offset_start, uint32_t *offset_end) const
+ {
+ if (!buf_) {
+ printe("failed to read next path: buffer not initialized\n");
+ return -1;
+ }
+ if (pos_ + PATH_LENGTH > len_) {
+ printe("failed to read next path: end of buffer reached at pos=0x%08x\n", pos_);
+ return -1;
+ }
+ memcpy(b, buf_ + pos_, PATH_LENGTH);
+ *offset_start = pos_ / sizeof(uint32_t);
+ pos_ += PATH_LENGTH;
+ *offset_end = pos_ / sizeof(uint32_t) - 1;
+ return 0;
+ }
+ };
+
+ void printe(const char *fmt, ...)
+ {
+ va_list ap;
+
+ va_start(ap, fmt);
+ fprintf(stderr, "error: ");
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ }
+
+ void print_header()
+ {
+ printf("SECTION ENTRY VALUE OFFSET COMMENT\n");
+ }
+
+ void print(const char *section, const char *subsection, uint32_t value, uint32_t offset,
+ const char *fmt, ...)
+ {
+ va_list ap;
+
+ va_start(ap, fmt);
+ printf("%-12s %-12s 0x%08x 0x%-4x ", section, subsection, value, offset);
+ vprintf(fmt, ap);
+ printf("\n");
+ va_end(ap);
+ }
+
+ void print_path(const char *section, const char *subsection, uint32_t offset_start,
+ uint32_t offset_end, const char *fmt, ...)
+ {
+ va_list ap;
+
+ va_start(ap, fmt);
+ printf("%-12s %-12s .......... 0x%02x-0x%02x ", section, subsection, offset_start,
+ offset_end);
+ vprintf(fmt, ap);
+ printf("\n");
+ va_end(ap);
+ }
+
+ int resource_metadata(const AssetManager& am, uint32_t res_id,
+ String8 *package, String8 *type, String8 *name)
+ {
+ const ResTable& rt = am.getResources();
+ struct ResTable::resource_name data;
+ if (!rt.getResourceName(res_id, false, &data)) {
+ printe("failed to get resource name id=0x%08x\n", res_id);
+ return -1;
+ }
+ if (package) {
+ *package = String8(String16(data.package, data.packageLen));
+ }
+ if (type) {
+ *type = String8(String16(data.type, data.typeLen));
+ }
+ if (name) {
+ *name = String8(String16(data.name, data.nameLen));
+ }
+ return 0;
+ }
+
+ int package_id(const AssetManager& am)
+ {
+ return (am.getResources().getBasePackageId(0)) << 24;
+ }
+
+ int parse_idmap_header(const IdmapBuffer& buf, AssetManager& am)
+ {
+ uint32_t i, o, e;
+ char path[PATH_LENGTH];
+
+ NEXT(buf, i, o);
+ if (i != IDMAP_MAGIC) {
+ printe("not an idmap file: actual magic constant 0x%08x does not match expected magic "
+ "constant 0x%08x\n", i, IDMAP_MAGIC);
+ return -1;
+ }
+ print_header();
+ print("IDMAP HEADER", "magic", i, o, "");
+
+ NEXT(buf, i, o);
+ print("", "base crc", i, o, "");
+
+ NEXT(buf, i, o);
+ print("", "overlay crc", i, o, "");
+
+ if (buf.nextPath(path, &o, &e) < 0) {
+ // printe done from IdmapBuffer::nextPath
+ return -1;
+ }
+ print_path("", "base path", o, e, "%s", path);
+ if (!am.addAssetPath(String8(path), NULL)) {
+ printe("failed to add '%s' as asset path\n", path);
+ return -1;
+ }
+
+ if (buf.nextPath(path, &o, &e) < 0) {
+ // printe done from IdmapBuffer::nextPath
+ return -1;
+ }
+ print_path("", "overlay path", o, e, "%s", path);
+
+ return 0;
+ }
+
+ int parse_data_header(const IdmapBuffer& buf, const AssetManager& am, Vector<uint32_t>& types)
+ {
+ uint32_t i, o;
+ const uint32_t numeric_package = package_id(am);
+
+ NEXT(buf, i, o);
+ print("DATA HEADER", "types count", i, o, "");
+ const uint32_t N = i;
+
+ for (uint32_t j = 0; j < N; ++j) {
+ NEXT(buf, i, o);
+ if (i == 0) {
+ print("", "padding", i, o, "");
+ } else {
+ String8 type;
+ const uint32_t numeric_type = (j + 1) << 16;
+ const uint32_t res_id = numeric_package | numeric_type;
+ if (resource_metadata(am, res_id, NULL, &type, NULL) < 0) {
+ // printe done from resource_metadata
+ return -1;
+ }
+ print("", "type offset", i, o, "absolute offset 0x%02x, %s",
+ i + IDMAP_HEADER_SIZE, type.string());
+ types.add(numeric_type);
+ }
+ }
+
+ return 0;
+ }
+
+ int parse_data_block(const IdmapBuffer& buf, const AssetManager& am, size_t numeric_type)
+ {
+ uint32_t i, o, n, id_offset;
+ const uint32_t numeric_package = package_id(am);
+
+ NEXT(buf, i, o);
+ print("DATA BLOCK", "entry count", i, o, "");
+ n = i;
+
+ NEXT(buf, i, o);
+ print("", "entry offset", i, o, "");
+ id_offset = i;
+
+ for ( ; n > 0; --n) {
+ String8 type, name;
+
+ NEXT(buf, i, o);
+ if (i == 0) {
+ print("", "padding", i, o, "");
+ } else {
+ uint32_t res_id = numeric_package | numeric_type | id_offset;
+ if (resource_metadata(am, res_id, NULL, &type, &name) < 0) {
+ // printe done from resource_metadata
+ return -1;
+ }
+ print("", "entry", i, o, "%s/%s", type.string(), name.string());
+ }
+ ++id_offset;
+ }
+
+ return 0;
+ }
+}
+
+int idmap_inspect(const char *idmap_path)
+{
+ IdmapBuffer buf;
+ if (buf.init(idmap_path) < 0) {
+ // printe done from IdmapBuffer::init
+ return EXIT_FAILURE;
+ }
+ AssetManager am;
+ if (parse_idmap_header(buf, am) < 0) {
+ // printe done from parse_idmap_header
+ return EXIT_FAILURE;
+ }
+ Vector<uint32_t> types;
+ if (parse_data_header(buf, am, types) < 0) {
+ // printe done from parse_data_header
+ return EXIT_FAILURE;
+ }
+ const size_t N = types.size();
+ for (size_t i = 0; i < N; ++i) {
+ if (parse_data_block(buf, am, types.itemAt(i)) < 0) {
+ // printe done from parse_data_block
+ return EXIT_FAILURE;
+ }
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/cmds/idmap/scan.cpp b/cmds/idmap/scan.cpp
new file mode 100644
index 0000000..c5fc941
--- /dev/null
+++ b/cmds/idmap/scan.cpp
@@ -0,0 +1,244 @@
+#include "idmap.h"
+
+#include <UniquePtr.h>
+#include <androidfw/ResourceTypes.h>
+#include <androidfw/StreamingZipInflater.h>
+#include <androidfw/ZipFileRO.h>
+#include <private/android_filesystem_config.h> // for AID_SYSTEM
+#include <utils/SortedVector.h>
+#include <utils/String16.h>
+#include <utils/String8.h>
+
+#include <dirent.h>
+
+#define NO_OVERLAY_TAG (-1000)
+
+using namespace android;
+
+namespace {
+ struct Overlay {
+ Overlay() {}
+ Overlay(const String8& a, const String8& i, int p) :
+ apk_path(a), idmap_path(i), priority(p) {}
+
+ bool operator<(Overlay const& rhs) const
+ {
+ // Note: order is reversed by design
+ return rhs.priority < priority;
+ }
+
+ String8 apk_path;
+ String8 idmap_path;
+ int priority;
+ };
+
+ bool writePackagesList(const char *filename, const SortedVector<Overlay>& overlayVector)
+ {
+ FILE* fout = fopen(filename, "w");
+ if (fout == NULL) {
+ return false;
+ }
+
+ for (size_t i = 0; i < overlayVector.size(); ++i) {
+ const Overlay& overlay = overlayVector[i];
+ fprintf(fout, "%s %s\n", overlay.apk_path.string(), overlay.idmap_path.string());
+ }
+
+ fclose(fout);
+
+ // Make file world readable since Zygote (running as root) will read
+ // it when creating the initial AssetManger object
+ const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; // 0644
+ if (chmod(filename, mode) == -1) {
+ unlink(filename);
+ return false;
+ }
+
+ return true;
+ }
+
+ String8 flatten_path(const char *path)
+ {
+ String16 tmp(path);
+ tmp.replaceAll('/', '@');
+ return String8(tmp);
+ }
+
+ int mkdir_p(const String8& path, uid_t uid, gid_t gid)
+ {
+ static const mode_t mode =
+ S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH;
+ struct stat st;
+
+ if (stat(path.string(), &st) == 0) {
+ return 0;
+ }
+ if (mkdir_p(path.getPathDir(), uid, gid) < 0) {
+ return -1;
+ }
+ if (mkdir(path.string(), 0755) != 0) {
+ return -1;
+ }
+ if (chown(path.string(), uid, gid) == -1) {
+ return -1;
+ }
+ if (chmod(path.string(), mode) == -1) {
+ return -1;
+ }
+ return 0;
+ }
+
+ int parse_overlay_tag(const ResXMLTree& parser, const char *target_package_name)
+ {
+ const size_t N = parser.getAttributeCount();
+ String16 target;
+ int priority = -1;
+ for (size_t i = 0; i < N; ++i) {
+ size_t len;
+ String16 key(parser.getAttributeName(i, &len));
+ if (key == String16("targetPackage")) {
+ const uint16_t *p = parser.getAttributeStringValue(i, &len);
+ if (p) {
+ target = String16(p, len);
+ }
+ } else if (key == String16("priority")) {
+ Res_value v;
+ if (parser.getAttributeValue(i, &v) == sizeof(Res_value)) {
+ priority = v.data;
+ if (priority < 0 || priority > 9999) {
+ return -1;
+ }
+ }
+ }
+ }
+ if (target == String16(target_package_name)) {
+ return priority;
+ }
+ return NO_OVERLAY_TAG;
+ }
+
+ int parse_manifest(const void *data, size_t size, const char *target_package_name)
+ {
+ ResXMLTree parser(data, size);
+ if (parser.getError() != NO_ERROR) {
+ ALOGD("%s failed to init xml parser, error=0x%08x\n", __FUNCTION__, parser.getError());
+ return -1;
+ }
+
+ ResXMLParser::event_code_t type;
+ do {
+ type = parser.next();
+ if (type == ResXMLParser::START_TAG) {
+ size_t len;
+ String16 tag(parser.getElementName(&len));
+ if (tag == String16("overlay")) {
+ return parse_overlay_tag(parser, target_package_name);
+ }
+ }
+ } while (type != ResXMLParser::BAD_DOCUMENT && type != ResXMLParser::END_DOCUMENT);
+
+ return NO_OVERLAY_TAG;
+ }
+
+ int parse_apk(const char *path, const char *target_package_name)
+ {
+ UniquePtr<ZipFileRO> zip(ZipFileRO::open(path));
+ if (zip.get() == NULL) {
+ ALOGW("%s: failed to open zip %s\n", __FUNCTION__, path);
+ return -1;
+ }
+ ZipEntryRO entry;
+ if ((entry = zip->findEntryByName("AndroidManifest.xml")) == NULL) {
+ ALOGW("%s: failed to find entry AndroidManifest.xml\n", __FUNCTION__);
+ return -1;
+ }
+ size_t uncompLen = 0;
+ int method;
+ if (!zip->getEntryInfo(entry, &method, &uncompLen, NULL, NULL, NULL, NULL)) {
+ ALOGW("%s: failed to read entry info\n", __FUNCTION__);
+ return -1;
+ }
+ if (method != ZipFileRO::kCompressDeflated) {
+ ALOGW("%s: cannot handle zip compression method %d\n", __FUNCTION__, method);
+ return -1;
+ }
+ FileMap *dataMap = zip->createEntryFileMap(entry);
+ if (!dataMap) {
+ ALOGW("%s: failed to create FileMap\n", __FUNCTION__);
+ return -1;
+ }
+ char *buf = new char[uncompLen];
+ if (NULL == buf) {
+ ALOGW("%s: failed to allocate %d byte\n", __FUNCTION__, uncompLen);
+ dataMap->release();
+ return -1;
+ }
+ StreamingZipInflater inflater(dataMap, uncompLen);
+ if (inflater.read(buf, uncompLen) < 0) {
+ ALOGW("%s: failed to inflate %d byte\n", __FUNCTION__, uncompLen);
+ delete[] buf;
+ dataMap->release();
+ return -1;
+ }
+
+ int priority = parse_manifest(buf, uncompLen, target_package_name);
+ delete[] buf;
+ dataMap->release();
+ return priority;
+ }
+}
+
+int idmap_scan(const char *overlay_dir, const char *target_package_name,
+ const char *target_apk_path, const char *idmap_dir)
+{
+ String8 filename = String8(idmap_dir);
+ filename.appendPath("overlays.list");
+ if (unlink(filename.string()) != 0 && errno != ENOENT) {
+ return EXIT_FAILURE;
+ }
+
+ DIR *dir = opendir(overlay_dir);
+ if (dir == NULL) {
+ return EXIT_FAILURE;
+ }
+
+ SortedVector<Overlay> overlayVector;
+ struct dirent *dirent;
+ while ((dirent = readdir(dir)) != NULL) {
+ struct stat st;
+ char overlay_apk_path[PATH_MAX + 1];
+ snprintf(overlay_apk_path, PATH_MAX, "%s/%s", overlay_dir, dirent->d_name);
+ if (stat(overlay_apk_path, &st) < 0) {
+ continue;
+ }
+ if (!S_ISREG(st.st_mode)) {
+ continue;
+ }
+
+ int priority = parse_apk(overlay_apk_path, target_package_name);
+ if (priority < 0) {
+ continue;
+ }
+
+ String8 idmap_path(idmap_dir);
+ idmap_path.appendPath(flatten_path(overlay_apk_path + 1));
+ idmap_path.append("@idmap");
+
+ if (idmap_create_path(target_apk_path, overlay_apk_path, idmap_path.string()) != 0) {
+ ALOGE("error: failed to create idmap for target=%s overlay=%s idmap=%s\n",
+ target_apk_path, overlay_apk_path, idmap_path.string());
+ continue;
+ }
+
+ Overlay overlay(String8(overlay_apk_path), idmap_path, priority);
+ overlayVector.add(overlay);
+ }
+
+ closedir(dir);
+
+ if (!writePackagesList(filename.string(), overlayVector)) {
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 97baf9a..d3080e5 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1541,11 +1541,11 @@
/**
* Creates the top level resources for the given package.
*/
- Resources getTopLevelResources(String resDir,
+ Resources getTopLevelResources(String resDir, String[] overlayDirs,
int displayId, Configuration overrideConfiguration,
LoadedApk pkgInfo) {
- return mResourcesManager.getTopLevelResources(resDir, displayId, overrideConfiguration,
- pkgInfo.getCompatibilityInfo(), null);
+ return mResourcesManager.getTopLevelResources(resDir, overlayDirs, displayId,
+ overrideConfiguration, pkgInfo.getCompatibilityInfo(), null);
}
final Handler getHandler() {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index b505d4f..a280448 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -774,7 +774,7 @@
}
Resources r = mContext.mMainThread.getTopLevelResources(
app.uid == Process.myUid() ? app.sourceDir : app.publicSourceDir,
- Display.DEFAULT_DISPLAY, null, mContext.mPackageInfo);
+ app.resourceDirs, Display.DEFAULT_DISPLAY, null, mContext.mPackageInfo);
if (r != null) {
return r;
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 190ddb4..8d127c6 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1911,8 +1911,8 @@
ContextImpl c = new ContextImpl();
c.init(mPackageInfo, null, mMainThread);
c.mResources = mResourcesManager.getTopLevelResources(mPackageInfo.getResDir(),
- getDisplayId(), overrideConfiguration, mResources.getCompatibilityInfo(),
- mActivityToken);
+ mPackageInfo.getOverlayDirs(), getDisplayId(), overrideConfiguration,
+ mResources.getCompatibilityInfo(), mActivityToken);
return c;
}
@@ -1929,7 +1929,7 @@
context.mDisplay = display;
DisplayAdjustments daj = getDisplayAdjustments(displayId);
context.mResources = mResourcesManager.getTopLevelResources(mPackageInfo.getResDir(),
- displayId, null, daj.getCompatibilityInfo(), null);
+ mPackageInfo.getOverlayDirs(), displayId, null, daj.getCompatibilityInfo(), null);
return context;
}
@@ -2041,7 +2041,8 @@
mDisplayAdjustments.setCompatibilityInfo(compatInfo);
mDisplayAdjustments.setActivityToken(activityToken);
mResources = mResourcesManager.getTopLevelResources(mPackageInfo.getResDir(),
- Display.DEFAULT_DISPLAY, null, compatInfo, activityToken);
+ mPackageInfo.getOverlayDirs(), Display.DEFAULT_DISPLAY, null, compatInfo,
+ activityToken);
} else {
mDisplayAdjustments.setCompatibilityInfo(packageInfo.getCompatibilityInfo());
mDisplayAdjustments.setActivityToken(activityToken);
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 4239a5d..0115d1b 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -76,6 +76,7 @@
final String mPackageName;
private final String mAppDir;
private final String mResDir;
+ private final String[] mOverlayDirs;
private final String[] mSharedLibraries;
private final String mDataDir;
private final String mLibDir;
@@ -120,6 +121,7 @@
final int myUid = Process.myUid();
mResDir = aInfo.uid == myUid ? aInfo.sourceDir
: aInfo.publicSourceDir;
+ mOverlayDirs = aInfo.resourceDirs;
if (!UserHandle.isSameUser(aInfo.uid, myUid) && !Process.isIsolated()) {
aInfo.dataDir = PackageManager.getDataDirForUser(UserHandle.getUserId(myUid),
mPackageName);
@@ -159,6 +161,7 @@
mPackageName = name;
mAppDir = null;
mResDir = null;
+ mOverlayDirs = null;
mSharedLibraries = null;
mDataDir = null;
mDataDirFile = null;
@@ -471,6 +474,10 @@
return mResDir;
}
+ public String[] getOverlayDirs() {
+ return mOverlayDirs;
+ }
+
public String getDataDir() {
return mDataDir;
}
@@ -485,7 +492,7 @@
public Resources getResources(ActivityThread mainThread) {
if (mResources == null) {
- mResources = mainThread.getTopLevelResources(mResDir,
+ mResources = mainThread.getTopLevelResources(mResDir, mOverlayDirs,
Display.DEFAULT_DISPLAY, null, this);
}
return mResources;
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index f55dba4..728f372 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -147,7 +147,7 @@
* @param compatInfo the compability info. Must not be null.
* @param token the application token for determining stack bounds.
*/
- public Resources getTopLevelResources(String resDir, int displayId,
+ public Resources getTopLevelResources(String resDir, String[] overlayDirs, int displayId,
Configuration overrideConfiguration, CompatibilityInfo compatInfo, IBinder token) {
final float scale = compatInfo.applicationScale;
ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfiguration, scale,
@@ -180,6 +180,12 @@
return null;
}
+ if (overlayDirs != null) {
+ for (String idmapPath : overlayDirs) {
+ assets.addOverlayPath(idmapPath);
+ }
+ }
+
//Slog.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics);
DisplayMetrics dm = getDisplayMetricsLocked(displayId);
Configuration config;
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index af1a6d5..785f2b4 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -227,6 +227,14 @@
/** @hide */
public String requiredAccountType;
+ /**
+ * What package, if any, this package will overlay.
+ *
+ * Package name of target package, or null.
+ * @hide
+ */
+ public String overlayTarget;
+
public PackageInfo() {
}
@@ -270,6 +278,7 @@
dest.writeInt(requiredForAllUsers ? 1 : 0);
dest.writeString(restrictedAccountType);
dest.writeString(requiredAccountType);
+ dest.writeString(overlayTarget);
}
public static final Parcelable.Creator<PackageInfo> CREATOR
@@ -311,5 +320,6 @@
requiredForAllUsers = source.readInt() != 0;
restrictedAccountType = source.readString();
requiredAccountType = source.readString();
+ overlayTarget = source.readString();
}
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4607902..52564eb 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -307,6 +307,7 @@
}
pi.restrictedAccountType = p.mRestrictedAccountType;
pi.requiredAccountType = p.mRequiredAccountType;
+ pi.overlayTarget = p.mOverlayTarget;
pi.firstInstallTime = firstInstallTime;
pi.lastUpdateTime = lastUpdateTime;
if ((flags&PackageManager.GET_GIDS) != 0) {
@@ -490,6 +491,11 @@
public Package parsePackage(File sourceFile, String destCodePath,
DisplayMetrics metrics, int flags) {
+ return parsePackage(sourceFile, destCodePath, metrics, flags, false);
+ }
+
+ public Package parsePackage(File sourceFile, String destCodePath,
+ DisplayMetrics metrics, int flags, boolean trustedOverlay) {
mParseError = PackageManager.INSTALL_SUCCEEDED;
mArchiveSourcePath = sourceFile.getPath();
@@ -542,7 +548,7 @@
Exception errorException = null;
try {
// XXXX todo: need to figure out correct configuration.
- pkg = parsePackage(res, parser, flags, errorText);
+ pkg = parsePackage(res, parser, flags, trustedOverlay, errorText);
} catch (Exception e) {
errorException = e;
mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
@@ -951,8 +957,8 @@
}
private Package parsePackage(
- Resources res, XmlResourceParser parser, int flags, String[] outError)
- throws XmlPullParserException, IOException {
+ Resources res, XmlResourceParser parser, int flags, boolean trustedOverlay,
+ String[] outError) throws XmlPullParserException, IOException {
AttributeSet attrs = parser;
mParseInstrumentationArgs = null;
@@ -1051,6 +1057,31 @@
if (!parseApplication(pkg, res, parser, attrs, flags, outError)) {
return null;
}
+ } else if (tagName.equals("overlay")) {
+ pkg.mTrustedOverlay = trustedOverlay;
+
+ sa = res.obtainAttributes(attrs,
+ com.android.internal.R.styleable.AndroidManifestResourceOverlay);
+ pkg.mOverlayTarget = sa.getString(
+ com.android.internal.R.styleable.AndroidManifestResourceOverlay_targetPackage);
+ pkg.mOverlayPriority = sa.getInt(
+ com.android.internal.R.styleable.AndroidManifestResourceOverlay_priority,
+ -1);
+ sa.recycle();
+
+ if (pkg.mOverlayTarget == null) {
+ outError[0] = "<overlay> does not specify a target package";
+ mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+ return null;
+ }
+ if (pkg.mOverlayPriority < 0 || pkg.mOverlayPriority > 9999) {
+ outError[0] = "<overlay> priority must be between 0 and 9999";
+ mParseError =
+ PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
+ return null;
+ }
+ XmlUtils.skipCurrentTag(parser);
+
} else if (tagName.equals("keys")) {
if (!parseKeys(pkg, res, parser, attrs, outError)) {
return null;
@@ -3546,6 +3577,10 @@
*/
public ManifestDigest manifestDigest;
+ public String mOverlayTarget;
+ public int mOverlayPriority;
+ public boolean mTrustedOverlay;
+
/**
* Data used to feed the KeySetManager
*/
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 780c4be..418bdda 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -90,7 +90,7 @@
mNumRefs = 0;
incRefsLocked(this.hashCode());
}
- init();
+ init(false);
if (localLOGV) Log.v(TAG, "New asset manager: " + this);
ensureSystemAssets();
}
@@ -113,7 +113,7 @@
incRefsLocked(this.hashCode());
}
}
- init();
+ init(true);
if (localLOGV) Log.v(TAG, "New asset manager: " + this);
}
@@ -615,6 +615,16 @@
private native final int addAssetPathNative(String path);
+ /**
+ * Add a set of assets to overlay an already added set of assets.
+ *
+ * This is only intended for application resources. System wide resources
+ * are handled before any Java code is executed.
+ *
+ * {@hide}
+ */
+ public native final int addOverlayPath(String idmapPath);
+
/**
* Add multiple sets of assets to the asset manager at once. See
* {@link #addAssetPath(String)} for more information. Returns array of
@@ -752,7 +762,7 @@
private native final int[] getArrayStringInfo(int arrayRes);
/*package*/ native final int[] getArrayIntResource(int arrayRes);
- private native final void init();
+ private native final void init(boolean isSystem);
private native final void destroy();
private final void incRefsLocked(long id) {
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 7fc364f..3d9daca 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1569,10 +1569,7 @@
String locale = null;
if (mConfiguration.locale != null) {
- locale = mConfiguration.locale.getLanguage();
- if (mConfiguration.locale.getCountry() != null) {
- locale += "-" + mConfiguration.locale.getCountry();
- }
+ locale = mConfiguration.locale.toLanguageTag();
}
int width, height;
if (mMetrics.widthPixels >= mMetrics.heightPixels) {
diff --git a/core/java/android/emoji/EmojiFactory.java b/core/java/android/emoji/EmojiFactory.java
index 8fd8695..aba990d 100644
--- a/core/java/android/emoji/EmojiFactory.java
+++ b/core/java/android/emoji/EmojiFactory.java
@@ -54,7 +54,7 @@
}
// A pointer to native EmojiFactory object.
- private int mNativeEmojiFactory;
+ private long mNativeEmojiFactory;
private String mName;
// Cache.
private Map<Integer, WeakReference<Bitmap>> mCache;
@@ -68,7 +68,7 @@
*
* This can be called from JNI code.
*/
- private EmojiFactory(int nativeEmojiFactory, String name) {
+ private EmojiFactory(long nativeEmojiFactory, String name) {
mNativeEmojiFactory = nativeEmojiFactory;
mName = name;
mCache = new CustomLinkedHashMap<Integer, WeakReference<Bitmap>>();
@@ -272,18 +272,18 @@
// native methods
- private native void nativeDestructor(int factory);
- private native Bitmap nativeGetBitmapFromAndroidPua(int nativeEmojiFactory, int AndroidPua);
- private native int nativeGetAndroidPuaFromVendorSpecificSjis(int nativeEmojiFactory,
+ private native void nativeDestructor(long nativeEmojiFactory);
+ private native Bitmap nativeGetBitmapFromAndroidPua(long nativeEmojiFactory, int AndroidPua);
+ private native int nativeGetAndroidPuaFromVendorSpecificSjis(long nativeEmojiFactory,
char sjis);
- private native int nativeGetVendorSpecificSjisFromAndroidPua(int nativeEmojiFactory,
+ private native int nativeGetVendorSpecificSjisFromAndroidPua(long nativeEmojiFactory,
int pua);
- private native int nativeGetAndroidPuaFromVendorSpecificPua(int nativeEmojiFactory,
+ private native int nativeGetAndroidPuaFromVendorSpecificPua(long nativeEmojiFactory,
int vsp);
- private native int nativeGetVendorSpecificPuaFromAndroidPua(int nativeEmojiFactory,
+ private native int nativeGetVendorSpecificPuaFromAndroidPua(long nativeEmojiFactory,
int pua);
- private native int nativeGetMaximumVendorSpecificPua(int nativeEmojiFactory);
- private native int nativeGetMinimumVendorSpecificPua(int nativeEmojiFactory);
- private native int nativeGetMaximumAndroidPua(int nativeEmojiFactory);
- private native int nativeGetMinimumAndroidPua(int nativeEmojiFactory);
+ private native int nativeGetMaximumVendorSpecificPua(long nativeEmojiFactory);
+ private native int nativeGetMinimumVendorSpecificPua(long nativeEmojiFactory);
+ private native int nativeGetMaximumAndroidPua(long nativeEmojiFactory);
+ private native int nativeGetMinimumAndroidPua(long nativeEmojiFactory);
}
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 6716098..3470790 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -1246,9 +1246,6 @@
} else if (v instanceof Parcelable[]) {
writeInt(VAL_PARCELABLEARRAY);
writeParcelableArray((Parcelable[]) v, 0);
- } else if (v instanceof Object[]) {
- writeInt(VAL_OBJECTARRAY);
- writeArray((Object[]) v);
} else if (v instanceof int[]) {
writeInt(VAL_INTARRAY);
writeIntArray((int[]) v);
@@ -1258,12 +1255,20 @@
} else if (v instanceof Byte) {
writeInt(VAL_BYTE);
writeInt((Byte) v);
- } else if (v instanceof Serializable) {
- // Must be last
- writeInt(VAL_SERIALIZABLE);
- writeSerializable((Serializable) v);
} else {
- throw new RuntimeException("Parcel: unable to marshal value " + v);
+ Class<?> clazz = v.getClass();
+ if (clazz.isArray() && clazz.getComponentType() == Object.class) {
+ // Only pure Object[] are written here, Other arrays of non-primitive types are
+ // handled by serialization as this does not record the component type.
+ writeInt(VAL_OBJECTARRAY);
+ writeArray((Object[]) v);
+ } else if (v instanceof Serializable) {
+ // Must be last
+ writeInt(VAL_SERIALIZABLE);
+ writeSerializable((Serializable) v);
+ } else {
+ throw new RuntimeException("Parcel: unable to marshal value " + v);
+ }
}
}
diff --git a/core/java/android/view/SurfaceSession.java b/core/java/android/view/SurfaceSession.java
index 0dfd94a..3cf5af4 100644
--- a/core/java/android/view/SurfaceSession.java
+++ b/core/java/android/view/SurfaceSession.java
@@ -24,11 +24,11 @@
*/
public final class SurfaceSession {
// Note: This field is accessed by native code.
- private int mNativeClient; // SurfaceComposerClient*
+ private long mNativeClient; // SurfaceComposerClient*
- private static native int nativeCreate();
- private static native void nativeDestroy(int ptr);
- private static native void nativeKill(int ptr);
+ private static native long nativeCreate();
+ private static native void nativeDestroy(long ptr);
+ private static native void nativeKill(long ptr);
/** Create a new connection with the surface flinger. */
public SurfaceSession() {
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 4f3b5b3..f9a1f89 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -224,9 +224,37 @@
ZygoteInit.setCloseOnExec(serverPipeFd, true);
}
+ /**
+ * In order to avoid leaking descriptors to the Zygote child,
+ * the native code must close the two Zygote socket descriptors
+ * in the child process before it switches from Zygote-root to
+ * the UID and privileges of the application being launched.
+ *
+ * In order to avoid "bad file descriptor" errors when the
+ * two LocalSocket objects are closed, the Posix file
+ * descriptors are released via a dup2() call which closes
+ * the socket and substitutes an open descriptor to /dev/null.
+ */
+
+ int [] fdsToClose = { -1, -1 };
+
+ FileDescriptor fd = mSocket.getFileDescriptor();
+
+ if (fd != null) {
+ fdsToClose[0] = fd.getInt$();
+ }
+
+ fd = ZygoteInit.getServerSocketFileDescriptor();
+
+ if (fd != null) {
+ fdsToClose[1] = fd.getInt$();
+ }
+
+ fd = null;
+
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
- parsedArgs.niceName);
+ parsedArgs.niceName, fdsToClose);
} catch (IOException ex) {
logAndPrintError(newStderr, "Exception creating pipe", ex);
} catch (ErrnoException ex) {
@@ -814,6 +842,12 @@
FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
throws ZygoteInit.MethodAndArgsCaller {
+ /**
+ * By the time we get here, the native code has closed the two actual Zygote
+ * socket connections, and substituted /dev/null in their place. The LocalSocket
+ * objects still need to be closed properly.
+ */
+
closeSocket();
ZygoteInit.closeServerSocket();
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 25118e8..cc24ff7 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -201,6 +201,16 @@
sServerSocket = null;
}
+ /**
+ * Return the server socket's underlying file descriptor, so that
+ * ZygoteConnection can pass it to the native code for proper
+ * closure after a child process is forked off.
+ */
+
+ static FileDescriptor getServerSocketFileDescriptor() {
+ return sServerSocket.getFileDescriptor();
+ }
+
private static final int UNPRIVILEGED_UID = 9999;
private static final int UNPRIVILEGED_GID = 9999;
diff --git a/core/jni/android/graphics/pdf/PdfDocument.cpp b/core/jni/android/graphics/pdf/PdfDocument.cpp
index 6175a8f..d54aaa8 100644
--- a/core/jni/android/graphics/pdf/PdfDocument.cpp
+++ b/core/jni/android/graphics/pdf/PdfDocument.cpp
@@ -113,24 +113,24 @@
PageRecord* mCurrentPage;
};
-static jint nativeCreateDocument(JNIEnv* env, jobject thiz) {
- return reinterpret_cast<jint>(new PdfDocument());
+static jlong nativeCreateDocument(JNIEnv* env, jobject thiz) {
+ return reinterpret_cast<jlong>(new PdfDocument());
}
-static jint nativeStartPage(JNIEnv* env, jobject thiz, jint documentPtr,
+static jlong nativeStartPage(JNIEnv* env, jobject thiz, jlong documentPtr,
jint pageWidth, jint pageHeight,
jint contentLeft, jint contentTop, jint contentRight, jint contentBottom) {
PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
- return reinterpret_cast<jint>(document->startPage(pageWidth, pageHeight,
+ return reinterpret_cast<jlong>(document->startPage(pageWidth, pageHeight,
contentLeft, contentTop, contentRight, contentBottom));
}
-static void nativeFinishPage(JNIEnv* env, jobject thiz, jint documentPtr) {
+static void nativeFinishPage(JNIEnv* env, jobject thiz, jlong documentPtr) {
PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
document->finishPage();
}
-static void nativeWriteTo(JNIEnv* env, jobject thiz, jint documentPtr, jobject out,
+static void nativeWriteTo(JNIEnv* env, jobject thiz, jlong documentPtr, jobject out,
jbyteArray chunk) {
PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
SkWStream* skWStream = CreateJavaOutputStreamAdaptor(env, out, chunk);
@@ -138,17 +138,17 @@
delete skWStream;
}
-static void nativeClose(JNIEnv* env, jobject thiz, jint documentPtr) {
+static void nativeClose(JNIEnv* env, jobject thiz, jlong documentPtr) {
PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
document->close();
}
static JNINativeMethod gPdfDocument_Methods[] = {
- {"nativeCreateDocument", "()I", (void*) nativeCreateDocument},
- {"nativeStartPage", "(IIIIIII)I", (void*) nativeStartPage},
- {"nativeFinishPage", "(I)V", (void*) nativeFinishPage},
- {"nativeWriteTo", "(ILjava/io/OutputStream;[B)V", (void*) nativeWriteTo},
- {"nativeClose", "(I)V", (void*) nativeClose}
+ {"nativeCreateDocument", "()J", (void*) nativeCreateDocument},
+ {"nativeStartPage", "(JIIIIII)J", (void*) nativeStartPage},
+ {"nativeFinishPage", "(J)V", (void*) nativeFinishPage},
+ {"nativeWriteTo", "(JLjava/io/OutputStream;[B)V", (void*) nativeWriteTo},
+ {"nativeClose", "(J)V", (void*) nativeClose}
};
int register_android_graphics_pdf_PdfDocument(JNIEnv* env) {
diff --git a/core/jni/android_database_SQLiteGlobal.cpp b/core/jni/android_database_SQLiteGlobal.cpp
index acc2276..89d64fa 100644
--- a/core/jni/android_database_SQLiteGlobal.cpp
+++ b/core/jni/android_database_SQLiteGlobal.cpp
@@ -39,7 +39,7 @@
bool verboseLog = !!data;
if (iErrCode == 0 || iErrCode == SQLITE_CONSTRAINT || iErrCode == SQLITE_SCHEMA) {
if (verboseLog) {
- ALOGV(LOG_VERBOSE, SQLITE_LOG_TAG, "(%d) %s\n", iErrCode, zMsg);
+ ALOG(LOG_VERBOSE, SQLITE_LOG_TAG, "(%d) %s\n", iErrCode, zMsg);
}
} else {
ALOG(LOG_ERROR, SQLITE_LOG_TAG, "(%d) %s\n", iErrCode, zMsg);
diff --git a/core/jni/android_emoji_EmojiFactory.cpp b/core/jni/android_emoji_EmojiFactory.cpp
index 5276934..f127d29 100644
--- a/core/jni/android_emoji_EmojiFactory.cpp
+++ b/core/jni/android_emoji_EmojiFactory.cpp
@@ -104,7 +104,7 @@
static jobject create_java_EmojiFactory(
JNIEnv* env, EmojiFactory* factory, jstring name) {
jobject obj = env->NewObject(gEmojiFactory_class, gEmojiFactory_constructorMethodID,
- static_cast<jint>(reinterpret_cast<uintptr_t>(factory)), name);
+ reinterpret_cast<jlong>(factory), name);
if (env->ExceptionCheck() != 0) {
ALOGE("*** Uncaught exception returned from Java call!\n");
env->ExceptionDescribe();
@@ -155,7 +155,7 @@
}
static jobject android_emoji_EmojiFactory_getBitmapFromAndroidPua(
- JNIEnv* env, jobject clazz, jint nativeEmojiFactory, jint pua) {
+ JNIEnv* env, jobject clazz, jlong nativeEmojiFactory, jint pua) {
EmojiFactory *factory = reinterpret_cast<EmojiFactory *>(nativeEmojiFactory);
int size;
@@ -175,7 +175,7 @@
}
static void android_emoji_EmojiFactory_destructor(
- JNIEnv* env, jobject obj, jint nativeEmojiFactory) {
+ JNIEnv* env, jobject obj, jlong nativeEmojiFactory) {
/*
// Must not delete this object!!
EmojiFactory *factory = reinterpret_cast<EmojiFactory *>(nativeEmojiFactory);
@@ -184,49 +184,49 @@
}
static jint android_emoji_EmojiFactory_getAndroidPuaFromVendorSpecificSjis(
- JNIEnv* env, jobject obj, jint nativeEmojiFactory, jchar sjis) {
+ JNIEnv* env, jobject obj, jlong nativeEmojiFactory, jchar sjis) {
EmojiFactory *factory = reinterpret_cast<EmojiFactory *>(nativeEmojiFactory);
return factory->GetAndroidPuaFromVendorSpecificSjis(sjis);
}
static jint android_emoji_EmojiFactory_getVendorSpecificSjisFromAndroidPua(
- JNIEnv* env, jobject obj, jint nativeEmojiFactory, jint pua) {
+ JNIEnv* env, jobject obj, jlong nativeEmojiFactory, jint pua) {
EmojiFactory *factory = reinterpret_cast<EmojiFactory *>(nativeEmojiFactory);
return factory->GetVendorSpecificSjisFromAndroidPua(pua);
}
static jint android_emoji_EmojiFactory_getAndroidPuaFromVendorSpecificPua(
- JNIEnv* env, jobject obj, jint nativeEmojiFactory, jint vsu) {
+ JNIEnv* env, jobject obj, jlong nativeEmojiFactory, jint vsu) {
EmojiFactory *factory = reinterpret_cast<EmojiFactory *>(nativeEmojiFactory);
return factory->GetAndroidPuaFromVendorSpecificPua(vsu);
}
static jint android_emoji_EmojiFactory_getVendorSpecificPuaFromAndroidPua(
- JNIEnv* env, jobject obj, jint nativeEmojiFactory, jint pua) {
+ JNIEnv* env, jobject obj, jlong nativeEmojiFactory, jint pua) {
EmojiFactory *factory = reinterpret_cast<EmojiFactory *>(nativeEmojiFactory);
return factory->GetVendorSpecificPuaFromAndroidPua(pua);
}
static jint android_emoji_EmojiFactory_getMaximumVendorSpecificPua(
- JNIEnv* env, jobject obj, jint nativeEmojiFactory) {
+ JNIEnv* env, jobject obj, jlong nativeEmojiFactory) {
EmojiFactory *factory = reinterpret_cast<EmojiFactory *>(nativeEmojiFactory);
return factory->GetMaximumVendorSpecificPua();
}
static jint android_emoji_EmojiFactory_getMinimumVendorSpecificPua(
- JNIEnv* env, jobject obj, jint nativeEmojiFactory) {
+ JNIEnv* env, jobject obj, jlong nativeEmojiFactory) {
EmojiFactory *factory = reinterpret_cast<EmojiFactory *>(nativeEmojiFactory);
return factory->GetMinimumVendorSpecificPua();
}
static jint android_emoji_EmojiFactory_getMaximumAndroidPua(
- JNIEnv* env, jobject obj, jint nativeEmojiFactory) {
+ JNIEnv* env, jobject obj, jlong nativeEmojiFactory) {
EmojiFactory *factory = reinterpret_cast<EmojiFactory *>(nativeEmojiFactory);
return factory->GetMaximumAndroidPua();
}
static jint android_emoji_EmojiFactory_getMinimumAndroidPua(
- JNIEnv* env, jobject obj, jint nativeEmojiFactory) {
+ JNIEnv* env, jobject obj, jlong nativeEmojiFactory) {
EmojiFactory *factory = reinterpret_cast<EmojiFactory *>(nativeEmojiFactory);
return factory->GetMinimumAndroidPua();
}
@@ -236,25 +236,25 @@
(void*)android_emoji_EmojiFactory_newInstance},
{ "newAvailableInstance", "()Landroid/emoji/EmojiFactory;",
(void*)android_emoji_EmojiFactory_newAvailableInstance},
- { "nativeDestructor", "(I)V",
+ { "nativeDestructor", "(J)V",
(void*)android_emoji_EmojiFactory_destructor},
- { "nativeGetBitmapFromAndroidPua", "(II)Landroid/graphics/Bitmap;",
+ { "nativeGetBitmapFromAndroidPua", "(JI)Landroid/graphics/Bitmap;",
(void*)android_emoji_EmojiFactory_getBitmapFromAndroidPua},
- { "nativeGetAndroidPuaFromVendorSpecificSjis", "(IC)I",
+ { "nativeGetAndroidPuaFromVendorSpecificSjis", "(JC)I",
(void*)android_emoji_EmojiFactory_getAndroidPuaFromVendorSpecificSjis},
- { "nativeGetVendorSpecificSjisFromAndroidPua", "(II)I",
+ { "nativeGetVendorSpecificSjisFromAndroidPua", "(JI)I",
(void*)android_emoji_EmojiFactory_getVendorSpecificSjisFromAndroidPua},
- { "nativeGetAndroidPuaFromVendorSpecificPua", "(II)I",
+ { "nativeGetAndroidPuaFromVendorSpecificPua", "(JI)I",
(void*)android_emoji_EmojiFactory_getAndroidPuaFromVendorSpecificPua},
- { "nativeGetVendorSpecificPuaFromAndroidPua", "(II)I",
+ { "nativeGetVendorSpecificPuaFromAndroidPua", "(JI)I",
(void*)android_emoji_EmojiFactory_getVendorSpecificPuaFromAndroidPua},
- { "nativeGetMaximumVendorSpecificPua", "(I)I",
+ { "nativeGetMaximumVendorSpecificPua", "(J)I",
(void*)android_emoji_EmojiFactory_getMaximumVendorSpecificPua},
- { "nativeGetMinimumVendorSpecificPua", "(I)I",
+ { "nativeGetMinimumVendorSpecificPua", "(J)I",
(void*)android_emoji_EmojiFactory_getMinimumVendorSpecificPua},
- { "nativeGetMaximumAndroidPua", "(I)I",
+ { "nativeGetMaximumAndroidPua", "(J)I",
(void*)android_emoji_EmojiFactory_getMaximumAndroidPua},
- { "nativeGetMinimumAndroidPua", "(I)I",
+ { "nativeGetMinimumAndroidPua", "(J)I",
(void*)android_emoji_EmojiFactory_getMinimumAndroidPua}
};
@@ -276,7 +276,7 @@
int register_android_emoji_EmojiFactory(JNIEnv* env) {
gEmojiFactory_class = make_globalref(env, "android/emoji/EmojiFactory");
gEmojiFactory_constructorMethodID = env->GetMethodID(
- gEmojiFactory_class, "<init>", "(ILjava/lang/String;)V");
+ gEmojiFactory_class, "<init>", "(JLjava/lang/String;)V");
return jniRegisterNativeMethods(env, "android/emoji/EmojiFactory",
gMethods, NELEM(gMethods));
}
diff --git a/core/jni/android_os_SELinux.cpp b/core/jni/android_os_SELinux.cpp
index 2b85fef..26405b5 100644
--- a/core/jni/android_os_SELinux.cpp
+++ b/core/jni/android_os_SELinux.cpp
@@ -411,11 +411,11 @@
ScopedUtfChars pathname(env, pathnameStr);
if (pathname.c_str() == NULL) {
- ALOGV("restorecon(%p) => threw exception", pathname);
+ ALOGV("restorecon(%p) => threw exception", pathnameStr);
return false;
}
- int ret = selinux_android_restorecon(pathname.c_str());
+ int ret = selinux_android_restorecon(pathname.c_str(), 0);
ALOGV("restorecon(%s) => %d", pathname.c_str(), ret);
return (ret == 0);
}
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index f96aef8..7162a1c 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -35,7 +35,16 @@
#include <androidfw/AssetManager.h>
#include <androidfw/ResourceTypes.h>
+#include <private/android_filesystem_config.h> // for AID_SYSTEM
+
#include <stdio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <linux/capability.h>
+extern "C" int capget(cap_user_header_t hdrp, cap_user_data_t datap);
+extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap);
+
namespace android {
@@ -100,6 +109,63 @@
return block;
}
+// This is called by zygote (running as user root) as part of preloadResources.
+static void verifySystemIdmaps()
+{
+ pid_t pid;
+ char system_id[10];
+
+ snprintf(system_id, sizeof(system_id), "%d", AID_SYSTEM);
+
+ switch (pid = fork()) {
+ case -1:
+ ALOGE("failed to fork for idmap: %s", strerror(errno));
+ break;
+ case 0: // child
+ {
+ struct __user_cap_header_struct capheader;
+ struct __user_cap_data_struct capdata;
+
+ memset(&capheader, 0, sizeof(capheader));
+ memset(&capdata, 0, sizeof(capdata));
+
+ capheader.version = _LINUX_CAPABILITY_VERSION;
+ capheader.pid = 0;
+
+ if (capget(&capheader, &capdata) != 0) {
+ ALOGE("capget: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ capdata.effective = capdata.permitted;
+ if (capset(&capheader, &capdata) != 0) {
+ ALOGE("capset: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ if (setgid(AID_SYSTEM) != 0) {
+ ALOGE("setgid: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ if (setuid(AID_SYSTEM) != 0) {
+ ALOGE("setuid: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ execl(AssetManager::IDMAP_BIN, AssetManager::IDMAP_BIN, "--scan",
+ AssetManager::OVERLAY_DIR, AssetManager::TARGET_PACKAGE_NAME,
+ AssetManager::TARGET_APK_PATH, AssetManager::IDMAP_DIR, (char*)NULL);
+ ALOGE("failed to execl for idmap: %s", strerror(errno));
+ exit(1); // should never get here
+ }
+ break;
+ default: // parent
+ waitpid(pid, NULL, 0);
+ break;
+ }
+}
+
// ----------------------------------------------------------------------------
// this guy is exported to other jni routines
@@ -444,6 +510,25 @@
return (res) ? static_cast<jint>(cookie) : 0;
}
+static jint android_content_AssetManager_addOverlayPath(JNIEnv* env, jobject clazz,
+ jstring idmapPath)
+{
+ ScopedUtfChars idmapPath8(env, idmapPath);
+ if (idmapPath8.c_str() == NULL) {
+ return 0;
+ }
+
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return 0;
+ }
+
+ int32_t cookie;
+ bool res = am->addOverlayPath(String8(idmapPath8.c_str()), &cookie);
+
+ return (res) ? (jint)cookie : 0;
+}
+
static jboolean android_content_AssetManager_isUpToDate(JNIEnv* env, jobject clazz)
{
AssetManager* am = assetManagerForJavaObject(env, clazz);
@@ -1579,8 +1664,11 @@
return array;
}
-static void android_content_AssetManager_init(JNIEnv* env, jobject clazz)
+static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem)
{
+ if (isSystem) {
+ verifySystemIdmaps();
+ }
AssetManager* am = new AssetManager();
if (am == NULL) {
jniThrowException(env, "java/lang/OutOfMemoryError", "");
@@ -1658,6 +1746,8 @@
(void*) android_content_AssetManager_getAssetRemainingLength },
{ "addAssetPathNative", "(Ljava/lang/String;)I",
(void*) android_content_AssetManager_addAssetPath },
+ { "addOverlayPath", "(Ljava/lang/String;)I",
+ (void*) android_content_AssetManager_addOverlayPath },
{ "isUpToDate", "()Z",
(void*) android_content_AssetManager_isUpToDate },
@@ -1724,7 +1814,7 @@
(void*) android_content_AssetManager_getArrayIntResource },
// Bookkeeping.
- { "init", "()V",
+ { "init", "(Z)V",
(void*) android_content_AssetManager_init },
{ "destroy", "()V",
(void*) android_content_AssetManager_destroy },
diff --git a/core/jni/android_view_SurfaceSession.cpp b/core/jni/android_view_SurfaceSession.cpp
index 87e339c..609c565 100644
--- a/core/jni/android_view_SurfaceSession.cpp
+++ b/core/jni/android_view_SurfaceSession.cpp
@@ -35,22 +35,22 @@
sp<SurfaceComposerClient> android_view_SurfaceSession_getClient(
JNIEnv* env, jobject surfaceSessionObj) {
return reinterpret_cast<SurfaceComposerClient*>(
- env->GetIntField(surfaceSessionObj, gSurfaceSessionClassInfo.mNativeClient));
+ env->GetLongField(surfaceSessionObj, gSurfaceSessionClassInfo.mNativeClient));
}
-static jint nativeCreate(JNIEnv* env, jclass clazz) {
+static jlong nativeCreate(JNIEnv* env, jclass clazz) {
SurfaceComposerClient* client = new SurfaceComposerClient();
client->incStrong((void*)nativeCreate);
- return reinterpret_cast<jint>(client);
+ return reinterpret_cast<jlong>(client);
}
-static void nativeDestroy(JNIEnv* env, jclass clazz, jint ptr) {
+static void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
SurfaceComposerClient* client = reinterpret_cast<SurfaceComposerClient*>(ptr);
client->decStrong((void*)nativeCreate);
}
-static void nativeKill(JNIEnv* env, jclass clazz, jint ptr) {
+static void nativeKill(JNIEnv* env, jclass clazz, jlong ptr) {
SurfaceComposerClient* client = reinterpret_cast<SurfaceComposerClient*>(ptr);
client->dispose();
}
@@ -58,11 +58,11 @@
static JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
- { "nativeCreate", "()I",
+ { "nativeCreate", "()J",
(void*)nativeCreate },
- { "nativeDestroy", "(I)V",
+ { "nativeDestroy", "(J)V",
(void*)nativeDestroy },
- { "nativeKill", "(I)V",
+ { "nativeKill", "(J)V",
(void*)nativeKill }
};
@@ -72,7 +72,7 @@
LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
jclass clazz = env->FindClass("android/view/SurfaceSession");
- gSurfaceSessionClassInfo.mNativeClient = env->GetFieldID(clazz, "mNativeClient", "I");
+ gSurfaceSessionClassInfo.mNativeClient = env->GetFieldID(clazz, "mNativeClient", "J");
return 0;
}
diff --git a/core/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
index a0982bd..3035d15 100644
--- a/core/jni/com_google_android_gles_jni_EGLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
@@ -50,36 +50,41 @@
static inline EGLDisplay getDisplay(JNIEnv* env, jobject o) {
if (!o) return EGL_NO_DISPLAY;
- return (EGLDisplay)env->GetIntField(o, gDisplay_EGLDisplayFieldID);
+ return (EGLDisplay)env->GetLongField(o, gDisplay_EGLDisplayFieldID);
}
static inline EGLSurface getSurface(JNIEnv* env, jobject o) {
if (!o) return EGL_NO_SURFACE;
- return (EGLSurface)env->GetIntField(o, gSurface_EGLSurfaceFieldID);
+ return (EGLSurface)env->GetLongField(o, gSurface_EGLSurfaceFieldID);
}
static inline EGLContext getContext(JNIEnv* env, jobject o) {
if (!o) return EGL_NO_CONTEXT;
- return (EGLContext)env->GetIntField(o, gContext_EGLContextFieldID);
+ return (EGLContext)env->GetLongField(o, gContext_EGLContextFieldID);
}
static inline EGLConfig getConfig(JNIEnv* env, jobject o) {
if (!o) return 0;
- return (EGLConfig)env->GetIntField(o, gConfig_EGLConfigFieldID);
+ return (EGLConfig)env->GetLongField(o, gConfig_EGLConfigFieldID);
}
+
+static inline jboolean EglBoolToJBool(EGLBoolean eglBool) {
+ return eglBool == EGL_TRUE ? JNI_TRUE : JNI_FALSE;
+}
+
static void nativeClassInit(JNIEnv *_env, jclass eglImplClass)
{
jclass config_class = _env->FindClass("com/google/android/gles_jni/EGLConfigImpl");
gConfig_class = (jclass) _env->NewGlobalRef(config_class);
- gConfig_ctorID = _env->GetMethodID(gConfig_class, "<init>", "(I)V");
- gConfig_EGLConfigFieldID = _env->GetFieldID(gConfig_class, "mEGLConfig", "I");
+ gConfig_ctorID = _env->GetMethodID(gConfig_class, "<init>", "(J)V");
+ gConfig_EGLConfigFieldID = _env->GetFieldID(gConfig_class, "mEGLConfig", "J");
jclass display_class = _env->FindClass("com/google/android/gles_jni/EGLDisplayImpl");
- gDisplay_EGLDisplayFieldID = _env->GetFieldID(display_class, "mEGLDisplay", "I");
+ gDisplay_EGLDisplayFieldID = _env->GetFieldID(display_class, "mEGLDisplay", "J");
jclass context_class = _env->FindClass("com/google/android/gles_jni/EGLContextImpl");
- gContext_EGLContextFieldID = _env->GetFieldID(context_class, "mEGLContext", "I");
+ gContext_EGLContextFieldID = _env->GetFieldID(context_class, "mEGLContext", "J");
jclass surface_class = _env->FindClass("com/google/android/gles_jni/EGLSurfaceImpl");
- gSurface_EGLSurfaceFieldID = _env->GetFieldID(surface_class, "mEGLSurface", "I");
- gSurface_NativePixelRefFieldID = _env->GetFieldID(surface_class, "mNativePixelRef", "I");
+ gSurface_EGLSurfaceFieldID = _env->GetFieldID(surface_class, "mEGLSurface", "J");
+ gSurface_NativePixelRefFieldID = _env->GetFieldID(surface_class, "mNativePixelRef", "J");
jclass bitmap_class = _env->FindClass("android/graphics/Bitmap");
gBitmap_NativeBitmapFieldID = _env->GetFieldID(bitmap_class, "mNativeBitmap", "J");
@@ -123,7 +128,7 @@
}
EGLDisplay dpy = getDisplay(_env, display);
- jboolean success = eglInitialize(dpy, NULL, NULL);
+ EGLBoolean success = eglInitialize(dpy, NULL, NULL);
if (success && major_minor) {
int len = _env->GetArrayLength(major_minor);
if (len) {
@@ -134,7 +139,7 @@
_env->ReleasePrimitiveArrayCritical(major_minor, base, JNI_ABORT);
}
}
- return success;
+ return EglBoolToJBool(success);
}
static jboolean jni_eglQueryContext(JNIEnv *_env, jobject _this, jobject display,
@@ -146,14 +151,14 @@
}
EGLDisplay dpy = getDisplay(_env, display);
EGLContext ctx = getContext(_env, context);
- jboolean success = JNI_FALSE;
+ EGLBoolean success = EGL_FALSE;
int len = _env->GetArrayLength(value);
if (len) {
jint* base = (jint *)_env->GetPrimitiveArrayCritical(value, (jboolean *)0);
success = eglQueryContext(dpy, ctx, attribute, base);
_env->ReleasePrimitiveArrayCritical(value, base, JNI_ABORT);
}
- return success;
+ return EglBoolToJBool(success);
}
static jboolean jni_eglQuerySurface(JNIEnv *_env, jobject _this, jobject display,
@@ -166,14 +171,14 @@
EGLDisplay dpy = getDisplay(_env, display);
EGLContext sur = getSurface(_env, surface);
- jboolean success = JNI_FALSE;
+ EGLBoolean success = EGL_FALSE;
int len = _env->GetArrayLength(value);
if (len) {
jint* base = (jint *)_env->GetPrimitiveArrayCritical(value, (jboolean *)0);
success = eglQuerySurface(dpy, sur, attribute, base);
_env->ReleasePrimitiveArrayCritical(value, base, JNI_ABORT);
}
- return success;
+ return EglBoolToJBool(success);
}
static jint jni_getInitCount(JNIEnv *_env, jobject _clazz, jobject display) {
@@ -183,7 +188,7 @@
}
static jboolean jni_eglReleaseThread(JNIEnv *_env, jobject _this) {
- return eglReleaseThread();
+ return EglBoolToJBool(eglReleaseThread());
}
static jboolean jni_eglChooseConfig(JNIEnv *_env, jobject _this, jobject display,
@@ -196,7 +201,7 @@
return JNI_FALSE;
}
EGLDisplay dpy = getDisplay(_env, display);
- jboolean success = JNI_FALSE;
+ EGLBoolean success = EGL_FALSE;
if (configs == NULL) {
config_size = 0;
@@ -214,14 +219,14 @@
if (success && configs!=NULL) {
for (int i=0 ; i<num ; i++) {
- jobject obj = _env->NewObject(gConfig_class, gConfig_ctorID, (jint)nativeConfigs[i]);
+ jobject obj = _env->NewObject(gConfig_class, gConfig_ctorID, reinterpret_cast<jlong>(nativeConfigs[i]));
_env->SetObjectArrayElement(configs, i, obj);
}
}
- return success;
+ return EglBoolToJBool(success);
}
-static jint jni_eglCreateContext(JNIEnv *_env, jobject _this, jobject display,
+static jlong jni_eglCreateContext(JNIEnv *_env, jobject _this, jobject display,
jobject config, jobject share_context, jintArray attrib_list) {
if (display == NULL || config == NULL || share_context == NULL
|| !validAttribList(_env, attrib_list)) {
@@ -234,10 +239,10 @@
jint* base = beginNativeAttribList(_env, attrib_list);
EGLContext ctx = eglCreateContext(dpy, cnf, shr, base);
endNativeAttributeList(_env, attrib_list, base);
- return (jint)ctx;
+ return reinterpret_cast<jlong>(ctx);
}
-static jint jni_eglCreatePbufferSurface(JNIEnv *_env, jobject _this, jobject display,
+static jlong jni_eglCreatePbufferSurface(JNIEnv *_env, jobject _this, jobject display,
jobject config, jintArray attrib_list) {
if (display == NULL || config == NULL
|| !validAttribList(_env, attrib_list)) {
@@ -249,7 +254,7 @@
jint* base = beginNativeAttribList(_env, attrib_list);
EGLSurface sur = eglCreatePbufferSurface(dpy, cnf, base);
endNativeAttributeList(_env, attrib_list, base);
- return (jint)sur;
+ return reinterpret_cast<jlong>(sur);
}
static PixelFormat convertPixelFormat(SkBitmap::Config format)
@@ -300,15 +305,15 @@
endNativeAttributeList(_env, attrib_list, base);
if (sur != EGL_NO_SURFACE) {
- _env->SetIntField(out_sur, gSurface_EGLSurfaceFieldID, (int)sur);
- _env->SetIntField(out_sur, gSurface_NativePixelRefFieldID, (int)ref);
+ _env->SetLongField(out_sur, gSurface_EGLSurfaceFieldID, reinterpret_cast<jlong>(sur));
+ _env->SetLongField(out_sur, gSurface_NativePixelRefFieldID, reinterpret_cast<jlong>(ref));
} else {
ref->unlockPixels();
SkSafeUnref(ref);
}
}
-static jint jni_eglCreateWindowSurface(JNIEnv *_env, jobject _this, jobject display,
+static jlong jni_eglCreateWindowSurface(JNIEnv *_env, jobject _this, jobject display,
jobject config, jobject native_window, jintArray attrib_list) {
if (display == NULL || config == NULL
|| !validAttribList(_env, attrib_list)) {
@@ -332,15 +337,15 @@
jint* base = beginNativeAttribList(_env, attrib_list);
EGLSurface sur = eglCreateWindowSurface(dpy, cnf, window.get(), base);
endNativeAttributeList(_env, attrib_list, base);
- return (jint)sur;
+ return reinterpret_cast<jlong>(sur);
}
-static jint jni_eglCreateWindowSurfaceTexture(JNIEnv *_env, jobject _this, jobject display,
+static jlong jni_eglCreateWindowSurfaceTexture(JNIEnv *_env, jobject _this, jobject display,
jobject config, jobject native_window, jintArray attrib_list) {
if (display == NULL || config == NULL
|| !validAttribList(_env, attrib_list)) {
jniThrowException(_env, "java/lang/IllegalArgumentException", NULL);
- return JNI_FALSE;
+ return 0;
}
EGLDisplay dpy = getDisplay(_env, display);
EGLContext cnf = getConfig(_env, config);
@@ -360,7 +365,7 @@
jint* base = beginNativeAttribList(_env, attrib_list);
EGLSurface sur = eglCreateWindowSurface(dpy, cnf, window.get(), base);
endNativeAttributeList(_env, attrib_list, base);
- return (jint)sur;
+ return reinterpret_cast<jlong>(sur);
}
static jboolean jni_eglGetConfigAttrib(JNIEnv *_env, jobject _this, jobject display,
@@ -372,13 +377,13 @@
}
EGLDisplay dpy = getDisplay(_env, display);
EGLContext cnf = getConfig(_env, config);
- jboolean success = JNI_FALSE;
+ EGLBoolean success = EGL_FALSE;
jint localValue;
success = eglGetConfigAttrib(dpy, cnf, attribute, &localValue);
if (success) {
_env->SetIntArrayRegion(value, 0, 1, &localValue);
}
- return success;
+ return EglBoolToJBool(success);
}
static jboolean jni_eglGetConfigs(JNIEnv *_env, jobject _this, jobject display,
@@ -389,7 +394,7 @@
return JNI_FALSE;
}
EGLDisplay dpy = getDisplay(_env, display);
- jboolean success = JNI_FALSE;
+ EGLBoolean success = EGL_FALSE;
if (configs == NULL) {
config_size = 0;
}
@@ -401,11 +406,11 @@
}
if (success && configs) {
for (int i=0 ; i<num ; i++) {
- jobject obj = _env->NewObject(gConfig_class, gConfig_ctorID, (jint)nativeConfigs[i]);
+ jobject obj = _env->NewObject(gConfig_class, gConfig_ctorID, reinterpret_cast<jlong>(nativeConfigs[i]));
_env->SetObjectArrayElement(configs, i, obj);
}
}
- return success;
+ return EglBoolToJBool(success);
}
static jint jni_eglGetError(JNIEnv *_env, jobject _this) {
@@ -413,20 +418,20 @@
return error;
}
-static jint jni_eglGetCurrentContext(JNIEnv *_env, jobject _this) {
- return (jint)eglGetCurrentContext();
+static jlong jni_eglGetCurrentContext(JNIEnv *_env, jobject _this) {
+ return reinterpret_cast<jlong>(eglGetCurrentContext());
}
-static jint jni_eglGetCurrentDisplay(JNIEnv *_env, jobject _this) {
- return (jint)eglGetCurrentDisplay();
+static jlong jni_eglGetCurrentDisplay(JNIEnv *_env, jobject _this) {
+ return reinterpret_cast<jlong>(eglGetCurrentDisplay());
}
-static jint jni_eglGetCurrentSurface(JNIEnv *_env, jobject _this, jint readdraw) {
+static jlong jni_eglGetCurrentSurface(JNIEnv *_env, jobject _this, jint readdraw) {
if ((readdraw != EGL_READ) && (readdraw != EGL_DRAW)) {
jniThrowException(_env, "java/lang/IllegalArgumentException", NULL);
return 0;
}
- return (jint)eglGetCurrentSurface(readdraw);
+ return reinterpret_cast<jlong>(eglGetCurrentSurface(readdraw));
}
static jboolean jni_eglDestroyContext(JNIEnv *_env, jobject _this, jobject display, jobject context) {
@@ -436,7 +441,7 @@
}
EGLDisplay dpy = getDisplay(_env, display);
EGLContext ctx = getContext(_env, context);
- return eglDestroyContext(dpy, ctx);
+ return EglBoolToJBool(eglDestroyContext(dpy, ctx));
}
static jboolean jni_eglDestroySurface(JNIEnv *_env, jobject _this, jobject display, jobject surface) {
@@ -448,18 +453,18 @@
EGLSurface sur = getSurface(_env, surface);
if (sur) {
- SkPixelRef* ref = (SkPixelRef*)(_env->GetIntField(surface,
+ SkPixelRef* ref = (SkPixelRef*)(_env->GetLongField(surface,
gSurface_NativePixelRefFieldID));
if (ref) {
ref->unlockPixels();
SkSafeUnref(ref);
}
}
- return eglDestroySurface(dpy, sur);
+ return EglBoolToJBool(eglDestroySurface(dpy, sur));
}
-static jint jni_eglGetDisplay(JNIEnv *_env, jobject _this, jobject native_display) {
- return (jint)eglGetDisplay(EGL_DEFAULT_DISPLAY);
+static jlong jni_eglGetDisplay(JNIEnv *_env, jobject _this, jobject native_display) {
+ return reinterpret_cast<jlong>(eglGetDisplay(EGL_DEFAULT_DISPLAY));
}
static jboolean jni_eglMakeCurrent(JNIEnv *_env, jobject _this, jobject display, jobject draw, jobject read, jobject context) {
@@ -471,7 +476,7 @@
EGLSurface sdr = getSurface(_env, draw);
EGLSurface srd = getSurface(_env, read);
EGLContext ctx = getContext(_env, context);
- return eglMakeCurrent(dpy, sdr, srd, ctx);
+ return EglBoolToJBool(eglMakeCurrent(dpy, sdr, srd, ctx));
}
static jstring jni_eglQueryString(JNIEnv *_env, jobject _this, jobject display, jint name) {
@@ -491,7 +496,7 @@
}
EGLDisplay dpy = getDisplay(_env, display);
EGLSurface sur = getSurface(_env, surface);
- return eglSwapBuffers(dpy, sur);
+ return EglBoolToJBool(eglSwapBuffers(dpy, sur));
}
static jboolean jni_eglTerminate(JNIEnv *_env, jobject _this, jobject display) {
@@ -500,7 +505,7 @@
return JNI_FALSE;
}
EGLDisplay dpy = getDisplay(_env, display);
- return eglTerminate(dpy);
+ return EglBoolToJBool(eglTerminate(dpy));
}
static jboolean jni_eglCopyBuffers(JNIEnv *_env, jobject _this, jobject display,
@@ -514,11 +519,11 @@
}
static jboolean jni_eglWaitGL(JNIEnv *_env, jobject _this) {
- return eglWaitGL();
+ return EglBoolToJBool(eglWaitGL());
}
static jboolean jni_eglWaitNative(JNIEnv *_env, jobject _this, jint engine, jobject bindTarget) {
- return eglWaitNative(engine);
+ return EglBoolToJBool(eglWaitNative(engine));
}
@@ -540,21 +545,21 @@
{"eglReleaseThread","()Z", (void*)jni_eglReleaseThread },
{"getInitCount", "(" DISPLAY ")I", (void*)jni_getInitCount },
{"eglChooseConfig", "(" DISPLAY "[I[" CONFIG "I[I)Z", (void*)jni_eglChooseConfig },
-{"_eglCreateContext","(" DISPLAY CONFIG CONTEXT "[I)I", (void*)jni_eglCreateContext },
+{"_eglCreateContext","(" DISPLAY CONFIG CONTEXT "[I)J", (void*)jni_eglCreateContext },
{"eglGetConfigs", "(" DISPLAY "[" CONFIG "I[I)Z", (void*)jni_eglGetConfigs },
{"eglTerminate", "(" DISPLAY ")Z", (void*)jni_eglTerminate },
{"eglCopyBuffers", "(" DISPLAY SURFACE OBJECT ")Z", (void*)jni_eglCopyBuffers },
{"eglWaitNative", "(I" OBJECT ")Z", (void*)jni_eglWaitNative },
{"eglGetError", "()I", (void*)jni_eglGetError },
{"eglGetConfigAttrib", "(" DISPLAY CONFIG "I[I)Z", (void*)jni_eglGetConfigAttrib },
-{"_eglGetDisplay", "(" OBJECT ")I", (void*)jni_eglGetDisplay },
-{"_eglGetCurrentContext", "()I", (void*)jni_eglGetCurrentContext },
-{"_eglGetCurrentDisplay", "()I", (void*)jni_eglGetCurrentDisplay },
-{"_eglGetCurrentSurface", "(I)I", (void*)jni_eglGetCurrentSurface },
-{"_eglCreatePbufferSurface","(" DISPLAY CONFIG "[I)I", (void*)jni_eglCreatePbufferSurface },
+{"_eglGetDisplay", "(" OBJECT ")J", (void*)jni_eglGetDisplay },
+{"_eglGetCurrentContext", "()J", (void*)jni_eglGetCurrentContext },
+{"_eglGetCurrentDisplay", "()J", (void*)jni_eglGetCurrentDisplay },
+{"_eglGetCurrentSurface", "(I)J", (void*)jni_eglGetCurrentSurface },
+{"_eglCreatePbufferSurface","(" DISPLAY CONFIG "[I)J", (void*)jni_eglCreatePbufferSurface },
{"_eglCreatePixmapSurface", "(" SURFACE DISPLAY CONFIG OBJECT "[I)V", (void*)jni_eglCreatePixmapSurface },
-{"_eglCreateWindowSurface", "(" DISPLAY CONFIG OBJECT "[I)I", (void*)jni_eglCreateWindowSurface },
-{"_eglCreateWindowSurfaceTexture", "(" DISPLAY CONFIG OBJECT "[I)I", (void*)jni_eglCreateWindowSurfaceTexture },
+{"_eglCreateWindowSurface", "(" DISPLAY CONFIG OBJECT "[I)J", (void*)jni_eglCreateWindowSurface },
+{"_eglCreateWindowSurfaceTexture", "(" DISPLAY CONFIG OBJECT "[I)J", (void*)jni_eglCreateWindowSurfaceTexture },
{"eglDestroyContext", "(" DISPLAY CONTEXT ")Z", (void*)jni_eglDestroyContext },
{"eglDestroySurface", "(" DISPLAY SURFACE ")Z", (void*)jni_eglDestroySurface },
{"eglMakeCurrent", "(" DISPLAY SURFACE SURFACE CONTEXT")Z", (void*)jni_eglMakeCurrent },
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index b198937..8355928 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1088,6 +1088,14 @@
android:label="@string/permlab_readPhoneState"
android:description="@string/permdesc_readPhoneState" />
+ <!-- Allows read only access to precise phone state.
+ @hide Pending API council approval -->
+ <permission android:name="android.permission.READ_PRECISE_PHONE_STATE"
+ android:permissionGroup="android.permission-group.PHONE_CALLS"
+ android:protectionLevel="dangerous"
+ android:label="@string/permlab_readPrecisePhoneState"
+ android:description="@string/permdesc_readPrecisePhoneState" />
+
<!-- Allows read access to privileged phone state.
@hide Used internally. -->
<permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index b20f5ba..4647413 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1777,6 +1777,16 @@
<attr name="publicKey" />
</declare-styleable>
+ <!-- Attributes relating to resource overlay packages. -->
+ <declare-styleable name="AndroidManifestResourceOverlay" parent="AndroidManifest">
+ <!-- Package name of base package whose resources will be overlaid. -->
+ <attr name="targetPackage" />
+
+ <!-- Load order of overlay package. -->
+ <attr name="priority" />
+
+ </declare-styleable>
+
<!-- Declaration of an {@link android.content.Intent} object in XML. May
also include zero or more {@link #IntentCategory <category> and
{@link #Extra <extra>} tags. -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 986a005..012fb83 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1639,6 +1639,14 @@
connected by a call.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_readPrecisePhoneState">read precise phone states</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_readPrecisePhoneState">Allows the app to access the precise
+ phone states. This permission allows the app to determine the real
+ call status, whether a call is active or in the background, call fails,
+ precise data connection status and data connection fails.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_wakeLock" product="tablet">prevent tablet from sleeping</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_wakeLock" product="default">prevent phone from sleeping</string>
diff --git a/core/tests/coretests/src/android/util/JsonReaderTest.java b/core/tests/coretests/src/android/util/JsonReaderTest.java
deleted file mode 100644
index 42b7640..0000000
--- a/core/tests/coretests/src/android/util/JsonReaderTest.java
+++ /dev/null
@@ -1,909 +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 android.util;
-
-import java.io.IOException;
-import java.io.StringReader;
-import java.util.Arrays;
-import junit.framework.TestCase;
-
-public final class JsonReaderTest extends TestCase {
-
- private static final int READER_BUFFER_SIZE = 1024;
-
- public void testReadArray() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[true, true]"));
- reader.beginArray();
- assertEquals(true, reader.nextBoolean());
- assertEquals(true, reader.nextBoolean());
- reader.endArray();
- assertEquals(JsonToken.END_DOCUMENT, reader.peek());
- }
-
- public void testReadEmptyArray() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[]"));
- reader.beginArray();
- assertFalse(reader.hasNext());
- reader.endArray();
- assertEquals(JsonToken.END_DOCUMENT, reader.peek());
- }
-
- public void testReadObject() throws IOException {
- JsonReader reader = new JsonReader(new StringReader(
- "{\"a\": \"android\", \"b\": \"banana\"}"));
- reader.beginObject();
- assertEquals("a", reader.nextName());
- assertEquals("android", reader.nextString());
- assertEquals("b", reader.nextName());
- assertEquals("banana", reader.nextString());
- reader.endObject();
- assertEquals(JsonToken.END_DOCUMENT, reader.peek());
- }
-
- public void testReadEmptyObject() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("{}"));
- reader.beginObject();
- assertFalse(reader.hasNext());
- reader.endObject();
- assertEquals(JsonToken.END_DOCUMENT, reader.peek());
- }
-
- public void testSkipObject() throws IOException {
- JsonReader reader = new JsonReader(new StringReader(
- "{\"a\": { \"c\": [], \"d\": [true, true, {}] }, \"b\": \"banana\"}"));
- reader.beginObject();
- assertEquals("a", reader.nextName());
- reader.skipValue();
- assertEquals("b", reader.nextName());
- reader.skipValue();
- reader.endObject();
- assertEquals(JsonToken.END_DOCUMENT, reader.peek());
- }
-
- public void testHelloWorld() throws IOException {
- String json = "{\n" +
- " \"hello\": true,\n" +
- " \"foo\": [\"world\"]\n" +
- "}";
- JsonReader reader = new JsonReader(new StringReader(json));
- reader.beginObject();
- assertEquals("hello", reader.nextName());
- assertEquals(true, reader.nextBoolean());
- assertEquals("foo", reader.nextName());
- reader.beginArray();
- assertEquals("world", reader.nextString());
- reader.endArray();
- reader.endObject();
- assertEquals(JsonToken.END_DOCUMENT, reader.peek());
- }
-
- public void testNulls() {
- try {
- new JsonReader(null);
- fail();
- } catch (NullPointerException expected) {
- }
- }
-
- public void testEmptyString() throws IOException {
- try {
- new JsonReader(new StringReader("")).beginArray();
- } catch (IOException expected) {
- }
- try {
- new JsonReader(new StringReader("")).beginObject();
- } catch (IOException expected) {
- }
- }
-
- public void testNoTopLevelObject() throws IOException {
- try {
- new JsonReader(new StringReader("true")).nextBoolean();
- } catch (IOException expected) {
- }
- }
-
- public void testCharacterUnescaping() throws IOException {
- String json = "[\"a\","
- + "\"a\\\"\","
- + "\"\\\"\","
- + "\":\","
- + "\",\","
- + "\"\\b\","
- + "\"\\f\","
- + "\"\\n\","
- + "\"\\r\","
- + "\"\\t\","
- + "\" \","
- + "\"\\\\\","
- + "\"{\","
- + "\"}\","
- + "\"[\","
- + "\"]\","
- + "\"\\u0000\","
- + "\"\\u0019\","
- + "\"\\u20AC\""
- + "]";
- JsonReader reader = new JsonReader(new StringReader(json));
- reader.beginArray();
- assertEquals("a", reader.nextString());
- assertEquals("a\"", reader.nextString());
- assertEquals("\"", reader.nextString());
- assertEquals(":", reader.nextString());
- assertEquals(",", reader.nextString());
- assertEquals("\b", reader.nextString());
- assertEquals("\f", reader.nextString());
- assertEquals("\n", reader.nextString());
- assertEquals("\r", reader.nextString());
- assertEquals("\t", reader.nextString());
- assertEquals(" ", reader.nextString());
- assertEquals("\\", reader.nextString());
- assertEquals("{", reader.nextString());
- assertEquals("}", reader.nextString());
- assertEquals("[", reader.nextString());
- assertEquals("]", reader.nextString());
- assertEquals("\0", reader.nextString());
- assertEquals("\u0019", reader.nextString());
- assertEquals("\u20AC", reader.nextString());
- reader.endArray();
- assertEquals(JsonToken.END_DOCUMENT, reader.peek());
- }
-
- public void testIntegersWithFractionalPartSpecified() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[1.0,1.0,1.0]"));
- reader.beginArray();
- assertEquals(1.0, reader.nextDouble());
- assertEquals(1, reader.nextInt());
- assertEquals(1L, reader.nextLong());
- }
-
- public void testDoubles() throws IOException {
- String json = "[-0.0,"
- + "1.0,"
- + "1.7976931348623157E308,"
- + "4.9E-324,"
- + "0.0,"
- + "-0.5,"
- + "2.2250738585072014E-308,"
- + "3.141592653589793,"
- + "2.718281828459045,"
- + "\"1.0\","
- + "\"011.0\","
- + "\"NaN\","
- + "\"Infinity\","
- + "\"-Infinity\""
- + "]";
- JsonReader reader = new JsonReader(new StringReader(json));
- reader.beginArray();
- assertEquals(-0.0, reader.nextDouble());
- assertEquals(1.0, reader.nextDouble());
- assertEquals(1.7976931348623157E308, reader.nextDouble());
- assertEquals(4.9E-324, reader.nextDouble());
- assertEquals(0.0, reader.nextDouble());
- assertEquals(-0.5, reader.nextDouble());
- assertEquals(2.2250738585072014E-308, reader.nextDouble());
- assertEquals(3.141592653589793, reader.nextDouble());
- assertEquals(2.718281828459045, reader.nextDouble());
- assertEquals(1,0, reader.nextDouble());
- assertEquals(11.0, reader.nextDouble());
- assertTrue(Double.isNaN(reader.nextDouble()));
- assertEquals(Double.POSITIVE_INFINITY, reader.nextDouble());
- assertEquals(Double.NEGATIVE_INFINITY, reader.nextDouble());
- reader.endArray();
- assertEquals(JsonToken.END_DOCUMENT, reader.peek());
- }
-
- public void testLenientDoubles() throws IOException {
- String json = "["
- + "011.0,"
- + "NaN,"
- + "NAN,"
- + "Infinity,"
- + "INFINITY,"
- + "-Infinity"
- + "]";
- JsonReader reader = new JsonReader(new StringReader(json));
- reader.setLenient(true);
- reader.beginArray();
- assertEquals(11.0, reader.nextDouble());
- assertTrue(Double.isNaN(reader.nextDouble()));
- try {
- reader.nextDouble();
- fail();
- } catch (NumberFormatException expected) {
- }
- assertEquals("NAN", reader.nextString());
- assertEquals(Double.POSITIVE_INFINITY, reader.nextDouble());
- try {
- reader.nextDouble();
- fail();
- } catch (NumberFormatException expected) {
- }
- assertEquals("INFINITY", reader.nextString());
- assertEquals(Double.NEGATIVE_INFINITY, reader.nextDouble());
- reader.endArray();
- assertEquals(JsonToken.END_DOCUMENT, reader.peek());
- }
-
- public void testBufferBoundary() throws IOException {
- char[] pad = new char[READER_BUFFER_SIZE - 8];
- Arrays.fill(pad, '5');
- String json = "[\"" + new String(pad) + "\",33333]";
- JsonReader reader = new JsonReader(new StringReader(json));
- reader.beginArray();
- assertEquals(JsonToken.STRING, reader.peek());
- assertEquals(new String(pad), reader.nextString());
- assertEquals(JsonToken.NUMBER, reader.peek());
- assertEquals(33333, reader.nextInt());
- }
-
- public void testTruncatedBufferBoundary() throws IOException {
- char[] pad = new char[READER_BUFFER_SIZE - 8];
- Arrays.fill(pad, '5');
- String json = "[\"" + new String(pad) + "\",33333";
- JsonReader reader = new JsonReader(new StringReader(json));
- reader.setLenient(true);
- reader.beginArray();
- assertEquals(JsonToken.STRING, reader.peek());
- assertEquals(new String(pad), reader.nextString());
- assertEquals(JsonToken.NUMBER, reader.peek());
- assertEquals(33333, reader.nextInt());
- try {
- reader.endArray();
- fail();
- } catch (IOException e) {
- }
- }
-
- public void testLongestSupportedNumericLiterals() throws IOException {
- testLongNumericLiterals(READER_BUFFER_SIZE - 1, JsonToken.NUMBER);
- }
-
- public void testLongerNumericLiterals() throws IOException {
- testLongNumericLiterals(READER_BUFFER_SIZE, JsonToken.STRING);
- }
-
- private void testLongNumericLiterals(int length, JsonToken expectedToken) throws IOException {
- char[] longNumber = new char[length];
- Arrays.fill(longNumber, '9');
- longNumber[0] = '1';
- longNumber[1] = '.';
-
- String json = "[" + new String(longNumber) + "]";
- JsonReader reader = new JsonReader(new StringReader(json));
- reader.setLenient(true);
- reader.beginArray();
- assertEquals(expectedToken, reader.peek());
- assertEquals(2.0d, reader.nextDouble());
- reader.endArray();
- }
-
- public void testLongs() throws IOException {
- String json = "[0,0,0,"
- + "1,1,1,"
- + "-1,-1,-1,"
- + "-9223372036854775808,"
- + "9223372036854775807,"
- + "5.0,"
- + "1.0e2,"
- + "\"011\","
- + "\"5.0\","
- + "\"1.0e2\""
- + "]";
- JsonReader reader = new JsonReader(new StringReader(json));
- reader.beginArray();
- assertEquals(0L, reader.nextLong());
- assertEquals(0, reader.nextInt());
- assertEquals(0.0, reader.nextDouble());
- assertEquals(1L, reader.nextLong());
- assertEquals(1, reader.nextInt());
- assertEquals(1.0, reader.nextDouble());
- assertEquals(-1L, reader.nextLong());
- assertEquals(-1, reader.nextInt());
- assertEquals(-1.0, reader.nextDouble());
- try {
- reader.nextInt();
- fail();
- } catch (NumberFormatException expected) {
- }
- assertEquals(Long.MIN_VALUE, reader.nextLong());
- try {
- reader.nextInt();
- fail();
- } catch (NumberFormatException expected) {
- }
- assertEquals(Long.MAX_VALUE, reader.nextLong());
- assertEquals(5, reader.nextLong());
- assertEquals(100, reader.nextLong());
- assertEquals(11, reader.nextLong());
- assertEquals(5, reader.nextLong());
- assertEquals(100, reader.nextLong());
- reader.endArray();
- assertEquals(JsonToken.END_DOCUMENT, reader.peek());
- }
-
- /**
- * This test fails because there's no double for 9223372036854775806, and
- * our long parsing uses Double.parseDouble() for fractional values.
- */
- public void testHighPrecisionLong() throws IOException {
- String json = "[9223372036854775806.000]";
- JsonReader reader = new JsonReader(new StringReader(json));
- reader.beginArray();
- assertEquals(9223372036854775806L, reader.nextLong());
- reader.endArray();
- }
-
- public void testMatchingValidNumbers() throws IOException {
- String json = "[-1,99,-0,0,0e1,0e+1,0e-1,0E1,0E+1,0E-1,0.0,1.0,-1.0,1.0e0,1.0e+1,1.0e-1]";
- JsonReader reader = new JsonReader(new StringReader(json));
- reader.beginArray();
- for (int i = 0; i < 16; i++) {
- assertEquals(JsonToken.NUMBER, reader.peek());
- reader.nextDouble();
- }
- reader.endArray();
- }
-
- public void testRecognizingInvalidNumbers() throws IOException {
- String json = "[-00,00,001,+1,1f,0x,0xf,0x0,0f1,0ee1,1..0,1e0.1,1.-01,1.+1,1.0x,1.0+]";
- JsonReader reader = new JsonReader(new StringReader(json));
- reader.setLenient(true);
- reader.beginArray();
- for (int i = 0; i < 16; i++) {
- assertEquals(JsonToken.STRING, reader.peek());
- reader.nextString();
- }
- reader.endArray();
- }
-
- public void testNonFiniteDouble() throws IOException {
- String json = "[NaN]";
- JsonReader reader = new JsonReader(new StringReader(json));
- reader.beginArray();
- try {
- reader.nextDouble();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testNumberWithHexPrefix() throws IOException {
- String json = "[0x11]";
- JsonReader reader = new JsonReader(new StringReader(json));
- reader.beginArray();
- try {
- reader.nextLong();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testNumberWithOctalPrefix() throws IOException {
- String json = "[01]";
- JsonReader reader = new JsonReader(new StringReader(json));
- reader.beginArray();
- try {
- reader.nextInt();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testBooleans() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[true,false]"));
- reader.beginArray();
- assertEquals(true, reader.nextBoolean());
- assertEquals(false, reader.nextBoolean());
- reader.endArray();
- assertEquals(JsonToken.END_DOCUMENT, reader.peek());
- }
-
- public void testMixedCaseLiterals() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[True,TruE,False,FALSE,NULL,nulL]"));
- reader.beginArray();
- assertEquals(true, reader.nextBoolean());
- assertEquals(true, reader.nextBoolean());
- assertEquals(false, reader.nextBoolean());
- assertEquals(false, reader.nextBoolean());
- reader.nextNull();
- reader.nextNull();
- reader.endArray();
- assertEquals(JsonToken.END_DOCUMENT, reader.peek());
- }
-
- public void testMissingValue() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("{\"a\":}"));
- reader.beginObject();
- assertEquals("a", reader.nextName());
- try {
- reader.nextString();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testPrematureEndOfInput() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("{\"a\":true,"));
- reader.beginObject();
- assertEquals("a", reader.nextName());
- assertEquals(true, reader.nextBoolean());
- try {
- reader.nextName();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testPrematurelyClosed() throws IOException {
- try {
- JsonReader reader = new JsonReader(new StringReader("{\"a\":[]}"));
- reader.beginObject();
- reader.close();
- reader.nextName();
- fail();
- } catch (IllegalStateException expected) {
- }
-
- try {
- JsonReader reader = new JsonReader(new StringReader("{\"a\":[]}"));
- reader.close();
- reader.beginObject();
- fail();
- } catch (IllegalStateException expected) {
- }
-
- try {
- JsonReader reader = new JsonReader(new StringReader("{\"a\":true}"));
- reader.beginObject();
- reader.nextName();
- reader.peek();
- reader.close();
- reader.nextBoolean();
- fail();
- } catch (IllegalStateException expected) {
- }
- }
-
- public void testNextFailuresDoNotAdvance() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("{\"a\":true}"));
- reader.beginObject();
- try {
- reader.nextString();
- fail();
- } catch (IllegalStateException expected) {
- }
- assertEquals("a", reader.nextName());
- try {
- reader.nextName();
- fail();
- } catch (IllegalStateException expected) {
- }
- try {
- reader.beginArray();
- fail();
- } catch (IllegalStateException expected) {
- }
- try {
- reader.endArray();
- fail();
- } catch (IllegalStateException expected) {
- }
- try {
- reader.beginObject();
- fail();
- } catch (IllegalStateException expected) {
- }
- try {
- reader.endObject();
- fail();
- } catch (IllegalStateException expected) {
- }
- assertEquals(true, reader.nextBoolean());
- try {
- reader.nextString();
- fail();
- } catch (IllegalStateException expected) {
- }
- try {
- reader.nextName();
- fail();
- } catch (IllegalStateException expected) {
- }
- try {
- reader.beginArray();
- fail();
- } catch (IllegalStateException expected) {
- }
- try {
- reader.endArray();
- fail();
- } catch (IllegalStateException expected) {
- }
- reader.endObject();
- assertEquals(JsonToken.END_DOCUMENT, reader.peek());
- reader.close();
- }
-
- public void testStringNullIsNotNull() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[\"null\"]"));
- reader.beginArray();
- try {
- reader.nextNull();
- fail();
- } catch (IllegalStateException expected) {
- }
- }
-
- public void testNullLiteralIsNotAString() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[null]"));
- reader.beginArray();
- try {
- reader.nextString();
- fail();
- } catch (IllegalStateException expected) {
- }
- }
-
- public void testStrictNameValueSeparator() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("{\"a\"=true}"));
- reader.beginObject();
- assertEquals("a", reader.nextName());
- try {
- reader.nextBoolean();
- fail();
- } catch (IOException expected) {
- }
-
- reader = new JsonReader(new StringReader("{\"a\"=>true}"));
- reader.beginObject();
- assertEquals("a", reader.nextName());
- try {
- reader.nextBoolean();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testLenientNameValueSeparator() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("{\"a\"=true}"));
- reader.setLenient(true);
- reader.beginObject();
- assertEquals("a", reader.nextName());
- assertEquals(true, reader.nextBoolean());
-
- reader = new JsonReader(new StringReader("{\"a\"=>true}"));
- reader.setLenient(true);
- reader.beginObject();
- assertEquals("a", reader.nextName());
- assertEquals(true, reader.nextBoolean());
- }
-
- public void testStrictComments() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[// comment \n true]"));
- reader.beginArray();
- try {
- reader.nextBoolean();
- fail();
- } catch (IOException expected) {
- }
-
- reader = new JsonReader(new StringReader("[# comment \n true]"));
- reader.beginArray();
- try {
- reader.nextBoolean();
- fail();
- } catch (IOException expected) {
- }
-
- reader = new JsonReader(new StringReader("[/* comment */ true]"));
- reader.beginArray();
- try {
- reader.nextBoolean();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testLenientComments() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[// comment \n true]"));
- reader.setLenient(true);
- reader.beginArray();
- assertEquals(true, reader.nextBoolean());
-
- reader = new JsonReader(new StringReader("[# comment \n true]"));
- reader.setLenient(true);
- reader.beginArray();
- assertEquals(true, reader.nextBoolean());
-
- reader = new JsonReader(new StringReader("[/* comment */ true]"));
- reader.setLenient(true);
- reader.beginArray();
- assertEquals(true, reader.nextBoolean());
- }
-
- public void testStrictUnquotedNames() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("{a:true}"));
- reader.beginObject();
- try {
- reader.nextName();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testLenientUnquotedNames() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("{a:true}"));
- reader.setLenient(true);
- reader.beginObject();
- assertEquals("a", reader.nextName());
- }
-
- public void testStrictSingleQuotedNames() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("{'a':true}"));
- reader.beginObject();
- try {
- reader.nextName();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testLenientSingleQuotedNames() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("{'a':true}"));
- reader.setLenient(true);
- reader.beginObject();
- assertEquals("a", reader.nextName());
- }
-
- public void testStrictUnquotedStrings() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[a]"));
- reader.beginArray();
- try {
- reader.nextString();
- fail();
- } catch (MalformedJsonException expected) {
- }
- }
-
- public void testLenientUnquotedStrings() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[a]"));
- reader.setLenient(true);
- reader.beginArray();
- assertEquals("a", reader.nextString());
- }
-
- public void testStrictSingleQuotedStrings() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("['a']"));
- reader.beginArray();
- try {
- reader.nextString();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testLenientSingleQuotedStrings() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("['a']"));
- reader.setLenient(true);
- reader.beginArray();
- assertEquals("a", reader.nextString());
- }
-
- public void testStrictSemicolonDelimitedArray() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[true;true]"));
- reader.beginArray();
- try {
- reader.nextBoolean();
- reader.nextBoolean();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testLenientSemicolonDelimitedArray() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[true;true]"));
- reader.setLenient(true);
- reader.beginArray();
- assertEquals(true, reader.nextBoolean());
- assertEquals(true, reader.nextBoolean());
- }
-
- public void testStrictSemicolonDelimitedNameValuePair() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("{\"a\":true;\"b\":true}"));
- reader.beginObject();
- assertEquals("a", reader.nextName());
- try {
- reader.nextBoolean();
- reader.nextName();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testLenientSemicolonDelimitedNameValuePair() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("{\"a\":true;\"b\":true}"));
- reader.setLenient(true);
- reader.beginObject();
- assertEquals("a", reader.nextName());
- assertEquals(true, reader.nextBoolean());
- assertEquals("b", reader.nextName());
- }
-
- public void testStrictUnnecessaryArraySeparators() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[true,,true]"));
- reader.beginArray();
- assertEquals(true, reader.nextBoolean());
- try {
- reader.nextNull();
- fail();
- } catch (IOException expected) {
- }
-
- reader = new JsonReader(new StringReader("[,true]"));
- reader.beginArray();
- try {
- reader.nextNull();
- fail();
- } catch (IOException expected) {
- }
-
- reader = new JsonReader(new StringReader("[true,]"));
- reader.beginArray();
- assertEquals(true, reader.nextBoolean());
- try {
- reader.nextNull();
- fail();
- } catch (IOException expected) {
- }
-
- reader = new JsonReader(new StringReader("[,]"));
- reader.beginArray();
- try {
- reader.nextNull();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testLenientUnnecessaryArraySeparators() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[true,,true]"));
- reader.setLenient(true);
- reader.beginArray();
- assertEquals(true, reader.nextBoolean());
- reader.nextNull();
- assertEquals(true, reader.nextBoolean());
- reader.endArray();
-
- reader = new JsonReader(new StringReader("[,true]"));
- reader.setLenient(true);
- reader.beginArray();
- reader.nextNull();
- assertEquals(true, reader.nextBoolean());
- reader.endArray();
-
- reader = new JsonReader(new StringReader("[true,]"));
- reader.setLenient(true);
- reader.beginArray();
- assertEquals(true, reader.nextBoolean());
- reader.nextNull();
- reader.endArray();
-
- reader = new JsonReader(new StringReader("[,]"));
- reader.setLenient(true);
- reader.beginArray();
- reader.nextNull();
- reader.nextNull();
- reader.endArray();
- }
-
- public void testStrictMultipleTopLevelValues() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[] []"));
- reader.beginArray();
- reader.endArray();
- try {
- reader.peek();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testLenientMultipleTopLevelValues() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[] true {}"));
- reader.setLenient(true);
- reader.beginArray();
- reader.endArray();
- assertEquals(true, reader.nextBoolean());
- reader.beginObject();
- reader.endObject();
- assertEquals(JsonToken.END_DOCUMENT, reader.peek());
- }
-
- public void testStrictTopLevelValueType() {
- JsonReader reader = new JsonReader(new StringReader("true"));
- try {
- reader.nextBoolean();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testLenientTopLevelValueType() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("true"));
- reader.setLenient(true);
- assertEquals(true, reader.nextBoolean());
- }
-
- public void testStrictNonExecutePrefix() {
- JsonReader reader = new JsonReader(new StringReader(")]}'\n []"));
- try {
- reader.beginArray();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testBomIgnoredAsFirstCharacterOfDocument() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("\ufeff[]"));
- reader.beginArray();
- reader.endArray();
- }
-
- public void testBomForbiddenAsOtherCharacterInDocument() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[\ufeff]"));
- reader.beginArray();
- try {
- reader.endArray();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testFailWithPosition() throws IOException {
- testFailWithPosition("Expected literal value at line 6 column 3",
- "[\n\n\n\n\n0,}]");
- }
-
- public void testFailWithPositionIsOffsetByBom() throws IOException {
- testFailWithPosition("Expected literal value at line 1 column 4",
- "\ufeff[0,}]");
- }
-
- public void testFailWithPositionGreaterThanBufferSize() throws IOException {
- String spaces = repeat(' ', 8192);
- testFailWithPosition("Expected literal value at line 6 column 3",
- "[\n\n" + spaces + "\n\n\n0,}]");
- }
-
- private void testFailWithPosition(String message, String json) throws IOException {
- JsonReader reader = new JsonReader(new StringReader(json));
- reader.beginArray();
- reader.nextInt();
- try {
- reader.peek();
- fail();
- } catch (IOException expected) {
- assertEquals(message, expected.getMessage());
- }
- }
-
- private String repeat(char c, int count) {
- char[] array = new char[count];
- Arrays.fill(array, c);
- return new String(array);
- }
-}
diff --git a/core/tests/coretests/src/android/util/JsonWriterTest.java b/core/tests/coretests/src/android/util/JsonWriterTest.java
deleted file mode 100644
index 1239a3c..0000000
--- a/core/tests/coretests/src/android/util/JsonWriterTest.java
+++ /dev/null
@@ -1,466 +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 android.util;
-
-import java.io.IOException;
-import java.io.StringWriter;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import junit.framework.TestCase;
-
-public final class JsonWriterTest extends TestCase {
-
- public void testWrongTopLevelType() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- try {
- jsonWriter.value("a");
- fail();
- } catch (IllegalStateException expected) {
- }
- }
-
- public void testTwoNames() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginObject();
- jsonWriter.name("a");
- try {
- jsonWriter.name("a");
- fail();
- } catch (IllegalStateException expected) {
- }
- }
-
- public void testNameWithoutValue() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginObject();
- jsonWriter.name("a");
- try {
- jsonWriter.endObject();
- fail();
- } catch (IllegalStateException expected) {
- }
- }
-
- public void testValueWithoutName() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginObject();
- try {
- jsonWriter.value(true);
- fail();
- } catch (IllegalStateException expected) {
- }
- }
-
- public void testMultipleTopLevelValues() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginArray().endArray();
- try {
- jsonWriter.beginArray();
- fail();
- } catch (IllegalStateException expected) {
- }
- }
-
- public void testBadNestingObject() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginArray();
- jsonWriter.beginObject();
- try {
- jsonWriter.endArray();
- fail();
- } catch (IllegalStateException expected) {
- }
- }
-
- public void testBadNestingArray() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginArray();
- jsonWriter.beginArray();
- try {
- jsonWriter.endObject();
- fail();
- } catch (IllegalStateException expected) {
- }
- }
-
- public void testNullName() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginObject();
- try {
- jsonWriter.name(null);
- fail();
- } catch (NullPointerException expected) {
- }
- }
-
- public void testNullStringValue() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginObject();
- jsonWriter.name("a");
- jsonWriter.value((String) null);
- jsonWriter.endObject();
- assertEquals("{\"a\":null}", stringWriter.toString());
- }
-
- public void testNonFiniteDoubles() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginArray();
- try {
- jsonWriter.value(Double.NaN);
- fail();
- } catch (IllegalArgumentException expected) {
- }
- try {
- jsonWriter.value(Double.NEGATIVE_INFINITY);
- fail();
- } catch (IllegalArgumentException expected) {
- }
- try {
- jsonWriter.value(Double.POSITIVE_INFINITY);
- fail();
- } catch (IllegalArgumentException expected) {
- }
- }
-
- public void testNonFiniteBoxedDoubles() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginArray();
- try {
- jsonWriter.value(new Double(Double.NaN));
- fail();
- } catch (IllegalArgumentException expected) {
- }
- try {
- jsonWriter.value(new Double(Double.NEGATIVE_INFINITY));
- fail();
- } catch (IllegalArgumentException expected) {
- }
- try {
- jsonWriter.value(new Double(Double.POSITIVE_INFINITY));
- fail();
- } catch (IllegalArgumentException expected) {
- }
- }
-
- public void testDoubles() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginArray();
- jsonWriter.value(-0.0);
- jsonWriter.value(1.0);
- jsonWriter.value(Double.MAX_VALUE);
- jsonWriter.value(Double.MIN_VALUE);
- jsonWriter.value(0.0);
- jsonWriter.value(-0.5);
- jsonWriter.value(Double.MIN_NORMAL);
- jsonWriter.value(Math.PI);
- jsonWriter.value(Math.E);
- jsonWriter.endArray();
- jsonWriter.close();
- assertEquals("[-0.0,"
- + "1.0,"
- + "1.7976931348623157E308,"
- + "4.9E-324,"
- + "0.0,"
- + "-0.5,"
- + "2.2250738585072014E-308,"
- + "3.141592653589793,"
- + "2.718281828459045]", stringWriter.toString());
- }
-
- public void testLongs() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginArray();
- jsonWriter.value(0);
- jsonWriter.value(1);
- jsonWriter.value(-1);
- jsonWriter.value(Long.MIN_VALUE);
- jsonWriter.value(Long.MAX_VALUE);
- jsonWriter.endArray();
- jsonWriter.close();
- assertEquals("[0,"
- + "1,"
- + "-1,"
- + "-9223372036854775808,"
- + "9223372036854775807]", stringWriter.toString());
- }
-
- public void testNumbers() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginArray();
- jsonWriter.value(new BigInteger("0"));
- jsonWriter.value(new BigInteger("9223372036854775808"));
- jsonWriter.value(new BigInteger("-9223372036854775809"));
- jsonWriter.value(new BigDecimal("3.141592653589793238462643383"));
- jsonWriter.endArray();
- jsonWriter.close();
- assertEquals("[0,"
- + "9223372036854775808,"
- + "-9223372036854775809,"
- + "3.141592653589793238462643383]", stringWriter.toString());
- }
-
- public void testBooleans() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginArray();
- jsonWriter.value(true);
- jsonWriter.value(false);
- jsonWriter.endArray();
- assertEquals("[true,false]", stringWriter.toString());
- }
-
- public void testNulls() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginArray();
- jsonWriter.nullValue();
- jsonWriter.endArray();
- assertEquals("[null]", stringWriter.toString());
- }
-
- public void testStrings() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginArray();
- jsonWriter.value("a");
- jsonWriter.value("a\"");
- jsonWriter.value("\"");
- jsonWriter.value(":");
- jsonWriter.value(",");
- jsonWriter.value("\b");
- jsonWriter.value("\f");
- jsonWriter.value("\n");
- jsonWriter.value("\r");
- jsonWriter.value("\t");
- jsonWriter.value(" ");
- jsonWriter.value("\\");
- jsonWriter.value("{");
- jsonWriter.value("}");
- jsonWriter.value("[");
- jsonWriter.value("]");
- jsonWriter.value("\0");
- jsonWriter.value("\u0019");
- jsonWriter.endArray();
- assertEquals("[\"a\","
- + "\"a\\\"\","
- + "\"\\\"\","
- + "\":\","
- + "\",\","
- + "\"\\b\","
- + "\"\\f\","
- + "\"\\n\","
- + "\"\\r\","
- + "\"\\t\","
- + "\" \","
- + "\"\\\\\","
- + "\"{\","
- + "\"}\","
- + "\"[\","
- + "\"]\","
- + "\"\\u0000\","
- + "\"\\u0019\"]", stringWriter.toString());
- }
-
- public void testUnicodeLineBreaksEscaped() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginArray();
- jsonWriter.value("\u2028 \u2029");
- jsonWriter.endArray();
- assertEquals("[\"\\u2028 \\u2029\"]", stringWriter.toString());
- }
-
- public void testEmptyArray() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginArray();
- jsonWriter.endArray();
- assertEquals("[]", stringWriter.toString());
- }
-
- public void testEmptyObject() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginObject();
- jsonWriter.endObject();
- assertEquals("{}", stringWriter.toString());
- }
-
- public void testObjectsInArrays() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginArray();
- jsonWriter.beginObject();
- jsonWriter.name("a").value(5);
- jsonWriter.name("b").value(false);
- jsonWriter.endObject();
- jsonWriter.beginObject();
- jsonWriter.name("c").value(6);
- jsonWriter.name("d").value(true);
- jsonWriter.endObject();
- jsonWriter.endArray();
- assertEquals("[{\"a\":5,\"b\":false},"
- + "{\"c\":6,\"d\":true}]", stringWriter.toString());
- }
-
- public void testArraysInObjects() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginObject();
- jsonWriter.name("a");
- jsonWriter.beginArray();
- jsonWriter.value(5);
- jsonWriter.value(false);
- jsonWriter.endArray();
- jsonWriter.name("b");
- jsonWriter.beginArray();
- jsonWriter.value(6);
- jsonWriter.value(true);
- jsonWriter.endArray();
- jsonWriter.endObject();
- assertEquals("{\"a\":[5,false],"
- + "\"b\":[6,true]}", stringWriter.toString());
- }
-
- public void testDeepNestingArrays() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- for (int i = 0; i < 20; i++) {
- jsonWriter.beginArray();
- }
- for (int i = 0; i < 20; i++) {
- jsonWriter.endArray();
- }
- assertEquals("[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]", stringWriter.toString());
- }
-
- public void testDeepNestingObjects() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginObject();
- for (int i = 0; i < 20; i++) {
- jsonWriter.name("a");
- jsonWriter.beginObject();
- }
- for (int i = 0; i < 20; i++) {
- jsonWriter.endObject();
- }
- jsonWriter.endObject();
- assertEquals("{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":"
- + "{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{"
- + "}}}}}}}}}}}}}}}}}}}}}", stringWriter.toString());
- }
-
- public void testRepeatedName() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginObject();
- jsonWriter.name("a").value(true);
- jsonWriter.name("a").value(false);
- jsonWriter.endObject();
- // JsonWriter doesn't attempt to detect duplicate names
- assertEquals("{\"a\":true,\"a\":false}", stringWriter.toString());
- }
-
- public void testPrettyPrintObject() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.setIndent(" ");
-
- jsonWriter.beginObject();
- jsonWriter.name("a").value(true);
- jsonWriter.name("b").value(false);
- jsonWriter.name("c").value(5.0);
- jsonWriter.name("e").nullValue();
- jsonWriter.name("f").beginArray();
- jsonWriter.value(6.0);
- jsonWriter.value(7.0);
- jsonWriter.endArray();
- jsonWriter.name("g").beginObject();
- jsonWriter.name("h").value(8.0);
- jsonWriter.name("i").value(9.0);
- jsonWriter.endObject();
- jsonWriter.endObject();
-
- String expected = "{\n"
- + " \"a\": true,\n"
- + " \"b\": false,\n"
- + " \"c\": 5.0,\n"
- + " \"e\": null,\n"
- + " \"f\": [\n"
- + " 6.0,\n"
- + " 7.0\n"
- + " ],\n"
- + " \"g\": {\n"
- + " \"h\": 8.0,\n"
- + " \"i\": 9.0\n"
- + " }\n"
- + "}";
- assertEquals(expected, stringWriter.toString());
- }
-
- public void testPrettyPrintArray() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.setIndent(" ");
-
- jsonWriter.beginArray();
- jsonWriter.value(true);
- jsonWriter.value(false);
- jsonWriter.value(5.0);
- jsonWriter.nullValue();
- jsonWriter.beginObject();
- jsonWriter.name("a").value(6.0);
- jsonWriter.name("b").value(7.0);
- jsonWriter.endObject();
- jsonWriter.beginArray();
- jsonWriter.value(8.0);
- jsonWriter.value(9.0);
- jsonWriter.endArray();
- jsonWriter.endArray();
-
- String expected = "[\n"
- + " true,\n"
- + " false,\n"
- + " 5.0,\n"
- + " null,\n"
- + " {\n"
- + " \"a\": 6.0,\n"
- + " \"b\": 7.0\n"
- + " },\n"
- + " [\n"
- + " 8.0,\n"
- + " 9.0\n"
- + " ]\n"
- + "]";
- assertEquals(expected, stringWriter.toString());
- }
-}
diff --git a/core/tests/overlaytests/OverlayAppFirst/Android.mk b/core/tests/overlaytests/OverlayAppFirst/Android.mk
new file mode 100644
index 0000000..ee991fc
--- /dev/null
+++ b/core/tests/overlaytests/OverlayAppFirst/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := com.android.overlaytest.first_app_overlay
+
+include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/OverlayAppFirst/AndroidManifest.xml b/core/tests/overlaytests/OverlayAppFirst/AndroidManifest.xml
new file mode 100644
index 0000000..ec10bbc
--- /dev/null
+++ b/core/tests/overlaytests/OverlayAppFirst/AndroidManifest.xml
@@ -0,0 +1,6 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.overlaytest.first_app_overlay"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <overlay android:targetPackage="com.android.overlaytest" android:priority="1"/>
+</manifest>
diff --git a/core/tests/overlaytests/OverlayTestOverlay/res/drawable-nodpi/default_wallpaper.jpg b/core/tests/overlaytests/OverlayAppFirst/res/drawable-nodpi/drawable.jpg
similarity index 100%
rename from core/tests/overlaytests/OverlayTestOverlay/res/drawable-nodpi/default_wallpaper.jpg
rename to core/tests/overlaytests/OverlayAppFirst/res/drawable-nodpi/drawable.jpg
Binary files differ
diff --git a/core/tests/overlaytests/OverlayAppFirst/res/raw/lorem_ipsum.txt b/core/tests/overlaytests/OverlayAppFirst/res/raw/lorem_ipsum.txt
new file mode 100644
index 0000000..756b0a3
--- /dev/null
+++ b/core/tests/overlaytests/OverlayAppFirst/res/raw/lorem_ipsum.txt
@@ -0,0 +1 @@
+Lorem ipsum: single overlay.
diff --git a/core/tests/overlaytests/OverlayAppFirst/res/values-sv/config.xml b/core/tests/overlaytests/OverlayAppFirst/res/values-sv/config.xml
new file mode 100644
index 0000000..9cdc73e
--- /dev/null
+++ b/core/tests/overlaytests/OverlayAppFirst/res/values-sv/config.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <integer name="matrix_100100">400</integer>
+ <integer name="matrix_100101">400</integer>
+ <integer name="matrix_100110">400</integer>
+ <integer name="matrix_100111">400</integer>
+ <integer name="matrix_101100">400</integer>
+ <integer name="matrix_101101">400</integer>
+ <integer name="matrix_101110">400</integer>
+ <integer name="matrix_101111">400</integer>
+ <integer name="matrix_110100">400</integer>
+ <integer name="matrix_110101">400</integer>
+ <integer name="matrix_110110">400</integer>
+ <integer name="matrix_110111">400</integer>
+ <integer name="matrix_111100">400</integer>
+ <integer name="matrix_111101">400</integer>
+ <integer name="matrix_111110">400</integer>
+ <integer name="matrix_111111">400</integer>
+</resources>
diff --git a/core/tests/overlaytests/OverlayAppFirst/res/values/config.xml b/core/tests/overlaytests/OverlayAppFirst/res/values/config.xml
new file mode 100644
index 0000000..972137a
--- /dev/null
+++ b/core/tests/overlaytests/OverlayAppFirst/res/values/config.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="str">single</string>
+ <string name="str2">single</string>
+ <integer name="matrix_101000">300</integer>
+ <integer name="matrix_101001">300</integer>
+ <integer name="matrix_101010">300</integer>
+ <integer name="matrix_101011">300</integer>
+ <integer name="matrix_101100">300</integer>
+ <integer name="matrix_101101">300</integer>
+ <integer name="matrix_101110">300</integer>
+ <integer name="matrix_101111">300</integer>
+ <integer name="matrix_111000">300</integer>
+ <integer name="matrix_111001">300</integer>
+ <integer name="matrix_111010">300</integer>
+ <integer name="matrix_111011">300</integer>
+ <integer name="matrix_111100">300</integer>
+ <integer name="matrix_111101">300</integer>
+ <integer name="matrix_111110">300</integer>
+ <integer name="matrix_111111">300</integer>
+ <bool name="usually_false">true</bool>
+ <integer-array name="fibonacci">
+ <item>21</item>
+ <item>13</item>
+ <item>8</item>
+ <item>5</item>
+ <item>3</item>
+ <item>2</item>
+ <item>1</item>
+ <item>1</item>
+ </integer-array>
+ <!-- The following integer does not exist in the original package. Idmap
+ generation should therefore ignore it. -->
+ <integer name="integer_not_in_original_package">0</integer>
+</resources>
diff --git a/core/tests/overlaytests/OverlayAppFirst/res/xml/integer.xml b/core/tests/overlaytests/OverlayAppFirst/res/xml/integer.xml
new file mode 100644
index 0000000..7f628d9
--- /dev/null
+++ b/core/tests/overlaytests/OverlayAppFirst/res/xml/integer.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<integer value="1"/>
diff --git a/core/tests/overlaytests/OverlayAppSecond/Android.mk b/core/tests/overlaytests/OverlayAppSecond/Android.mk
new file mode 100644
index 0000000..87402c43
--- /dev/null
+++ b/core/tests/overlaytests/OverlayAppSecond/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := com.android.overlaytest.second_app_overlay
+
+include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/OverlayAppSecond/AndroidManifest.xml b/core/tests/overlaytests/OverlayAppSecond/AndroidManifest.xml
new file mode 100644
index 0000000..ed49863
--- /dev/null
+++ b/core/tests/overlaytests/OverlayAppSecond/AndroidManifest.xml
@@ -0,0 +1,6 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.overlaytest.second_app_overlay"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <overlay android:targetPackage="com.android.overlaytest" android:priority="2"/>
+</manifest>
diff --git a/core/tests/overlaytests/OverlayAppSecond/res/raw/lorem_ipsum.txt b/core/tests/overlaytests/OverlayAppSecond/res/raw/lorem_ipsum.txt
new file mode 100644
index 0000000..613f5b6
--- /dev/null
+++ b/core/tests/overlaytests/OverlayAppSecond/res/raw/lorem_ipsum.txt
@@ -0,0 +1 @@
+Lorem ipsum: multiple overlays.
diff --git a/core/tests/overlaytests/OverlayAppSecond/res/values-sv/config.xml b/core/tests/overlaytests/OverlayAppSecond/res/values-sv/config.xml
new file mode 100644
index 0000000..ec4b6c0
--- /dev/null
+++ b/core/tests/overlaytests/OverlayAppSecond/res/values-sv/config.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <integer name="matrix_100001">600</integer>
+ <integer name="matrix_100011">600</integer>
+ <integer name="matrix_100101">600</integer>
+ <integer name="matrix_100111">600</integer>
+ <integer name="matrix_101001">600</integer>
+ <integer name="matrix_101011">600</integer>
+ <integer name="matrix_101101">600</integer>
+ <integer name="matrix_101111">600</integer>
+ <integer name="matrix_110001">600</integer>
+ <integer name="matrix_110011">600</integer>
+ <integer name="matrix_110101">600</integer>
+ <integer name="matrix_110111">600</integer>
+ <integer name="matrix_111001">600</integer>
+ <integer name="matrix_111011">600</integer>
+ <integer name="matrix_111101">600</integer>
+ <integer name="matrix_111111">600</integer>
+</resources>
diff --git a/core/tests/overlaytests/OverlayAppSecond/res/values/config.xml b/core/tests/overlaytests/OverlayAppSecond/res/values/config.xml
new file mode 100644
index 0000000..8b07216
--- /dev/null
+++ b/core/tests/overlaytests/OverlayAppSecond/res/values/config.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="str">multiple</string>
+ <integer name="matrix_100010">500</integer>
+ <integer name="matrix_100011">500</integer>
+ <integer name="matrix_100110">500</integer>
+ <integer name="matrix_100111">500</integer>
+ <integer name="matrix_101010">500</integer>
+ <integer name="matrix_101011">500</integer>
+ <integer name="matrix_101110">500</integer>
+ <integer name="matrix_101111">500</integer>
+ <integer name="matrix_110010">500</integer>
+ <integer name="matrix_110011">500</integer>
+ <integer name="matrix_110110">500</integer>
+ <integer name="matrix_110111">500</integer>
+ <integer name="matrix_111010">500</integer>
+ <integer name="matrix_111011">500</integer>
+ <integer name="matrix_111110">500</integer>
+ <integer name="matrix_111111">500</integer>
+ <bool name="usually_false">false</bool>
+</resources>
diff --git a/core/tests/overlaytests/OverlayAppSecond/res/xml/integer.xml b/core/tests/overlaytests/OverlayAppSecond/res/xml/integer.xml
new file mode 100644
index 0000000..f3370a6
--- /dev/null
+++ b/core/tests/overlaytests/OverlayAppSecond/res/xml/integer.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<integer value="2"/>
diff --git a/core/tests/overlaytests/OverlayTest/Android.mk b/core/tests/overlaytests/OverlayTest/Android.mk
index f7f67f6..4767e52 100644
--- a/core/tests/overlaytests/OverlayTest/Android.mk
+++ b/core/tests/overlaytests/OverlayTest/Android.mk
@@ -5,6 +5,10 @@
LOCAL_PACKAGE_NAME := OverlayTest
+LOCAL_DEX_PREOPT := false
+
+LOCAL_MODULE_PATH := $(TARGET_OUT)/app
+
LOCAL_SRC_FILES := $(call all-java-files-under, src)
include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/OverlayTest/res/drawable-nodpi/drawable.jpg b/core/tests/overlaytests/OverlayTest/res/drawable-nodpi/drawable.jpg
new file mode 100644
index 0000000..a3f14f3
--- /dev/null
+++ b/core/tests/overlaytests/OverlayTest/res/drawable-nodpi/drawable.jpg
Binary files differ
diff --git a/core/tests/overlaytests/OverlayTest/res/raw/lorem_ipsum.txt b/core/tests/overlaytests/OverlayTest/res/raw/lorem_ipsum.txt
new file mode 100644
index 0000000..cee7a92
--- /dev/null
+++ b/core/tests/overlaytests/OverlayTest/res/raw/lorem_ipsum.txt
@@ -0,0 +1 @@
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
diff --git a/core/tests/overlaytests/OverlayTest/res/values-sv/config.xml b/core/tests/overlaytests/OverlayTest/res/values-sv/config.xml
new file mode 100644
index 0000000..891853e
--- /dev/null
+++ b/core/tests/overlaytests/OverlayTest/res/values-sv/config.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <integer name="matrix_110000">200</integer>
+ <integer name="matrix_110001">200</integer>
+ <integer name="matrix_110010">200</integer>
+ <integer name="matrix_110011">200</integer>
+ <integer name="matrix_110100">200</integer>
+ <integer name="matrix_110101">200</integer>
+ <integer name="matrix_110110">200</integer>
+ <integer name="matrix_110111">200</integer>
+ <integer name="matrix_111000">200</integer>
+ <integer name="matrix_111001">200</integer>
+ <integer name="matrix_111010">200</integer>
+ <integer name="matrix_111011">200</integer>
+ <integer name="matrix_111100">200</integer>
+ <integer name="matrix_111101">200</integer>
+ <integer name="matrix_111110">200</integer>
+ <integer name="matrix_111111">200</integer>
+</resources>
diff --git a/core/tests/overlaytests/OverlayTest/res/values/config.xml b/core/tests/overlaytests/OverlayTest/res/values/config.xml
new file mode 100644
index 0000000..c692a262
--- /dev/null
+++ b/core/tests/overlaytests/OverlayTest/res/values/config.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="str">none</string>
+ <string name="str2">none</string>
+ <integer name="matrix_100000">100</integer>
+ <integer name="matrix_100001">100</integer>
+ <integer name="matrix_100010">100</integer>
+ <integer name="matrix_100011">100</integer>
+ <integer name="matrix_100100">100</integer>
+ <integer name="matrix_100101">100</integer>
+ <integer name="matrix_100110">100</integer>
+ <integer name="matrix_100111">100</integer>
+ <integer name="matrix_101000">100</integer>
+ <integer name="matrix_101001">100</integer>
+ <integer name="matrix_101010">100</integer>
+ <integer name="matrix_101011">100</integer>
+ <integer name="matrix_101100">100</integer>
+ <integer name="matrix_101101">100</integer>
+ <integer name="matrix_101110">100</integer>
+ <integer name="matrix_101111">100</integer>
+ <integer name="matrix_110000">100</integer>
+ <integer name="matrix_110001">100</integer>
+ <integer name="matrix_110010">100</integer>
+ <integer name="matrix_110011">100</integer>
+ <integer name="matrix_110100">100</integer>
+ <integer name="matrix_110101">100</integer>
+ <integer name="matrix_110110">100</integer>
+ <integer name="matrix_110111">100</integer>
+ <integer name="matrix_111000">100</integer>
+ <integer name="matrix_111001">100</integer>
+ <integer name="matrix_111010">100</integer>
+ <integer name="matrix_111011">100</integer>
+ <integer name="matrix_111100">100</integer>
+ <integer name="matrix_111101">100</integer>
+ <integer name="matrix_111110">100</integer>
+ <integer name="matrix_111111">100</integer>
+ <bool name="usually_false">false</bool>
+ <bool name="always_true">true</bool>
+ <integer-array name="fibonacci">
+ <item>1</item>
+ <item>1</item>
+ <item>2</item>
+ <item>3</item>
+ <item>5</item>
+ <item>8</item>
+ <item>13</item>
+ <item>21</item>
+ </integer-array>
+ <integer-array name="prime_numbers">
+ <item>2</item>
+ <item>3</item>
+ <item>5</item>
+ <item>7</item>
+ <item>11</item>
+ <item>13</item>
+ <item>17</item>
+ <item>19</item>
+ </integer-array>
+</resources>
diff --git a/core/tests/overlaytests/OverlayTest/res/xml/integer.xml b/core/tests/overlaytests/OverlayTest/res/xml/integer.xml
new file mode 100644
index 0000000..9383daa
--- /dev/null
+++ b/core/tests/overlaytests/OverlayTest/res/xml/integer.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<integer value="0"/>
diff --git a/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/OverlayBaseTest.java b/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/OverlayBaseTest.java
index 6211c1c..58b7db9 100644
--- a/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/OverlayBaseTest.java
+++ b/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/OverlayBaseTest.java
@@ -2,13 +2,21 @@
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
import android.test.AndroidTestCase;
+import android.util.AttributeSet;
+import android.util.Xml;
+import java.io.BufferedReader;
import java.io.InputStream;
+import java.io.InputStreamReader;
import java.util.Locale;
public abstract class OverlayBaseTest extends AndroidTestCase {
private Resources mResources;
- protected boolean mWithOverlay; // will be set by subclasses
+ protected int mMode; // will be set by subclasses
+ static final protected int MODE_NO_OVERLAY = 0;
+ static final protected int MODE_SINGLE_OVERLAY = 1;
+ static final protected int MODE_MULTIPLE_OVERLAYS = 2;
protected void setUp() {
mResources = getContext().getResources();
@@ -36,20 +44,82 @@
mResources.updateConfiguration(config, mResources.getDisplayMetrics());
}
- private void assertResource(int resId, boolean ewo, boolean ew) throws Throwable {
- boolean expected = mWithOverlay ? ew : ewo;
+ private boolean getExpected(boolean no, boolean so, boolean mo) {
+ switch (mMode) {
+ case MODE_NO_OVERLAY:
+ return no;
+ case MODE_SINGLE_OVERLAY:
+ return so;
+ case MODE_MULTIPLE_OVERLAYS:
+ return mo;
+ default:
+ fail("Unknown mode!");
+ return no;
+ }
+ }
+
+ private String getExpected(String no, String so, String mo) {
+ switch (mMode) {
+ case MODE_NO_OVERLAY:
+ return no;
+ case MODE_SINGLE_OVERLAY:
+ return so;
+ case MODE_MULTIPLE_OVERLAYS:
+ return mo;
+ default:
+ fail("Unknown mode!");
+ return no;
+ }
+ }
+
+ private int getExpected(int no, int so, int mo) {
+ switch (mMode) {
+ case MODE_NO_OVERLAY:
+ return no;
+ case MODE_SINGLE_OVERLAY:
+ return so;
+ case MODE_MULTIPLE_OVERLAYS:
+ return mo;
+ default:
+ fail("Unknown mode!");
+ return no;
+ }
+ }
+
+ private int[] getExpected(int[] no, int[] so, int[] mo) {
+ switch (mMode) {
+ case MODE_NO_OVERLAY:
+ return no;
+ case MODE_SINGLE_OVERLAY:
+ return so;
+ case MODE_MULTIPLE_OVERLAYS:
+ return mo;
+ default:
+ fail("Unknown mode!");
+ return no;
+ }
+ }
+
+ private void assertResource(int resId, boolean no, boolean so, boolean mo) throws Throwable {
+ boolean expected = getExpected(no, so, mo);
boolean actual = mResources.getBoolean(resId);
assertEquals(expected, actual);
}
- private void assertResource(int resId, String ewo, String ew) throws Throwable {
- String expected = mWithOverlay ? ew : ewo;
+ private void assertResource(int resId, int no, int so, int mo) throws Throwable {
+ int expected = getExpected(no, so, mo);
+ int actual = mResources.getInteger(resId);
+ assertEquals(expected, actual);
+ }
+
+ private void assertResource(int resId, String no, String so, String mo) throws Throwable {
+ String expected = getExpected(no, so, mo);
String actual = mResources.getString(resId);
assertEquals(expected, actual);
}
- private void assertResource(int resId, int[] ewo, int[] ew) throws Throwable {
- int[] expected = mWithOverlay ? ew : ewo;
+ private void assertResource(int resId, int[] no, int[] so, int[] mo) throws Throwable {
+ int[] expected = getExpected(no, so, mo);
int[] actual = mResources.getIntArray(resId);
assertEquals("length:", expected.length, actual.length);
for (int i = 0; i < actual.length; ++i) {
@@ -57,62 +127,334 @@
}
}
+ public void testFrameworkBooleanOverlay() throws Throwable {
+ // config_annoy_dianne has the value:
+ // - true when no overlay exists (MODE_NO_OVERLAY)
+ // - false when a single overlay exists (MODE_SINGLE_OVERLAY)
+ // - false when multiple overlays exists (MODE_MULTIPLE_OVERLAYS)
+ final int resId = com.android.internal.R.bool.config_annoy_dianne;
+ assertResource(resId, true, false, false);
+ }
+
public void testBooleanOverlay() throws Throwable {
- // config_automatic_brightness_available has overlay (default config)
- final int resId = com.android.internal.R.bool.config_automatic_brightness_available;
- assertResource(resId, false, true);
+ // usually_false has the value:
+ // - false when no overlay exists (MODE_NO_OVERLAY)
+ // - true when a single overlay exists (MODE_SINGLE_OVERLAY)
+ // - false when multiple overlays exists (MODE_MULTIPLE_OVERLAYS)
+ final int resId = R.bool.usually_false;
+ assertResource(resId, false, true, false);
}
public void testBoolean() throws Throwable {
- // config_annoy_dianne has no overlay
- final int resId = com.android.internal.R.bool.config_annoy_dianne;
- assertResource(resId, true, true);
- }
-
- public void testStringOverlay() throws Throwable {
- // phoneTypeCar has an overlay (default config), which shouldn't shadow
- // the Swedish translation
- final int resId = com.android.internal.R.string.phoneTypeCar;
- setLocale("sv_SE");
- assertResource(resId, "Bil", "Bil");
- }
-
- public void testStringSwedishOverlay() throws Throwable {
- // phoneTypeWork has overlay (no default config, only for lang=sv)
- final int resId = com.android.internal.R.string.phoneTypeWork;
- setLocale("en_US");
- assertResource(resId, "Work", "Work");
- setLocale("sv_SE");
- assertResource(resId, "Arbete", "Jobb");
- }
-
- public void testString() throws Throwable {
- // phoneTypeHome has no overlay
- final int resId = com.android.internal.R.string.phoneTypeHome;
- setLocale("en_US");
- assertResource(resId, "Home", "Home");
- setLocale("sv_SE");
- assertResource(resId, "Hem", "Hem");
+ // always_true has no overlay
+ final int resId = R.bool.always_true;
+ assertResource(resId, true, true, true);
}
public void testIntegerArrayOverlay() throws Throwable {
- // config_scrollBarrierVibePattern has overlay (default config)
- final int resId = com.android.internal.R.array.config_scrollBarrierVibePattern;
- assertResource(resId, new int[]{0, 15, 10, 10}, new int[]{100, 200, 300});
+ // fibonacci has values:
+ // - eight first values of Fibonacci sequence, when no overlay exists (MODE_NO_OVERLAY)
+ // - eight first values of Fibonacci sequence (reversed), for single and multiple overlays
+ // (MODE_SINGLE_OVERLAY, MODE_MULTIPLE_OVERLAYS)
+ final int resId = R.array.fibonacci;
+ assertResource(resId,
+ new int[]{1, 1, 2, 3, 5, 8, 13, 21},
+ new int[]{21, 13, 8, 5, 3, 2, 1, 1},
+ new int[]{21, 13, 8, 5, 3, 2, 1, 1});
}
public void testIntegerArray() throws Throwable {
- // config_virtualKeyVibePattern has no overlay
- final int resId = com.android.internal.R.array.config_virtualKeyVibePattern;
- final int[] expected = {0, 10, 20, 30};
- assertResource(resId, expected, expected);
+ // prime_numbers has no overlay
+ final int resId = R.array.prime_numbers;
+ final int[] expected = {2, 3, 5, 7, 11, 13, 17, 19};
+ assertResource(resId, expected, expected, expected);
}
- public void testAsset() throws Throwable {
- // drawable/default_background.jpg has overlay (default config)
- final int resId = com.android.internal.R.drawable.default_wallpaper;
+ public void testDrawable() throws Throwable {
+ // drawable-nodpi/drawable has overlay (default config)
+ final int resId = R.drawable.drawable;
int actual = calculateRawResourceChecksum(resId);
- int expected = mWithOverlay ? 0x000051da : 0x0014ebce;
+ int expected = 0;
+ switch (mMode) {
+ case MODE_NO_OVERLAY:
+ expected = 0x00005665;
+ break;
+ case MODE_SINGLE_OVERLAY:
+ case MODE_MULTIPLE_OVERLAYS:
+ expected = 0x000051da;
+ break;
+ default:
+ fail("Unknown mode " + mMode);
+ }
assertEquals(expected, actual);
}
+
+ public void testAppString() throws Throwable {
+ final int resId = R.string.str;
+ assertResource(resId, "none", "single", "multiple");
+ }
+
+ public void testApp2() throws Throwable {
+ final int resId = R.string.str2; // only in base package and first app overlay
+ assertResource(resId, "none", "single", "single");
+ }
+
+ public void testAppXml() throws Throwable {
+ int expected = getExpected(0, 1, 2);
+ int actual = -1;
+ XmlResourceParser parser = mResources.getXml(R.xml.integer);
+ int type = parser.getEventType();
+ while (type != XmlResourceParser.END_DOCUMENT && actual == -1) {
+ if (type == XmlResourceParser.START_TAG && "integer".equals(parser.getName())) {
+ AttributeSet as = Xml.asAttributeSet(parser);
+ actual = as.getAttributeIntValue(null, "value", -1);
+ }
+ type = parser.next();
+ }
+ parser.close();
+ assertEquals(expected, actual);
+ }
+
+ public void testAppRaw() throws Throwable {
+ final int resId = R.raw.lorem_ipsum;
+
+ InputStream input = null;
+ BufferedReader reader = null;
+ String actual = "";
+ try {
+ input = mResources.openRawResource(resId);
+ reader = new BufferedReader(new InputStreamReader(input));
+ actual = reader.readLine();
+ } finally {
+ reader.close();
+ input.close();
+ }
+
+ final String no = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, " +
+ "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. " +
+ "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip " +
+ "ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit " +
+ "esse cillum dolore eu fugiat nulla pariatur. " +
+ "Excepteur sint occaecat cupidatat non proident, " +
+ "sunt in culpa qui officia deserunt mollit anim id est laborum.";
+ final String so = "Lorem ipsum: single overlay.";
+ final String mo = "Lorem ipsum: multiple overlays.";
+
+ assertEquals(getExpected(no, so, mo), actual);
+ }
+
+ /*
+ * testMatrix* tests
+ *
+ * The naming convention textMatrixABCDEF refers to in which packages and
+ * which configurations a resource is defined (1 if the resource is
+ * defined). If defined, a slot is always given the same value.
+ *
+ * SLOT PACKAGE CONFIGURATION VALUE
+ * A target package (default) 100
+ * B target package -sv 200
+ * C OverlayAppFirst (default) 300
+ * D OverlayAppFirst -sv 400
+ * E OverlayAppSecond (default) 500
+ * F OverlayAppSecond -sv 600
+ *
+ * Example: in testMatrix101110, the base package defines the
+ * R.integer.matrix101110 resource for the default configuration (value
+ * 100), OverlayAppFirst defines it for both default and Swedish
+ * configurations (values 300 and 400, respectively), and OverlayAppSecond
+ * defines it for the default configuration (value 500). If both overlays
+ * are loaded, the expected value after setting the language to Swedish is
+ * 400.
+ */
+ public void testMatrix100000() throws Throwable {
+ final int resId = R.integer.matrix_100000;
+ setLocale("sv_SE");
+ assertResource(resId, 100, 100, 100);
+ }
+
+ public void testMatrix100001() throws Throwable {
+ final int resId = R.integer.matrix_100001;
+ setLocale("sv_SE");
+ assertResource(resId, 100, 100, 600);
+ }
+
+ public void testMatrix100010() throws Throwable {
+ final int resId = R.integer.matrix_100010;
+ setLocale("sv_SE");
+ assertResource(resId, 100, 100, 500);
+ }
+
+ public void testMatrix100011() throws Throwable {
+ final int resId = R.integer.matrix_100011;
+ setLocale("sv_SE");
+ assertResource(resId, 100, 100, 600);
+ }
+
+ public void testMatrix100100() throws Throwable {
+ final int resId = R.integer.matrix_100100;
+ setLocale("sv_SE");
+ assertResource(resId, 100, 400, 400);
+ }
+
+ public void testMatrix100101() throws Throwable {
+ final int resId = R.integer.matrix_100101;
+ setLocale("sv_SE");
+ assertResource(resId, 100, 400, 600);
+ }
+
+ public void testMatrix100110() throws Throwable {
+ final int resId = R.integer.matrix_100110;
+ setLocale("sv_SE");
+ assertResource(resId, 100, 400, 400);
+ }
+
+ public void testMatrix100111() throws Throwable {
+ final int resId = R.integer.matrix_100111;
+ setLocale("sv_SE");
+ assertResource(resId, 100, 400, 600);
+ }
+
+ public void testMatrix101000() throws Throwable {
+ final int resId = R.integer.matrix_101000;
+ setLocale("sv_SE");
+ assertResource(resId, 100, 300, 300);
+ }
+
+ public void testMatrix101001() throws Throwable {
+ final int resId = R.integer.matrix_101001;
+ setLocale("sv_SE");
+ assertResource(resId, 100, 300, 600);
+ }
+
+ public void testMatrix101010() throws Throwable {
+ final int resId = R.integer.matrix_101010;
+ setLocale("sv_SE");
+ assertResource(resId, 100, 300, 500);
+ }
+
+ public void testMatrix101011() throws Throwable {
+ final int resId = R.integer.matrix_101011;
+ setLocale("sv_SE");
+ assertResource(resId, 100, 300, 600);
+ }
+
+ public void testMatrix101100() throws Throwable {
+ final int resId = R.integer.matrix_101100;
+ setLocale("sv_SE");
+ assertResource(resId, 100, 400, 400);
+ }
+
+ public void testMatrix101101() throws Throwable {
+ final int resId = R.integer.matrix_101101;
+ setLocale("sv_SE");
+ assertResource(resId, 100, 400, 600);
+ }
+
+ public void testMatrix101110() throws Throwable {
+ final int resId = R.integer.matrix_101110;
+ setLocale("sv_SE");
+ assertResource(resId, 100, 400, 400);
+ }
+
+ public void testMatrix101111() throws Throwable {
+ final int resId = R.integer.matrix_101111;
+ setLocale("sv_SE");
+ assertResource(resId, 100, 400, 600);
+ }
+
+ public void testMatrix110000() throws Throwable {
+ final int resId = R.integer.matrix_110000;
+ setLocale("sv_SE");
+ assertResource(resId, 200, 200, 200);
+ }
+
+ public void testMatrix110001() throws Throwable {
+ final int resId = R.integer.matrix_110001;
+ setLocale("sv_SE");
+ assertResource(resId, 200, 200, 600);
+ }
+
+ public void testMatrix110010() throws Throwable {
+ final int resId = R.integer.matrix_110010;
+ setLocale("sv_SE");
+ assertResource(resId, 200, 200, 200);
+ }
+
+ public void testMatrix110011() throws Throwable {
+ final int resId = R.integer.matrix_110011;
+ setLocale("sv_SE");
+ assertResource(resId, 200, 200, 600);
+ }
+
+ public void testMatrix110100() throws Throwable {
+ final int resId = R.integer.matrix_110100;
+ setLocale("sv_SE");
+ assertResource(resId, 200, 400, 400);
+ }
+
+ public void testMatrix110101() throws Throwable {
+ final int resId = R.integer.matrix_110101;
+ setLocale("sv_SE");
+ assertResource(resId, 200, 400, 600);
+ }
+
+ public void testMatrix110110() throws Throwable {
+ final int resId = R.integer.matrix_110110;
+ setLocale("sv_SE");
+ assertResource(resId, 200, 400, 400);
+ }
+
+ public void testMatrix110111() throws Throwable {
+ final int resId = R.integer.matrix_110111;
+ setLocale("sv_SE");
+ assertResource(resId, 200, 400, 600);
+ }
+
+ public void testMatrix111000() throws Throwable {
+ final int resId = R.integer.matrix_111000;
+ setLocale("sv_SE");
+ assertResource(resId, 200, 200, 200);
+ }
+
+ public void testMatrix111001() throws Throwable {
+ final int resId = R.integer.matrix_111001;
+ setLocale("sv_SE");
+ assertResource(resId, 200, 200, 600);
+ }
+
+ public void testMatrix111010() throws Throwable {
+ final int resId = R.integer.matrix_111010;
+ setLocale("sv_SE");
+ assertResource(resId, 200, 200, 200);
+ }
+
+ public void testMatrix111011() throws Throwable {
+ final int resId = R.integer.matrix_111011;
+ setLocale("sv_SE");
+ assertResource(resId, 200, 200, 600);
+ }
+
+ public void testMatrix111100() throws Throwable {
+ final int resId = R.integer.matrix_111100;
+ setLocale("sv_SE");
+ assertResource(resId, 200, 400, 400);
+ }
+
+ public void testMatrix111101() throws Throwable {
+ final int resId = R.integer.matrix_111101;
+ setLocale("sv_SE");
+ assertResource(resId, 200, 400, 600);
+ }
+
+ public void testMatrix111110() throws Throwable {
+ final int resId = R.integer.matrix_111110;
+ setLocale("sv_SE");
+ assertResource(resId, 200, 400, 400);
+ }
+
+ public void testMatrix111111() throws Throwable {
+ final int resId = R.integer.matrix_111111;
+ setLocale("sv_SE");
+ assertResource(resId, 200, 400, 600);
+ }
}
diff --git a/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/WithMultipleOverlaysTest.java b/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/WithMultipleOverlaysTest.java
new file mode 100644
index 0000000..e104f5a
--- /dev/null
+++ b/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/WithMultipleOverlaysTest.java
@@ -0,0 +1,7 @@
+package com.android.overlaytest;
+
+public class WithMultipleOverlaysTest extends OverlayBaseTest {
+ public WithMultipleOverlaysTest() {
+ mMode = MODE_MULTIPLE_OVERLAYS;
+ }
+}
diff --git a/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/WithOverlayTest.java b/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/WithOverlayTest.java
index 1292d03..816a476 100644
--- a/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/WithOverlayTest.java
+++ b/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/WithOverlayTest.java
@@ -2,6 +2,6 @@
public class WithOverlayTest extends OverlayBaseTest {
public WithOverlayTest() {
- mWithOverlay = true;
+ mMode = MODE_SINGLE_OVERLAY;
}
}
diff --git a/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/WithoutOverlayTest.java b/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/WithoutOverlayTest.java
index 630ff8f..318cccc 100644
--- a/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/WithoutOverlayTest.java
+++ b/core/tests/overlaytests/OverlayTest/src/com/android/overlaytest/WithoutOverlayTest.java
@@ -2,6 +2,6 @@
public class WithoutOverlayTest extends OverlayBaseTest {
public WithoutOverlayTest() {
- mWithOverlay = false;
+ mMode = MODE_NO_OVERLAY;
}
}
diff --git a/core/tests/overlaytests/OverlayTestOverlay/AndroidManifest.xml b/core/tests/overlaytests/OverlayTestOverlay/AndroidManifest.xml
index bcbb0d1..f8b6c7b 100644
--- a/core/tests/overlaytests/OverlayTestOverlay/AndroidManifest.xml
+++ b/core/tests/overlaytests/OverlayTestOverlay/AndroidManifest.xml
@@ -2,5 +2,5 @@
package="com.android.overlaytest.overlay"
android:versionCode="1"
android:versionName="1.0">
- <overlay-package android:name="android"/>
+ <overlay android:targetPackage="android" android:priority="1"/>
</manifest>
diff --git a/core/tests/overlaytests/OverlayTestOverlay/res/values-sv/config.xml b/core/tests/overlaytests/OverlayTestOverlay/res/values-sv/config.xml
deleted file mode 100644
index bc52367..0000000
--- a/core/tests/overlaytests/OverlayTestOverlay/res/values-sv/config.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <string name="phoneTypeWork">Jobb</string>
-</resources>
diff --git a/core/tests/overlaytests/OverlayTestOverlay/res/values/config.xml b/core/tests/overlaytests/OverlayTestOverlay/res/values/config.xml
index 794f475..c1e3de1 100644
--- a/core/tests/overlaytests/OverlayTestOverlay/res/values/config.xml
+++ b/core/tests/overlaytests/OverlayTestOverlay/res/values/config.xml
@@ -1,13 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <bool name="config_automatic_brightness_available">true</bool>
- <string name="phoneTypeCar">Automobile</string>
- <integer-array name="config_scrollBarrierVibePattern">
- <item>100</item>
- <item>200</item>
- <item>300</item>
- </integer-array>
- <!-- The following integer does not exist in the original package. Idmap
- generation should therefore ignore it. -->
- <integer name="integer_not_in_original_package">0</integer>
+ <bool name="config_annoy_dianne">false</bool>
</resources>
diff --git a/core/tests/overlaytests/README b/core/tests/overlaytests/README
deleted file mode 100644
index 4b3e6f2..0000000
--- a/core/tests/overlaytests/README
+++ /dev/null
@@ -1,15 +0,0 @@
-Unit tests for runtime resource overlay
-=======================================
-
-As of this writing, runtime resource overlay is only triggered for
-/system/framework/framework-res.apk. Because of this, installation of
-overlay packages require the Android platform be rebooted. However, the
-regular unit tests (triggered via development/testrunner/runtest.py)
-cannot handle reboots. As a workaround, this directory contains a shell
-script which will trigger the tests in a non-standard way.
-
-Once runtime resource overlay may be applied to applications, the tests
-in this directory should be moved to core/tests/coretests. Also, by
-applying runtime resource overlay to a dedicated test application, the
-test cases would not need to assume default values for non-overlaid
-resources.
diff --git a/core/tests/overlaytests/runtests.sh b/core/tests/overlaytests/runtests.sh
deleted file mode 100755
index 0a721ad40..0000000
--- a/core/tests/overlaytests/runtests.sh
+++ /dev/null
@@ -1,129 +0,0 @@
-#!/bin/bash
-
-adb="adb"
-if [[ $# -gt 0 ]]; then
- adb="adb $*" # for setting -e, -d or -s <serial>
-fi
-
-function atexit()
-{
- local retval=$?
-
- if [[ $retval -eq 0 ]]; then
- rm $log
- else
- echo "There were errors, please check log at $log"
- fi
-}
-
-log=$(mktemp)
-trap "atexit" EXIT
-
-function compile_module()
-{
- local android_mk="$1"
-
- echo "Compiling .${android_mk:${#PWD}}"
- ONE_SHOT_MAKEFILE="$android_mk" make -C "../../../../../" files | tee -a $log
- if [[ ${PIPESTATUS[0]} -ne 0 ]]; then
- exit 1
- fi
-}
-
-function wait_for_boot_completed()
-{
- echo "Rebooting device"
- $adb wait-for-device logcat -c
- $adb wait-for-device logcat | grep -m 1 -e 'PowerManagerService.*bootCompleted' >/dev/null
-}
-
-function mkdir_if_needed()
-{
- local path="$1"
-
- if [[ "${path:0:1}" != "/" ]]; then
- echo "mkdir_if_needed: error: path '$path' does not begin with /" | tee -a $log
- exit 1
- fi
-
- local basename=$(basename "$path")
- local dirname=$(dirname "$path")
- local t=$($adb shell ls -l $dirname | tr -d '\r' | grep -e "${basename}$" | grep -oe '^.')
-
- case "$t" in
- d) # File exists, and is a directory ...
- # do nothing
- ;;
- l) # ... (or symbolic link possibly to a directory).
- # do nothing
- ;;
- "") # File does not exist.
- mkdir_if_needed "$dirname"
- $adb shell mkdir "$path"
- ;;
- *) # File exists, but is not a directory.
- echo "mkdir_if_needed: file '$path' exists, but is not a directory" | tee -a $log
- exit 1
- ;;
- esac
-}
-
-function disable_overlay()
-{
- echo "Disabling overlay"
- $adb shell rm /vendor/overlay/framework/framework-res.apk
- $adb shell rm /data/resource-cache/vendor@overlay@framework@framework-res.apk@idmap
-}
-
-function enable_overlay()
-{
- echo "Enabling overlay"
- mkdir_if_needed "/system/vendor"
- mkdir_if_needed "/vendor/overlay/framework"
- $adb shell ln -s /data/app/com.android.overlaytest.overlay.apk /vendor/overlay/framework/framework-res.apk
-}
-
-function instrument()
-{
- local class="$1"
-
- echo "Instrumenting $class"
- $adb shell am instrument -w -e class $class com.android.overlaytest/android.test.InstrumentationTestRunner | tee -a $log
-}
-
-function remount()
-{
- echo "Remounting file system writable"
- $adb remount | tee -a $log
-}
-
-function sync()
-{
- echo "Syncing to device"
- $adb sync data | tee -a $log
-}
-
-# some commands require write access, remount once and for all
-remount
-
-# build and sync
-compile_module "$PWD/OverlayTest/Android.mk"
-compile_module "$PWD/OverlayTestOverlay/Android.mk"
-sync
-
-# instrument test (without overlay)
-$adb shell stop
-disable_overlay
-$adb shell start
-wait_for_boot_completed
-instrument "com.android.overlaytest.WithoutOverlayTest"
-
-# instrument test (with overlay)
-$adb shell stop
-enable_overlay
-$adb shell start
-wait_for_boot_completed
-instrument "com.android.overlaytest.WithOverlayTest"
-
-# cleanup
-exit $(grep -c -e '^FAILURES' $log)
diff --git a/core/tests/overlaytests/testrunner.py b/core/tests/overlaytests/testrunner.py
new file mode 100755
index 0000000..4f94373
--- /dev/null
+++ b/core/tests/overlaytests/testrunner.py
@@ -0,0 +1,679 @@
+#!/usr/bin/python
+import hashlib
+import optparse
+import os
+import re
+import shlex
+import subprocess
+import sys
+import threading
+import time
+
+TASK_COMPILATION = 'compile'
+TASK_DISABLE_OVERLAYS = 'disable overlays'
+TASK_ENABLE_MULTIPLE_OVERLAYS = 'enable multiple overlays'
+TASK_ENABLE_SINGLE_OVERLAY = 'enable single overlay'
+TASK_FILE_EXISTS_TEST = 'test (file exists)'
+TASK_GREP_IDMAP_TEST = 'test (grep idmap)'
+TASK_MD5_TEST = 'test (md5)'
+TASK_IDMAP_PATH = 'idmap --path'
+TASK_IDMAP_SCAN = 'idmap --scan'
+TASK_INSTRUMENTATION = 'instrumentation'
+TASK_INSTRUMENTATION_TEST = 'test (instrumentation)'
+TASK_MKDIR = 'mkdir'
+TASK_PUSH = 'push'
+TASK_ROOT = 'root'
+TASK_REMOUNT = 'remount'
+TASK_RM = 'rm'
+TASK_SETUP_IDMAP_PATH = 'setup idmap --path'
+TASK_SETUP_IDMAP_SCAN = 'setup idmap --scan'
+TASK_START = 'start'
+TASK_STOP = 'stop'
+
+adb = 'adb'
+
+def _adb_shell(cmd):
+ argv = shlex.split(adb + " shell '" + cmd + "; echo $?'")
+ proc = subprocess.Popen(argv, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (stdout, stderr) = proc.communicate()
+ (stdout, stderr) = (stdout.replace('\r', ''), stderr.replace('\r', ''))
+ tmp = stdout.rsplit('\n', 2)
+ if len(tmp) == 2:
+ stdout == ''
+ returncode = int(tmp[0])
+ else:
+ stdout = tmp[0] + '\n'
+ returncode = int(tmp[1])
+ return returncode, stdout, stderr
+
+class VerbosePrinter:
+ class Ticker(threading.Thread):
+ def _print(self):
+ s = '\r' + self.text + '[' + '.' * self.i + ' ' * (4 - self.i) + ']'
+ sys.stdout.write(s)
+ sys.stdout.flush()
+ self.i = (self.i + 1) % 5
+
+ def __init__(self, cond_var, text):
+ threading.Thread.__init__(self)
+ self.text = text
+ self.setDaemon(True)
+ self.cond_var = cond_var
+ self.running = False
+ self.i = 0
+ self._print()
+ self.running = True
+
+ def run(self):
+ self.cond_var.acquire()
+ while True:
+ self.cond_var.wait(0.25)
+ running = self.running
+ if not running:
+ break
+ self._print()
+ self.cond_var.release()
+
+ def stop(self):
+ self.cond_var.acquire()
+ self.running = False
+ self.cond_var.notify_all()
+ self.cond_var.release()
+
+ def _start_ticker(self):
+ self.ticker = VerbosePrinter.Ticker(self.cond_var, self.text)
+ self.ticker.start()
+
+ def _stop_ticker(self):
+ self.ticker.stop()
+ self.ticker.join()
+ self.ticker = None
+
+ def _format_begin(self, type, name):
+ N = self.width - len(type) - len(' [ ] ')
+ fmt = '%%s %%-%ds ' % N
+ return fmt % (type, name)
+
+ def __init__(self, use_color):
+ self.cond_var = threading.Condition()
+ self.ticker = None
+ if use_color:
+ self.color_RED = '\033[1;31m'
+ self.color_red = '\033[0;31m'
+ self.color_reset = '\033[0;37m'
+ else:
+ self.color_RED = ''
+ self.color_red = ''
+ self.color_reset = ''
+
+ argv = shlex.split('stty size') # get terminal width
+ proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (stdout, stderr) = proc.communicate()
+ if proc.returncode == 0:
+ (h, w) = stdout.split()
+ self.width = int(w)
+ else:
+ self.width = 72 # conservative guesstimate
+
+ def begin(self, type, name):
+ self.text = self._format_begin(type, name)
+ sys.stdout.write(self.text + '[ ]')
+ sys.stdout.flush()
+ self._start_ticker()
+
+ def end_pass(self, type, name):
+ self._stop_ticker()
+ sys.stdout.write('\r' + self.text + '[ OK ]\n')
+ sys.stdout.flush()
+
+ def end_fail(self, type, name, msg):
+ self._stop_ticker()
+ sys.stdout.write('\r' + self.color_RED + self.text + '[FAIL]\n')
+ sys.stdout.write(self.color_red)
+ sys.stdout.write(msg)
+ sys.stdout.write(self.color_reset)
+ sys.stdout.flush()
+
+class QuietPrinter:
+ def begin(self, type, name):
+ pass
+
+ def end_pass(self, type, name):
+ sys.stdout.write('PASS ' + type + ' ' + name + '\n')
+ sys.stdout.flush()
+
+ def end_fail(self, type, name, msg):
+ sys.stdout.write('FAIL ' + type + ' ' + name + '\n')
+ sys.stdout.flush()
+
+class CompilationTask:
+ def __init__(self, makefile):
+ self.makefile = makefile
+
+ def get_type(self):
+ return TASK_COMPILATION
+
+ def get_name(self):
+ return self.makefile
+
+ def execute(self):
+ os.putenv('ONE_SHOT_MAKEFILE', os.getcwd() + "/" + self.makefile)
+ argv = shlex.split('make -C "../../../../../" files')
+ proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (stdout, stderr) = proc.communicate()
+ return proc.returncode, stdout, stderr
+
+class InstrumentationTask:
+ def __init__(self, instrumentation_class):
+ self.instrumentation_class = instrumentation_class
+
+ def get_type(self):
+ return TASK_INSTRUMENTATION
+
+ def get_name(self):
+ return self.instrumentation_class
+
+ def execute(self):
+ return _adb_shell('am instrument -r -w -e class %s com.android.overlaytest/android.test.InstrumentationTestRunner' % self.instrumentation_class)
+
+class PushTask:
+ def __init__(self, src, dest):
+ self.src = src
+ self.dest = dest
+
+ def get_type(self):
+ return TASK_PUSH
+
+ def get_name(self):
+ return "%s -> %s" % (self.src, self.dest)
+
+ def execute(self):
+ src = os.getenv('OUT') + "/" + self.src
+ argv = shlex.split(adb + ' push %s %s' % (src, self.dest))
+ proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (stdout, stderr) = proc.communicate()
+ return proc.returncode, stdout, stderr
+
+class MkdirTask:
+ def __init__(self, path):
+ self.path = path
+
+ def get_type(self):
+ return TASK_MKDIR
+
+ def get_name(self):
+ return self.path
+
+ def execute(self):
+ return _adb_shell('mkdir -p %s' % self.path)
+
+class RmTask:
+ def __init__(self, path):
+ self.path = path
+
+ def get_type(self):
+ return TASK_RM
+
+ def get_name(self):
+ return self.path
+
+ def execute(self):
+ returncode, stdout, stderr = _adb_shell('ls %s' % self.path)
+ if returncode != 0 and stdout.endswith(': No such file or directory\n'):
+ return 0, "", ""
+ return _adb_shell('rm -r %s' % self.path)
+
+class IdmapPathTask:
+ def __init__(self, path_target_apk, path_overlay_apk, path_idmap):
+ self.path_target_apk = path_target_apk
+ self.path_overlay_apk = path_overlay_apk
+ self.path_idmap = path_idmap
+
+ def get_type(self):
+ return TASK_IDMAP_PATH
+
+ def get_name(self):
+ return self.path_idmap
+
+ def execute(self):
+ return _adb_shell('su system idmap --path "%s" "%s" "%s"' % (self.path_target_apk, self.path_overlay_apk, self.path_idmap))
+
+class IdmapScanTask:
+ def __init__(self, overlay_dir, target_pkg_name, target_pkg, idmap_dir, symlink_dir):
+ self.overlay_dir = overlay_dir
+ self.target_pkg_name = target_pkg_name
+ self.target_pkg = target_pkg
+ self.idmap_dir = idmap_dir
+ self.symlink_dir = symlink_dir
+
+ def get_type(self):
+ return TASK_IDMAP_SCAN
+
+ def get_name(self):
+ return self.target_pkg_name
+
+ def execute(self):
+ return _adb_shell('su system idmap --scan "%s" "%s" "%s" "%s"' % (self.overlay_dir, self.target_pkg_name, self.target_pkg, self.idmap_dir))
+
+class FileExistsTest:
+ def __init__(self, path):
+ self.path = path
+
+ def get_type(self):
+ return TASK_FILE_EXISTS_TEST
+
+ def get_name(self):
+ return self.path
+
+ def execute(self):
+ return _adb_shell('ls %s' % self.path)
+
+class GrepIdmapTest:
+ def __init__(self, path_idmap, pattern, expected_n):
+ self.path_idmap = path_idmap
+ self.pattern = pattern
+ self.expected_n = expected_n
+
+ def get_type(self):
+ return TASK_GREP_IDMAP_TEST
+
+ def get_name(self):
+ return self.pattern
+
+ def execute(self):
+ returncode, stdout, stderr = _adb_shell('idmap --inspect %s' % self.path_idmap)
+ if returncode != 0:
+ return returncode, stdout, stderr
+ all_matches = re.findall('\s' + self.pattern + '$', stdout, flags=re.MULTILINE)
+ if len(all_matches) != self.expected_n:
+ return 1, 'pattern=%s idmap=%s expected=%d found=%d\n' % (self.pattern, self.path_idmap, self.expected_n, len(all_matches)), ''
+ return 0, "", ""
+
+class Md5Test:
+ def __init__(self, path, expected_content):
+ self.path = path
+ self.expected_md5 = hashlib.md5(expected_content).hexdigest()
+
+ def get_type(self):
+ return TASK_MD5_TEST
+
+ def get_name(self):
+ return self.path
+
+ def execute(self):
+ returncode, stdout, stderr = _adb_shell('md5 %s' % self.path)
+ if returncode != 0:
+ return returncode, stdout, stderr
+ actual_md5 = stdout.split()[0]
+ if actual_md5 != self.expected_md5:
+ return 1, 'expected %s, got %s\n' % (self.expected_md5, actual_md5), ''
+ return 0, "", ""
+
+class StartTask:
+ def get_type(self):
+ return TASK_START
+
+ def get_name(self):
+ return ""
+
+ def execute(self):
+ (returncode, stdout, stderr) = _adb_shell('start')
+ if returncode != 0:
+ return returncode, stdout, stderr
+
+ while True:
+ (returncode, stdout, stderr) = _adb_shell('getprop dev.bootcomplete')
+ if returncode != 0:
+ return returncode, stdout, stderr
+ if stdout.strip() == "1":
+ break
+ time.sleep(0.5)
+
+ return 0, "", ""
+
+class StopTask:
+ def get_type(self):
+ return TASK_STOP
+
+ def get_name(self):
+ return ""
+
+ def execute(self):
+ (returncode, stdout, stderr) = _adb_shell('stop')
+ if returncode != 0:
+ return returncode, stdout, stderr
+ return _adb_shell('setprop dev.bootcomplete 0')
+
+class RootTask:
+ def get_type(self):
+ return TASK_ROOT
+
+ def get_name(self):
+ return ""
+
+ def execute(self):
+ (returncode, stdout, stderr) = _adb_shell('getprop service.adb.root 0')
+ if returncode != 0:
+ return returncode, stdout, stderr
+ if stdout.strip() == '1': # already root
+ return 0, "", ""
+
+ argv = shlex.split(adb + ' root')
+ proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (stdout, stderr) = proc.communicate()
+ if proc.returncode != 0:
+ return proc.returncode, stdout, stderr
+
+ argv = shlex.split(adb + ' wait-for-device')
+ proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (stdout, stderr) = proc.communicate()
+ return proc.returncode, stdout, stderr
+
+class RemountTask:
+ def get_type(self):
+ return TASK_REMOUNT
+
+ def get_name(self):
+ return ""
+
+ def execute(self):
+ argv = shlex.split(adb + ' remount')
+ proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ (stdout, stderr) = proc.communicate()
+ # adb remount returns 0 even if the operation failed, so check stdout
+ if stdout.startswith('remount failed:'):
+ return 1, stdout, stderr
+ return proc.returncode, stdout, stderr
+
+class CompoundTask:
+ def __init__(self, type, tasks):
+ self.type = type
+ self.tasks = tasks
+
+ def get_type(self):
+ return self.type
+
+ def get_name(self):
+ return ""
+
+ def execute(self):
+ for t in self.tasks:
+ (returncode, stdout, stderr) = t.execute()
+ if returncode != 0:
+ return returncode, stdout, stderr
+ return 0, "", ""
+
+def _create_disable_overlays_task():
+ tasks = [
+ RmTask("/vendor/overlay/framework_a.apk"),
+ RmTask("/vendor/overlay/framework_b.apk"),
+ RmTask("/data/resource-cache/vendor@overlay@framework_a.apk@idmap"),
+ RmTask("/data/resource-cache/vendor@overlay@framework_b.apk@idmap"),
+ RmTask("/vendor/overlay/app_a.apk"),
+ RmTask("/vendor/overlay/app_b.apk"),
+ RmTask("/data/resource-cache/vendor@overlay@app_a.apk@idmap"),
+ RmTask("/data/resource-cache/vendor@overlay@app_b.apk@idmap"),
+ ]
+ return CompoundTask(TASK_DISABLE_OVERLAYS, tasks)
+
+def _create_enable_single_overlay_task():
+ tasks = [
+ _create_disable_overlays_task(),
+ MkdirTask('/system/vendor'),
+ MkdirTask('/vendor/overlay'),
+ PushTask('/data/app/com.android.overlaytest.overlay.apk', '/vendor/overlay/framework_a.apk'),
+ PushTask('/data/app/com.android.overlaytest.first_app_overlay.apk', '/vendor/overlay/app_a.apk'),
+ ]
+ return CompoundTask(TASK_ENABLE_SINGLE_OVERLAY, tasks)
+
+def _create_enable_multiple_overlays_task():
+ tasks = [
+ _create_disable_overlays_task(),
+ MkdirTask('/system/vendor'),
+ MkdirTask('/vendor/overlay'),
+
+ PushTask('/data/app/com.android.overlaytest.overlay.apk', '/vendor/overlay/framework_b.apk'),
+ PushTask('/data/app/com.android.overlaytest.first_app_overlay.apk', '/vendor/overlay/app_a.apk'),
+ PushTask('/data/app/com.android.overlaytest.second_app_overlay.apk', '/vendor/overlay/app_b.apk'),
+ ]
+ return CompoundTask(TASK_ENABLE_MULTIPLE_OVERLAYS, tasks)
+
+def _create_setup_idmap_path_task(idmaps, symlinks):
+ tasks = [
+ _create_enable_single_overlay_task(),
+ RmTask(symlinks),
+ RmTask(idmaps),
+ MkdirTask(idmaps),
+ MkdirTask(symlinks),
+ ]
+ return CompoundTask(TASK_SETUP_IDMAP_PATH, tasks)
+
+def _create_setup_idmap_scan_task(idmaps, symlinks):
+ tasks = [
+ _create_enable_single_overlay_task(),
+ RmTask(symlinks),
+ RmTask(idmaps),
+ MkdirTask(idmaps),
+ MkdirTask(symlinks),
+ _create_enable_multiple_overlays_task(),
+ ]
+ return CompoundTask(TASK_SETUP_IDMAP_SCAN, tasks)
+
+def _handle_instrumentation_task_output(stdout, printer):
+ regex_status_code = re.compile(r'^INSTRUMENTATION_STATUS_CODE: -?(\d+)')
+ regex_name = re.compile(r'^INSTRUMENTATION_STATUS: test=(.*)')
+ regex_begin_stack = re.compile(r'^INSTRUMENTATION_STATUS: stack=(.*)')
+ regex_end_stack = re.compile(r'^$')
+
+ failed_tests = 0
+ current_test = None
+ current_stack = []
+ mode_stack = False
+ for line in stdout.split("\n"):
+ line = line.rstrip() # strip \r from adb output
+ m = regex_status_code.match(line)
+ if m:
+ c = int(m.group(1))
+ if c == 1:
+ printer.begin(TASK_INSTRUMENTATION_TEST, current_test)
+ elif c == 0:
+ printer.end_pass(TASK_INSTRUMENTATION_TEST, current_test)
+ else:
+ failed_tests += 1
+ current_stack.append("\n")
+ msg = "\n".join(current_stack)
+ printer.end_fail(TASK_INSTRUMENTATION_TEST, current_test, msg.rstrip() + '\n')
+ continue
+
+ m = regex_name.match(line)
+ if m:
+ current_test = m.group(1)
+ continue
+
+ m = regex_begin_stack.match(line)
+ if m:
+ mode_stack = True
+ current_stack = []
+ current_stack.append(" " + m.group(1))
+ continue
+
+ m = regex_end_stack.match(line)
+ if m:
+ mode_stack = False
+ continue
+
+ if mode_stack:
+ current_stack.append(" " + line.strip())
+
+ return failed_tests
+
+def _set_adb_device(option, opt, value, parser):
+ global adb
+ if opt == '-d' or opt == '--device':
+ adb = 'adb -d'
+ if opt == '-e' or opt == '--emulator':
+ adb = 'adb -e'
+ if opt == '-s' or opt == '--serial':
+ adb = 'adb -s ' + value
+
+def _create_opt_parser():
+ parser = optparse.OptionParser()
+ parser.add_option('-d', '--device', action='callback', callback=_set_adb_device,
+ help='pass -d to adb')
+ parser.add_option('-e', '--emulator', action='callback', callback=_set_adb_device,
+ help='pass -e to adb')
+ parser.add_option('-s', '--serial', type="str", action='callback', callback=_set_adb_device,
+ help='pass -s <serical> to adb')
+ parser.add_option('-C', '--no-color', action='store_false',
+ dest='use_color', default=True,
+ help='disable color escape sequences in output')
+ parser.add_option('-q', '--quiet', action='store_true',
+ dest='quiet_mode', default=False,
+ help='quiet mode, output only results')
+ parser.add_option('-b', '--no-build', action='store_false',
+ dest='do_build', default=True,
+ help='do not rebuild test projects')
+ parser.add_option('-k', '--continue', action='store_true',
+ dest='do_continue', default=False,
+ help='do not rebuild test projects')
+ parser.add_option('-i', '--test-idmap', action='store_true',
+ dest='test_idmap', default=False,
+ help='run tests for single overlay')
+ parser.add_option('-0', '--test-no-overlay', action='store_true',
+ dest='test_no_overlay', default=False,
+ help='run tests without any overlay')
+ parser.add_option('-1', '--test-single-overlay', action='store_true',
+ dest='test_single_overlay', default=False,
+ help='run tests for single overlay')
+ parser.add_option('-2', '--test-multiple-overlays', action='store_true',
+ dest='test_multiple_overlays', default=False,
+ help='run tests for multiple overlays')
+ return parser
+
+if __name__ == '__main__':
+ opt_parser = _create_opt_parser()
+ opts, args = opt_parser.parse_args(sys.argv[1:])
+ if not opts.test_idmap and not opts.test_no_overlay and not opts.test_single_overlay and not opts.test_multiple_overlays:
+ opts.test_idmap = True
+ opts.test_no_overlay = True
+ opts.test_single_overlay = True
+ opts.test_multiple_overlays = True
+ if len(args) > 0:
+ opt_parser.error("unexpected arguments: %s" % " ".join(args))
+ # will never reach this: opt_parser.error will call sys.exit
+
+ if opts.quiet_mode:
+ printer = QuietPrinter()
+ else:
+ printer = VerbosePrinter(opts.use_color)
+ tasks = []
+
+ # must be in the same directory as this script for compilation tasks to work
+ script = sys.argv[0]
+ dirname = os.path.dirname(script)
+ wd = os.path.realpath(dirname)
+ os.chdir(wd)
+
+ # build test cases
+ if opts.do_build:
+ tasks.append(CompilationTask('OverlayTest/Android.mk'))
+ tasks.append(CompilationTask('OverlayTestOverlay/Android.mk'))
+ tasks.append(CompilationTask('OverlayAppFirst/Android.mk'))
+ tasks.append(CompilationTask('OverlayAppSecond/Android.mk'))
+
+ # remount filesystem, install test project
+ tasks.append(RootTask())
+ tasks.append(RemountTask())
+ tasks.append(PushTask('/system/app/OverlayTest.apk', '/system/app/OverlayTest.apk'))
+
+ # test idmap
+ if opts.test_idmap:
+ idmaps='/data/local/tmp/idmaps'
+ symlinks='/data/local/tmp/symlinks'
+
+ # idmap --path
+ tasks.append(StopTask())
+ tasks.append(_create_setup_idmap_path_task(idmaps, symlinks))
+ tasks.append(StartTask())
+ tasks.append(IdmapPathTask('/vendor/overlay/framework_a.apk', '/system/framework/framework-res.apk', idmaps + '/a.idmap'))
+ tasks.append(FileExistsTest(idmaps + '/a.idmap'))
+ tasks.append(GrepIdmapTest(idmaps + '/a.idmap', 'bool/config_annoy_dianne', 1))
+
+ # idmap --scan
+ idmap = idmaps + '/vendor@overlay@framework_b.apk@idmap'
+ tasks.append(StopTask())
+ tasks.append(_create_setup_idmap_scan_task(idmaps, symlinks))
+ tasks.append(StartTask())
+ tasks.append(IdmapScanTask('/vendor/overlay', 'android', '/system/framework/framework-res.apk', idmaps, symlinks))
+ tasks.append(FileExistsTest(idmap))
+ tasks.append(GrepIdmapTest(idmap, 'bool/config_annoy_dianne', 1))
+
+ # overlays.list
+ overlays_list_path = '/data/resource-cache/overlays.list'
+ expected_content = '''\
+/vendor/overlay/framework_b.apk /data/resource-cache/vendor@overlay@framework_b.apk@idmap
+'''
+ tasks.append(FileExistsTest(overlays_list_path))
+ tasks.append(Md5Test(overlays_list_path, expected_content))
+
+ # idmap cleanup
+ tasks.append(RmTask(symlinks))
+ tasks.append(RmTask(idmaps))
+
+ # test no overlay
+ if opts.test_no_overlay:
+ tasks.append(StopTask())
+ tasks.append(_create_disable_overlays_task())
+ tasks.append(StartTask())
+ tasks.append(InstrumentationTask('com.android.overlaytest.WithoutOverlayTest'))
+
+ # test single overlay
+ if opts.test_single_overlay:
+ tasks.append(StopTask())
+ tasks.append(_create_enable_single_overlay_task())
+ tasks.append(StartTask())
+ tasks.append(InstrumentationTask('com.android.overlaytest.WithOverlayTest'))
+
+ # test multiple overlays
+ if opts.test_multiple_overlays:
+ tasks.append(StopTask())
+ tasks.append(_create_enable_multiple_overlays_task())
+ tasks.append(StartTask())
+ tasks.append(InstrumentationTask('com.android.overlaytest.WithMultipleOverlaysTest'))
+
+ ignored_errors = 0
+ for t in tasks:
+ type = t.get_type()
+ name = t.get_name()
+ if type == TASK_INSTRUMENTATION:
+ # InstrumentationTask will run several tests, but we want it
+ # to appear as if each test was run individually. Calling
+ # "am instrument" with a single test method is prohibitively
+ # expensive, so let's instead post-process the output to
+ # emulate individual calls.
+ retcode, stdout, stderr = t.execute()
+ if retcode != 0:
+ printer.begin(TASK_INSTRUMENTATION, name)
+ printer.end_fail(TASK_INSTRUMENTATION, name, stderr)
+ sys.exit(retcode)
+ retcode = _handle_instrumentation_task_output(stdout, printer)
+ if retcode != 0:
+ if not opts.do_continue:
+ sys.exit(retcode)
+ else:
+ ignored_errors += retcode
+ else:
+ printer.begin(type, name)
+ retcode, stdout, stderr = t.execute()
+ if retcode == 0:
+ printer.end_pass(type, name)
+ if retcode != 0:
+ if len(stderr) == 0:
+ # hope for output from stdout instead (true for eg adb shell rm)
+ stderr = stdout
+ printer.end_fail(type, name, stderr)
+ if not opts.do_continue:
+ sys.exit(retcode)
+ else:
+ ignored_errors += retcode
+ sys.exit(ignored_errors)
diff --git a/graphics/java/android/graphics/pdf/PdfDocument.java b/graphics/java/android/graphics/pdf/PdfDocument.java
index 29d14a2..f5b07c1 100644
--- a/graphics/java/android/graphics/pdf/PdfDocument.java
+++ b/graphics/java/android/graphics/pdf/PdfDocument.java
@@ -82,7 +82,7 @@
private final List<PageInfo> mPages = new ArrayList<PageInfo>();
- private int mNativeDocument;
+ private long mNativeDocument;
private Page mCurrentPage;
@@ -235,20 +235,20 @@
}
}
- private native int nativeCreateDocument();
+ private native long nativeCreateDocument();
- private native void nativeClose(int document);
+ private native void nativeClose(long nativeDocument);
- private native void nativeFinishPage(int document);
+ private native void nativeFinishPage(long nativeDocument);
- private native void nativeWriteTo(int document, OutputStream out, byte[] chunk);
+ private native void nativeWriteTo(long nativeDocument, OutputStream out, byte[] chunk);
- private static native int nativeStartPage(int documentPtr, int pageWidth, int pageHeight,
+ private static native long nativeStartPage(long nativeDocument, int pageWidth, int pageHeight,
int contentLeft, int contentTop, int contentRight, int contentBottom);
private final class PdfCanvas extends Canvas {
- public PdfCanvas(int nativeCanvas) {
+ public PdfCanvas(long nativeCanvas) {
super(nativeCanvas);
}
diff --git a/include/androidfw/AssetManager.h b/include/androidfw/AssetManager.h
index a010957..a13dd16 100644
--- a/include/androidfw/AssetManager.h
+++ b/include/androidfw/AssetManager.h
@@ -69,6 +69,13 @@
*/
class AssetManager : public AAssetManager {
public:
+ static const char* RESOURCES_FILENAME;
+ static const char* IDMAP_BIN;
+ static const char* OVERLAY_DIR;
+ static const char* TARGET_PACKAGE_NAME;
+ static const char* TARGET_APK_PATH;
+ static const char* IDMAP_DIR;
+
typedef enum CacheMode {
CACHE_UNKNOWN = 0,
CACHE_OFF, // don't try to cache file locations
@@ -93,6 +100,7 @@
* newly-added asset source.
*/
bool addAssetPath(const String8& path, int32_t* cookie);
+ bool addOverlayPath(const String8& path, int32_t* cookie);
/*
* Convenience for adding the standard system assets. Uses the
@@ -218,6 +226,13 @@
*/
void getLocales(Vector<String8>* locales) const;
+ /**
+ * Generate idmap data to translate resources IDs between a package and a
+ * corresponding overlay package.
+ */
+ bool createIdmap(const char* targetApkPath, const char* overlayApkPath,
+ uint32_t targetCrc, uint32_t overlayCrc, uint32_t** outData, size_t* outSize);
+
private:
struct asset_path
{
@@ -264,19 +279,14 @@
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);
+ void addSystemOverlays(const char* pathOverlaysList, const String8& targetPackagePath,
+ ResTable* sharedRes, size_t offset) const;
class SharedZip : public RefBase {
public:
- static sp<SharedZip> get(const String8& path);
+ static sp<SharedZip> get(const String8& path, bool createIfNotPresent = true);
ZipFileRO* getZip();
@@ -287,6 +297,9 @@
ResTable* setResourceTable(ResTable* res);
bool isUpToDate();
+
+ void addOverlay(const asset_path& ap);
+ bool getOverlay(size_t idx, asset_path* out) const;
protected:
~SharedZip();
@@ -302,6 +315,8 @@
Asset* mResourceTableAsset;
ResTable* mResourceTable;
+ Vector<asset_path> mOverlays;
+
static Mutex gLock;
static DefaultKeyedVector<String8, wp<SharedZip> > gOpen;
};
@@ -334,6 +349,9 @@
static String8 getPathName(const char* path);
bool isUpToDate();
+
+ void addOverlay(const String8& path, const asset_path& overlay);
+ bool getOverlay(const String8& path, size_t idx, asset_path* out) const;
private:
void closeZip(int idx);
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index 6799766..a0bae12 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -808,6 +808,19 @@
uint32_t lastPublicKey;
};
+// The most specific locale can consist of:
+//
+// - a 3 char language code
+// - a 3 char region code prefixed by a 'r'
+// - a 4 char script code prefixed by a 's'
+// - a 8 char variant code prefixed by a 'v'
+//
+// each separated by a single char separator, which sums up to a total of 24
+// chars, (25 include the string terminator) rounded up to 28 to be 4 byte
+// aligned.
+#define RESTABLE_MAX_LOCALE_LEN 28
+
+
/**
* Describes a particular resource configuration.
*/
@@ -828,10 +841,42 @@
union {
struct {
- // \0\0 means "any". Otherwise, en, fr, etc.
+ // This field can take three different forms:
+ // - \0\0 means "any".
+ //
+ // - Two 7 bit ascii values interpreted as ISO-639-1 language
+ // codes ('fr', 'en' etc. etc.). The high bit for both bytes is
+ // zero.
+ //
+ // - A single 16 bit little endian packed value representing an
+ // ISO-639-2 3 letter language code. This will be of the form:
+ //
+ // {1, t, t, t, t, t, s, s, s, s, s, f, f, f, f, f}
+ //
+ // bit[0, 4] = first letter of the language code
+ // bit[5, 9] = second letter of the language code
+ // bit[10, 14] = third letter of the language code.
+ // bit[15] = 1 always
+ //
+ // For backwards compatibility, languages that have unambiguous
+ // two letter codes are represented in that format.
+ //
+ // The layout is always bigendian irrespective of the runtime
+ // architecture.
char language[2];
- // \0\0 means "any". Otherwise, US, CA, etc.
+ // This field can take three different forms:
+ // - \0\0 means "any".
+ //
+ // - Two 7 bit ascii values interpreted as 2 letter region
+ // codes ('US', 'GB' etc.). The high bit for both bytes is zero.
+ //
+ // - An UN M.49 3 digit region code. For simplicity, these are packed
+ // in the same manner as the language codes, though we should need
+ // only 10 bits to represent them, instead of the 15.
+ //
+ // The layout is always bigendian irrespective of the runtime
+ // architecture.
char country[2];
};
uint32_t locale;
@@ -933,7 +978,7 @@
SDKVERSION_ANY = 0
};
- enum {
+ enum {
MINORVERSION_ANY = 0
};
@@ -1006,6 +1051,15 @@
uint32_t screenSizeDp;
};
+ // The ISO-15924 short name for the script corresponding to this
+ // configuration. (eg. Hant, Latn, etc.). Interpreted in conjunction with
+ // the locale field.
+ char localeScript[4];
+
+ // A single BCP-47 variant subtag. Will vary in length between 5 and 8
+ // chars. Interpreted in conjunction with the locale field.
+ char localeVariant[8];
+
void copyFromDeviceNoSwap(const ResTable_config& o);
void copyFromDtoH(const ResTable_config& o);
@@ -1063,7 +1117,46 @@
// settings is the requested settings
bool match(const ResTable_config& settings) const;
- void getLocale(char str[6]) const;
+ // Get the string representation of the locale component of this
+ // Config. The maximum size of this representation will be
+ // |RESTABLE_MAX_LOCALE_LEN| (including a terminating '\0').
+ //
+ // Example: en-US, en-Latn-US, en-POSIX.
+ void getBcp47Locale(char* out) const;
+
+ // Sets the values of language, region, script and variant to the
+ // well formed BCP-47 locale contained in |in|. The input locale is
+ // assumed to be valid and no validation is performed.
+ void setBcp47Locale(const char* in);
+
+ inline void clearLocale() {
+ locale = 0;
+ memset(localeScript, 0, sizeof(localeScript));
+ memset(localeVariant, 0, sizeof(localeVariant));
+ }
+
+ // Get the 2 or 3 letter language code of this configuration. Trailing
+ // bytes are set to '\0'.
+ size_t unpackLanguage(char language[4]) const;
+ // Get the 2 or 3 letter language code of this configuration. Trailing
+ // bytes are set to '\0'.
+ size_t unpackRegion(char region[4]) const;
+
+ // Sets the language code of this configuration to the first three
+ // chars at |language|.
+ //
+ // If |language| is a 2 letter code, the trailing byte must be '\0' or
+ // the BCP-47 separator '-'.
+ void packLanguage(const char* language);
+ // Sets the region code of this configuration to the first three bytes
+ // at |region|. If |region| is a 2 letter code, the trailing byte must be '\0'
+ // or the BCP-47 separator '-'.
+ void packRegion(const char* region);
+
+ // Returns a positive integer if this config is more specific than |o|
+ // with respect to their locales, a negative integer if |o| is more specific
+ // and 0 if they're equally specific.
+ int isLocaleMoreSpecificThan(const ResTable_config &o) const;
String8 toString() const;
};
@@ -1284,7 +1377,7 @@
~ResTable();
status_t add(Asset* asset, const int32_t cookie, bool copyData,
- const void* idmap);
+ const void* idmap = NULL);
status_t add(const void *data, size_t size);
status_t add(ResTable* src);
@@ -1545,18 +1638,21 @@
// 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;
+ status_t createIdmap(const ResTable& overlay,
+ uint32_t targetCrc, uint32_t overlayCrc,
+ const char* targetPath, const char* overlayPath,
+ void** outData, size_t* outSize) const;
enum {
- IDMAP_HEADER_SIZE_BYTES = 3 * sizeof(uint32_t),
+ IDMAP_HEADER_SIZE_BYTES = 3 * sizeof(uint32_t) + 2 * 256,
};
// 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);
+ uint32_t* pTargetCrc, uint32_t* pOverlayCrc,
+ String8* pTargetPath, String8* pOverlayPath);
void print(bool inclValues) const;
static String8 normalizeForOutput(const char* input);
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 08328d0..b4d482a 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -41,10 +41,8 @@
#include <assert.h>
#include <dirent.h>
#include <errno.h>
-#include <fcntl.h>
+#include <string.h> // strerror
#include <strings.h>
-#include <sys/stat.h>
-#include <unistd.h>
#ifndef TEMP_FAILURE_RETRY
/* Used to retry syscalls that can return EINTR. */
@@ -75,7 +73,7 @@
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* kResourceCache = "resource-cache";
static const char* kExcludeExtension = ".EXCLUDE";
@@ -83,14 +81,20 @@
static volatile int32_t gCount = 0;
+const char* AssetManager::RESOURCES_FILENAME = "resources.arsc";
+const char* AssetManager::IDMAP_BIN = "/system/bin/idmap";
+const char* AssetManager::OVERLAY_DIR = "/vendor/overlay";
+const char* AssetManager::TARGET_PACKAGE_NAME = "android";
+const char* AssetManager::TARGET_APK_PATH = "/system/framework/framework-res.apk";
+const char* AssetManager::IDMAP_DIR = "/data/resource-cache";
+
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);
+ path.appendPath(kResourceCache);
char buf[256]; // 256 chars should be enough for anyone...
strncpy(buf, pkgPath.string(), 255);
@@ -208,180 +212,99 @@
*cookie = static_cast<int32_t>(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 {
- ALOGW("failed to add overlay package %s\n", overlayPath.string());
- }
- }
+#ifdef HAVE_ANDROID_OS
+ // Load overlays, if any
+ asset_path oap;
+ for (size_t idx = 0; mZipSet.getOverlay(ap.path, idx, &oap); idx++) {
+ mAssetPaths.add(oap);
}
+#endif
return true;
}
-bool AssetManager::isIdmapStaleLocked(const String8& originalPath, const String8& overlayPath,
- const String8& idmapPath)
+bool AssetManager::addOverlayPath(const String8& packagePath, int32_t* cookie)
{
- struct stat st;
- if (TEMP_FAILURE_RETRY(stat(idmapPath.string(), &st)) == -1) {
- if (errno == ENOENT) {
- return true; // non-existing idmap is always stale
- } else {
- ALOGW("failed to stat file %s: %s\n", idmapPath.string(), strerror(errno));
- return false;
- }
- }
- if (st.st_size < ResTable::IDMAP_HEADER_SIZE_BYTES) {
- 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) {
- ALOGW("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));
+ const String8 idmapPath = idmapPathForPackagePath(packagePath);
- uint32_t cachedOriginalCrc, cachedOverlayCrc;
- if (!ResTable::getIdmapInfo(buf, ResTable::IDMAP_HEADER_SIZE_BYTES,
- &cachedOriginalCrc, &cachedOverlayCrc)) {
+ AutoMutex _l(mLock);
+
+ for (size_t i = 0; i < mAssetPaths.size(); ++i) {
+ if (mAssetPaths[i].idmap == idmapPath) {
+ *cookie = static_cast<int32_t>(i + 1);
+ return true;
+ }
+ }
+
+ Asset* idmap = NULL;
+ if ((idmap = openAssetFromFileLocked(idmapPath, Asset::ACCESS_BUFFER)) == NULL) {
+ ALOGW("failed to open idmap file %s\n", idmapPath.string());
return false;
}
- uint32_t actualOriginalCrc, actualOverlayCrc;
- if (!getZipEntryCrcLocked(originalPath, "resources.arsc", &actualOriginalCrc)) {
+ String8 targetPath;
+ String8 overlayPath;
+ if (!ResTable::getIdmapInfo(idmap->getBuffer(false), idmap->getLength(),
+ NULL, NULL, &targetPath, &overlayPath)) {
+ ALOGW("failed to read idmap file %s\n", idmapPath.string());
+ delete idmap;
return false;
}
- if (!getZipEntryCrcLocked(overlayPath, "resources.arsc", &actualOverlayCrc)) {
- return false;
- }
- return cachedOriginalCrc != actualOriginalCrc || cachedOverlayCrc != actualOverlayCrc;
-}
+ delete idmap;
-bool AssetManager::getZipEntryCrcLocked(const String8& zipPath, const char* entryFilename,
- uint32_t* pCrc)
+ if (overlayPath != packagePath) {
+ ALOGW("idmap file %s inconcistent: expected path %s does not match actual path %s\n",
+ idmapPath.string(), packagePath.string(), overlayPath.string());
+ return false;
+ }
+ if (access(targetPath.string(), R_OK) != 0) {
+ ALOGW("failed to access file %s: %s\n", targetPath.string(), strerror(errno));
+ return false;
+ }
+ if (access(idmapPath.string(), R_OK) != 0) {
+ ALOGW("failed to access file %s: %s\n", idmapPath.string(), strerror(errno));
+ return false;
+ }
+ if (access(overlayPath.string(), R_OK) != 0) {
+ ALOGW("failed to access file %s: %s\n", overlayPath.string(), strerror(errno));
+ return false;
+ }
+
+ asset_path oap;
+ oap.path = overlayPath;
+ oap.type = ::getFileType(overlayPath.string());
+ oap.idmap = idmapPath;
+#if 0
+ ALOGD("Overlay added: targetPath=%s overlayPath=%s idmapPath=%s\n",
+ targetPath.string(), overlayPath.string(), idmapPath.string());
+#endif
+ mAssetPaths.add(oap);
+ *cookie = static_cast<int32_t>(mAssetPaths.size());
+
+ return true;
+ }
+
+bool AssetManager::createIdmap(const char* targetApkPath, const char* overlayApkPath,
+ uint32_t targetCrc, uint32_t overlayCrc, uint32_t** outData, size_t* outSize)
{
- 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;
- }
-
- const bool gotInfo = zip->getEntryInfo(entry, NULL, NULL, NULL, NULL, NULL, (long*)pCrc);
- zip->releaseEntry(entry);
-
- return gotInfo;
-}
-
-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());
+ AutoMutex _l(mLock);
+ const String8 paths[2] = { String8(targetApkPath), String8(overlayApkPath) };
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];
+ 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;
+ return false;
}
tables[i].add(ass, 1, false /* copyData */, NULL /* idMap */);
}
- 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;
+ return tables[0].createIdmap(tables[1], targetCrc, overlayCrc,
+ targetApkPath, overlayApkPath, (void**)outData, outSize) == NO_ERROR;
}
bool AssetManager::addDefaultAssets()
@@ -463,17 +386,8 @@
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;
- }
+ char spec[RESTABLE_MAX_LOCALE_LEN];
+ config.getBcp47Locale(spec);
setLocaleLocked(spec);
} else {
updateResourceParamsLocked();
@@ -660,6 +574,10 @@
// which we want to avoid parsing every time.
sharedRes = const_cast<AssetManager*>(this)->
mZipSet.getZipResourceTable(ap.path);
+ if (sharedRes != NULL) {
+ // skip ahead the number of system overlay packages preloaded
+ i += sharedRes->getTableCount() - 1;
+ }
}
if (sharedRes == NULL) {
ass = const_cast<AssetManager*>(this)->
@@ -683,6 +601,14 @@
ALOGV("Creating shared resources for %s", ap.path.string());
sharedRes = new ResTable();
sharedRes->add(ass, i + 1, false, idmap);
+#ifdef HAVE_ANDROID_OS
+ const char* data = getenv("ANDROID_DATA");
+ LOG_ALWAYS_FATAL_IF(data == NULL, "ANDROID_DATA not set");
+ String8 overlaysListPath(data);
+ overlaysListPath.appendPath(kResourceCache);
+ overlaysListPath.appendPath("overlays.list");
+ addSystemOverlays(overlaysListPath.string(), ap.path, sharedRes, i);
+#endif
sharedRes = const_cast<AssetManager*>(this)->
mZipSet.setZipResourceTable(ap.path, sharedRes);
}
@@ -733,20 +659,11 @@
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 (mLocale) {
+ mConfig->setBcp47Locale(mLocale);
+ } else {
+ mConfig->clearLocale();
}
- if (llen >= 5) {
- mConfig->country[0] = mLocale[3];
- mConfig->country[1] = mLocale[4];
- }
- mConfig->size = sizeof(*mConfig);
res->setParameters(mConfig);
}
@@ -766,6 +683,46 @@
return ass;
}
+void AssetManager::addSystemOverlays(const char* pathOverlaysList,
+ const String8& targetPackagePath, ResTable* sharedRes, size_t offset) const
+{
+ FILE* fin = fopen(pathOverlaysList, "r");
+ if (fin == NULL) {
+ return;
+ }
+
+ char buf[1024];
+ while (fgets(buf, sizeof(buf), fin)) {
+ // format of each line:
+ // <path to apk><space><path to idmap><newline>
+ char* space = strchr(buf, ' ');
+ char* newline = strchr(buf, '\n');
+ asset_path oap;
+
+ if (space == NULL || newline == NULL || newline < space) {
+ continue;
+ }
+
+ oap.path = String8(buf, space - buf);
+ oap.type = kFileTypeRegular;
+ oap.idmap = String8(space + 1, newline - space - 1);
+
+ Asset* oass = const_cast<AssetManager*>(this)->
+ openNonAssetInPathLocked("resources.arsc",
+ Asset::ACCESS_BUFFER,
+ oap);
+
+ if (oass != NULL) {
+ Asset* oidmap = openIdmapLocked(oap);
+ offset++;
+ sharedRes->add(oass, offset + 1, false, oidmap);
+ const_cast<AssetManager*>(this)->mAssetPaths.add(oap);
+ const_cast<AssetManager*>(this)->mZipSet.addOverlay(targetPackagePath, oap);
+ }
+ }
+ fclose(fin);
+}
+
const ResTable& AssetManager::getResources(bool required) const
{
const ResTable* rt = getResTable(required);
@@ -1824,7 +1781,8 @@
}
}
-sp<AssetManager::SharedZip> AssetManager::SharedZip::get(const String8& path)
+sp<AssetManager::SharedZip> AssetManager::SharedZip::get(const String8& path,
+ bool createIfNotPresent)
{
AutoMutex _l(gLock);
time_t modWhen = getFileModDate(path);
@@ -1832,6 +1790,9 @@
if (zip != NULL && zip->mModWhen == modWhen) {
return zip;
}
+ if (zip == NULL && !createIfNotPresent) {
+ return NULL;
+ }
zip = new SharedZip(path, modWhen);
gOpen.add(path, zip);
return zip;
@@ -1890,6 +1851,20 @@
return mModWhen == modWhen;
}
+void AssetManager::SharedZip::addOverlay(const asset_path& ap)
+{
+ mOverlays.add(ap);
+}
+
+bool AssetManager::SharedZip::getOverlay(size_t idx, asset_path* out) const
+{
+ if (idx >= mOverlays.size()) {
+ return false;
+ }
+ *out = mOverlays[idx];
+ return true;
+}
+
AssetManager::SharedZip::~SharedZip()
{
//ALOGI("Destroying SharedZip %p %s\n", this, (const char*)mPath);
@@ -2013,6 +1988,22 @@
return true;
}
+void AssetManager::ZipSet::addOverlay(const String8& path, const asset_path& overlay)
+{
+ int idx = getIndex(path);
+ sp<SharedZip> zip = mZipFile[idx];
+ zip->addOverlay(overlay);
+}
+
+bool AssetManager::ZipSet::getOverlay(const String8& path, size_t idx, asset_path* out) const
+{
+ sp<SharedZip> zip = SharedZip::get(path, false);
+ if (zip == NULL) {
+ return false;
+ }
+ return zip->getOverlay(idx, out);
+}
+
/*
* Compute the zip file's index.
*
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 72d331c..efb589a 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -66,11 +66,6 @@
// size measured in sizeof(uint32_t)
#define IDMAP_HEADER_SIZE (ResTable::IDMAP_HEADER_SIZE_BYTES / sizeof(uint32_t))
-static void printToLogFunc(int32_t cookie, const char* txt)
-{
- ALOGV("[cookie=%d] %s", cookie, 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.
@@ -284,11 +279,37 @@
if (!assertIdmapHeader(map, mapSize)) {
return UNKNOWN_ERROR;
}
+ if (mapSize <= IDMAP_HEADER_SIZE + 1) {
+ ALOGW("corrupt idmap: map size %d too short\n", mapSize);
+ return UNKNOWN_ERROR;
+ }
+ uint32_t typeCount = *(map + IDMAP_HEADER_SIZE);
+ if (typeCount == 0) {
+ ALOGW("corrupt idmap: no types\n");
+ return UNKNOWN_ERROR;
+ }
+ if (IDMAP_HEADER_SIZE + 1 + typeCount > mapSize) {
+ ALOGW("corrupt idmap: number of types %d extends past idmap size %d\n", typeCount, mapSize);
+ return UNKNOWN_ERROR;
+ }
const uint32_t* p = map + IDMAP_HEADER_SIZE + 1;
+ // find first defined type
while (*p == 0) {
++p;
+ if (--typeCount == 0) {
+ ALOGW("corrupt idmap: types declared, none found\n");
+ return UNKNOWN_ERROR;
+ }
}
- *outId = (map[*p + IDMAP_HEADER_SIZE + 2] >> 24) & 0x000000ff;
+
+ // determine package id from first entry of first type
+ const uint32_t offset = *p + IDMAP_HEADER_SIZE + 2;
+ if (offset > mapSize) {
+ ALOGW("corrupt idmap: entry offset %d points outside map size %d\n", offset, mapSize);
+ return UNKNOWN_ERROR;
+ }
+ *outId = (map[offset] >> 24) & 0x000000ff;
+
return NO_ERROR;
}
@@ -1539,6 +1560,71 @@
}
}
+/* static */ size_t unpackLanguageOrRegion(const char in[2], const char base,
+ char out[4]) {
+ if (in[0] & 0x80) {
+ // The high bit is "1", which means this is a packed three letter
+ // language code.
+
+ // The smallest 5 bits of the second char are the first alphabet.
+ const uint8_t first = in[1] & 0x1f;
+ // The last three bits of the second char and the first two bits
+ // of the first char are the second alphabet.
+ const uint8_t second = ((in[1] & 0xe0) >> 5) + ((in[0] & 0x03) << 3);
+ // Bits 3 to 7 (inclusive) of the first char are the third alphabet.
+ const uint8_t third = (in[0] & 0x7c) >> 2;
+
+ out[0] = first + base;
+ out[1] = second + base;
+ out[2] = third + base;
+ out[3] = 0;
+
+ return 3;
+ }
+
+ if (in[0]) {
+ memcpy(out, in, 2);
+ memset(out + 2, 0, 2);
+ return 2;
+ }
+
+ memset(out, 0, 4);
+ return 0;
+}
+
+/* static */ void packLanguageOrRegion(const char* in, const char base,
+ char out[2]) {
+ if (in[2] == 0 || in[2] == '-') {
+ out[0] = in[0];
+ out[1] = in[1];
+ } else {
+ uint8_t first = (in[0] - base) & 0x00ef;
+ uint8_t second = (in[1] - base) & 0x00ef;
+ uint8_t third = (in[2] - base) & 0x00ef;
+
+ out[0] = (0x80 | (third << 2) | (second >> 3));
+ out[1] = ((second << 5) | first);
+ }
+}
+
+
+void ResTable_config::packLanguage(const char* language) {
+ packLanguageOrRegion(language, 'a', this->language);
+}
+
+void ResTable_config::packRegion(const char* region) {
+ packLanguageOrRegion(region, '0', this->country);
+}
+
+size_t ResTable_config::unpackLanguage(char language[4]) const {
+ return unpackLanguageOrRegion(this->language, 'a', language);
+}
+
+size_t ResTable_config::unpackRegion(char region[4]) const {
+ return unpackLanguageOrRegion(this->country, '0', region);
+}
+
+
void ResTable_config::copyFromDtoH(const ResTable_config& o) {
copyFromDeviceNoSwap(o);
size = sizeof(ResTable_config);
@@ -1568,10 +1654,30 @@
screenHeightDp = htods(screenHeightDp);
}
+/* static */ inline int compareLocales(const ResTable_config &l, const ResTable_config &r) {
+ if (l.locale != r.locale) {
+ // NOTE: This is the old behaviour with respect to comparison orders.
+ // The diff value here doesn't make much sense (given our bit packing scheme)
+ // but it's stable, and that's all we need.
+ return l.locale - r.locale;
+ }
+
+ // The language & region are equal, so compare the scripts and variants.
+ int script = memcmp(l.localeScript, r.localeScript, sizeof(l.localeScript));
+ if (script) {
+ return script;
+ }
+
+ // The language, region and script are equal, so compare variants.
+ //
+ // This should happen very infrequently (if at all.)
+ return memcmp(l.localeVariant, r.localeVariant, sizeof(l.localeVariant));
+}
+
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);
+ diff = compareLocales(*this, o);
if (diff != 0) return diff;
diff = (int32_t)(screenType - o.screenType);
if (diff != 0) return diff;
@@ -1598,18 +1704,15 @@
if (mnc != o.mnc) {
return mnc < o.mnc ? -1 : 1;
}
- if (language[0] != o.language[0]) {
- return language[0] < o.language[0] ? -1 : 1;
+
+ int diff = compareLocales(*this, o);
+ if (diff < 0) {
+ return -1;
}
- if (language[1] != o.language[1]) {
- return language[1] < o.language[1] ? -1 : 1;
+ if (diff > 0) {
+ return 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 ((screenLayout & MASK_LAYOUTDIR) != (o.screenLayout & MASK_LAYOUTDIR)) {
return (screenLayout & MASK_LAYOUTDIR) < (o.screenLayout & MASK_LAYOUTDIR) ? -1 : 1;
}
@@ -1656,7 +1759,6 @@
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;
@@ -1671,9 +1773,44 @@
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;
+
+ const int diff = compareLocales(*this, o);
+ if (diff) diffs |= CONFIG_LOCALE;
+
return diffs;
}
+int ResTable_config::isLocaleMoreSpecificThan(const ResTable_config& o) const {
+ if (locale || o.locale) {
+ if (language[0] != o.language[0]) {
+ if (!language[0]) return -1;
+ if (!o.language[0]) return 1;
+ }
+
+ if (country[0] != o.country[0]) {
+ if (!country[0]) return -1;
+ if (!o.country[0]) return 1;
+ }
+ }
+
+ // There isn't a well specified "importance" order between variants and
+ // scripts. We can't easily tell whether, say "en-Latn-US" is more or less
+ // specific than "en-US-POSIX".
+ //
+ // We therefore arbitrarily decide to give priority to variants over
+ // scripts since it seems more useful to do so. We will consider
+ // "en-US-POSIX" to be more specific than "en-Latn-US".
+
+ const int score = ((localeScript[0] != 0) ? 1 : 0) +
+ ((localeVariant[0] != 0) ? 2 : 0);
+
+ const int oScore = ((o.localeScript[0] != 0) ? 1 : 0) +
+ ((o.localeVariant[0] != 0) ? 2 : 0);
+
+ return score - oScore;
+
+}
+
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
@@ -1691,14 +1828,13 @@
}
if (locale || o.locale) {
- if (language[0] != o.language[0]) {
- if (!language[0]) return false;
- if (!o.language[0]) return true;
+ const int diff = isLocaleMoreSpecificThan(o);
+ if (diff < 0) {
+ return false;
}
- if (country[0] != o.country[0]) {
- if (!country[0]) return false;
- if (!o.country[0]) return true;
+ if (diff > 0) {
+ return true;
}
}
@@ -1834,6 +1970,18 @@
}
}
+ if (localeScript[0] || o.localeScript[0]) {
+ if (localeScript[0] != o.localeScript[0] && requested->localeScript[0]) {
+ return localeScript[0];
+ }
+ }
+
+ if (localeVariant[0] || o.localeVariant[0]) {
+ if (localeVariant[0] != o.localeVariant[0] && requested->localeVariant[0]) {
+ return localeVariant[0];
+ }
+ }
+
if (screenLayout || o.screenLayout) {
if (((screenLayout^o.screenLayout) & MASK_LAYOUTDIR) != 0
&& (requested->screenLayout & MASK_LAYOUTDIR)) {
@@ -2054,17 +2202,23 @@
}
}
if (locale != 0) {
+ // Don't consider the script & variants when deciding matches.
+ //
+ // If we two configs differ only in their script or language, they
+ // can be weeded out in the isMoreSpecificThan test.
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 layoutDir = screenLayout&MASK_LAYOUTDIR;
const int setLayoutDir = settings.screenLayout&MASK_LAYOUTDIR;
@@ -2166,17 +2320,92 @@
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];
- }
+void ResTable_config::getBcp47Locale(char str[RESTABLE_MAX_LOCALE_LEN]) const {
+ memset(str, 0, RESTABLE_MAX_LOCALE_LEN);
+
+ // This represents the "any" locale value, which has traditionally been
+ // represented by the empty string.
+ if (!language[0] && !country[0]) {
+ return;
}
+
+ size_t charsWritten = 0;
+ if (language[0]) {
+ charsWritten += unpackLanguage(str);
+ }
+
+ if (localeScript[0]) {
+ if (charsWritten) {
+ str[charsWritten++] = '-';
+ }
+ memcpy(str + charsWritten, localeScript, sizeof(localeScript));
+ charsWritten += sizeof(localeScript);
+ }
+
+ if (country[0]) {
+ if (charsWritten) {
+ str[charsWritten++] = '-';
+ }
+ charsWritten += unpackRegion(str + charsWritten);
+ }
+
+ if (localeVariant[0]) {
+ if (charsWritten) {
+ str[charsWritten++] = '-';
+ }
+ memcpy(str + charsWritten, localeVariant, sizeof(localeVariant));
+ }
+}
+
+/* static */ inline bool assignLocaleComponent(ResTable_config* config,
+ const char* start, size_t size) {
+
+ switch (size) {
+ case 0:
+ return false;
+ case 2:
+ case 3:
+ config->language[0] ? config->packRegion(start) : config->packLanguage(start);
+ break;
+ case 4:
+ config->localeScript[0] = toupper(start[0]);
+ for (size_t i = 1; i < 4; ++i) {
+ config->localeScript[i] = tolower(start[i]);
+ }
+ break;
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ for (size_t i = 0; i < size; ++i) {
+ config->localeVariant[i] = tolower(start[i]);
+ }
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+void ResTable_config::setBcp47Locale(const char* in) {
+ locale = 0;
+ memset(localeScript, 0, sizeof(localeScript));
+ memset(localeVariant, 0, sizeof(localeVariant));
+
+ const char* separator = in;
+ const char* start = in;
+ while ((separator = strchr(start, '-')) != NULL) {
+ const size_t size = separator - start;
+ if (!assignLocaleComponent(this, start, size)) {
+ fprintf(stderr, "Invalid BCP-47 locale string: %s", in);
+ }
+
+ start = (separator + 1);
+ }
+
+ const size_t size = in + strlen(in) - start;
+ assignLocaleComponent(this, start, size);
}
String8 ResTable_config::toString() const {
@@ -2190,14 +2419,10 @@
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);
- }
+ char localeStr[RESTABLE_MAX_LOCALE_LEN];
+ getBcp47Locale(localeStr);
+ res.append(localeStr);
+
if ((screenLayout&MASK_LAYOUTDIR) != 0) {
if (res.size() > 0) res.append("-");
switch (screenLayout&ResTable_config::MASK_LAYOUTDIR) {
@@ -4950,18 +5175,20 @@
const size_t L = type->configs.size();
for (size_t l=0; l<L; l++) {
const ResTable_type* config = type->configs[l];
- const ResTable_config* cfg = &config->config;
+ ResTable_config cfg;
+ memset(&cfg, 0, sizeof(ResTable_config));
+ cfg.copyFromDtoH(config->config);
// only insert unique
const size_t M = configs->size();
size_t m;
for (m=0; m<M; m++) {
- if (0 == (*configs)[m].compare(*cfg)) {
+ if (0 == (*configs)[m].compare(cfg)) {
break;
}
}
// if we didn't find it
if (m == M) {
- configs->add(*cfg);
+ configs->add(cfg);
}
}
}
@@ -4976,9 +5203,10 @@
getConfigurations(&configs);
ALOGV("called getConfigurations size=%d", (int)configs.size());
const size_t I = configs.size();
+
+ char locale[RESTABLE_MAX_LOCALE_LEN];
for (size_t i=0; i<I; i++) {
- char locale[6];
- configs[i].getLocale(locale);
+ configs[i].getBcp47Locale(locale);
const size_t J = locales->size();
size_t j;
for (j=0; j<J; j++) {
@@ -5334,23 +5562,30 @@
return NO_ERROR;
}
-status_t ResTable::createIdmap(const ResTable& overlay, uint32_t originalCrc, uint32_t overlayCrc,
- void** outData, size_t* outSize) const
+status_t ResTable::createIdmap(const ResTable& overlay,
+ uint32_t targetCrc, uint32_t overlayCrc,
+ const char* targetPath, const char* overlayPath,
+ void** outData, size_t* outSize) const
{
// see README for details on the format of map
if (mPackageGroups.size() == 0) {
+ ALOGW("idmap: target package has no package groups, cannot create idmap\n");
return UNKNOWN_ERROR;
}
if (mPackageGroups[0]->packages.size() == 0) {
+ ALOGW("idmap: target package has no packages in its first package group, "
+ "cannot create idmap\n");
return UNKNOWN_ERROR;
}
Vector<Vector<uint32_t> > map;
+ // overlaid packages are assumed to contain only one package group
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);
+ // overlay packages are assumed to contain only one package group
const String16 overlayPackage(overlay.mPackageGroups[0]->packages[0]->package->name);
const uint32_t pkg_id = pkg->package->id << 24;
@@ -5368,7 +5603,7 @@
| (0x00ff0000 & ((typeIndex+1)<<16))
| (0x0000ffff & (entryIndex));
resource_name resName;
- if (!this->getResourceName(resID, true, &resName)) {
+ if (!this->getResourceName(resID, false, &resName)) {
ALOGW("idmap: resource 0x%08x has spec but lacks values, skipping\n", resID);
// add dummy value, or trimming leading/trailing zeroes later will fail
vector.push(0);
@@ -5426,8 +5661,22 @@
}
uint32_t* data = (uint32_t*)*outData;
*data++ = htodl(IDMAP_MAGIC);
- *data++ = htodl(originalCrc);
+ *data++ = htodl(targetCrc);
*data++ = htodl(overlayCrc);
+ const char* paths[] = { targetPath, overlayPath };
+ for (int j = 0; j < 2; ++j) {
+ char* p = (char*)data;
+ const char* path = paths[j];
+ const size_t I = strlen(path);
+ if (I > 255) {
+ ALOGV("path exceeds expected 255 characters: %s\n", path);
+ return UNKNOWN_ERROR;
+ }
+ for (size_t i = 0; i < 256; ++i) {
+ *p++ = i < I ? path[i] : '\0';
+ }
+ data += 256 / sizeof(uint32_t);
+ }
const size_t mapSize = map.size();
*data++ = htodl(mapSize);
size_t offset = mapSize;
@@ -5442,6 +5691,10 @@
offset += N;
}
}
+ if (offset == mapSize) {
+ ALOGW("idmap: no resources in overlay package present in base package\n");
+ return UNKNOWN_ERROR;
+ }
for (size_t i = 0; i < mapSize; ++i) {
const Vector<uint32_t>& vector = map.itemAt(i);
const size_t N = vector.size();
@@ -5463,14 +5716,25 @@
}
bool ResTable::getIdmapInfo(const void* idmap, size_t sizeBytes,
- uint32_t* pOriginalCrc, uint32_t* pOverlayCrc)
+ uint32_t* pTargetCrc, uint32_t* pOverlayCrc,
+ String8* pTargetPath, String8* pOverlayPath)
{
const uint32_t* map = (const uint32_t*)idmap;
if (!assertIdmapHeader(map, sizeBytes)) {
return false;
}
- *pOriginalCrc = map[1];
- *pOverlayCrc = map[2];
+ if (pTargetCrc) {
+ *pTargetCrc = map[1];
+ }
+ if (pOverlayCrc) {
+ *pOverlayCrc = map[2];
+ }
+ if (pTargetPath) {
+ pTargetPath->setTo(reinterpret_cast<const char*>(map + 3));
+ }
+ if (pOverlayPath) {
+ pOverlayPath->setTo(reinterpret_cast<const char*>(map + 3 + 256 / sizeof(uint32_t)));
+ }
return true;
}
@@ -5601,9 +5865,9 @@
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]);
+ char localeStr[RESTABLE_MAX_LOCALE_LEN];
+ mParams.getBcp47Locale(localeStr);
+ printf("mParams=%s,\n" localeStr);
#endif
size_t pgCount = mPackageGroups.size();
printf("Package Groups (%d)\n", (int)pgCount);
@@ -5732,7 +5996,7 @@
continue;
}
- uint16_t esize = dtohs(ent->size);
+ uintptr_t esize = dtohs(ent->size);
if ((esize&0x3) != 0) {
printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void*)esize);
continue;
diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk
index 3c55375..977ba80 100644
--- a/libs/androidfw/tests/Android.mk
+++ b/libs/androidfw/tests/Android.mk
@@ -5,7 +5,8 @@
# Build the unit tests.
test_src_files := \
ObbFile_test.cpp \
- ZipUtils_test.cpp
+ ZipUtils_test.cpp \
+ ResourceTypes_test.cpp
shared_libraries := \
libandroidfw \
diff --git a/libs/androidfw/tests/ResourceTypes_test.cpp b/libs/androidfw/tests/ResourceTypes_test.cpp
new file mode 100644
index 0000000..4888b4a
--- /dev/null
+++ b/libs/androidfw/tests/ResourceTypes_test.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <androidfw/ResourceTypes.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include <gtest/gtest.h>
+namespace android {
+
+TEST(ResourceTypesTest, ResourceConfig_packAndUnpack2LetterLanguage) {
+ ResTable_config config;
+ config.packLanguage("en");
+
+ EXPECT_EQ('e', config.language[0]);
+ EXPECT_EQ('n', config.language[1]);
+
+ char out[4] = { 1, 1, 1, 1};
+ config.unpackLanguage(out);
+ EXPECT_EQ('e', out[0]);
+ EXPECT_EQ('n', out[1]);
+ EXPECT_EQ(0, out[2]);
+ EXPECT_EQ(0, out[3]);
+
+ memset(out, 1, sizeof(out));
+ config.locale = 0;
+ config.unpackLanguage(out);
+ EXPECT_EQ(0, out[0]);
+ EXPECT_EQ(0, out[1]);
+ EXPECT_EQ(0, out[2]);
+ EXPECT_EQ(0, out[3]);
+}
+
+TEST(ResourceTypesTest, ResourceConfig_packAndUnpack2LetterRegion) {
+ ResTable_config config;
+ config.packRegion("US");
+
+ EXPECT_EQ('U', config.country[0]);
+ EXPECT_EQ('S', config.country[1]);
+
+ char out[4] = { 1, 1, 1, 1};
+ config.unpackRegion(out);
+ EXPECT_EQ('U', out[0]);
+ EXPECT_EQ('S', out[1]);
+ EXPECT_EQ(0, out[2]);
+ EXPECT_EQ(0, out[3]);
+}
+
+TEST(ResourceTypesTest, ResourceConfig_packAndUnpack3LetterLanguage) {
+ ResTable_config config;
+ config.packLanguage("eng");
+
+ // 1-00110-01 101-00100
+ EXPECT_EQ(0x99, config.language[0]);
+ EXPECT_EQ(0xa4, config.language[1]);
+
+ char out[4] = { 1, 1, 1, 1};
+ config.unpackLanguage(out);
+ EXPECT_EQ('e', out[0]);
+ EXPECT_EQ('n', out[1]);
+ EXPECT_EQ('g', out[2]);
+ EXPECT_EQ(0, out[3]);
+}
+
+TEST(ResourceTypesTest, ResourceConfig_packAndUnpack3LetterRegion) {
+ ResTable_config config;
+ config.packRegion("419");
+
+ char out[4] = { 1, 1, 1, 1};
+ config.unpackRegion(out);
+
+ EXPECT_EQ('4', out[0]);
+ EXPECT_EQ('1', out[1]);
+ EXPECT_EQ('9', out[2]);
+}
+
+/* static */ void fillIn(const char* lang, const char* country,
+ const char* script, const char* variant, ResTable_config* out) {
+ memset(out, 0, sizeof(ResTable_config));
+ if (lang != NULL) {
+ out->packLanguage(lang);
+ }
+
+ if (country != NULL) {
+ out->packRegion(country);
+ }
+
+ if (script != NULL) {
+ memcpy(out->localeScript, script, 4);
+ }
+
+ if (variant != NULL) {
+ memcpy(out->localeVariant, variant, strlen(variant));
+ }
+}
+
+TEST(ResourceTypesTest, IsMoreSpecificThan) {
+ ResTable_config l;
+ ResTable_config r;
+
+ fillIn("en", NULL, NULL, NULL, &l);
+ fillIn(NULL, NULL, NULL, NULL, &r);
+
+ EXPECT_TRUE(l.isMoreSpecificThan(r));
+ EXPECT_FALSE(r.isMoreSpecificThan(l));
+
+ fillIn("eng", NULL, NULL, NULL, &l);
+ EXPECT_TRUE(l.isMoreSpecificThan(r));
+ EXPECT_FALSE(r.isMoreSpecificThan(l));
+
+ fillIn("eng", "419", NULL, NULL, &r);
+ EXPECT_FALSE(l.isMoreSpecificThan(r));
+ EXPECT_TRUE(r.isMoreSpecificThan(l));
+
+ fillIn("en", NULL, NULL, NULL, &l);
+ fillIn("en", "US", NULL, NULL, &r);
+ EXPECT_FALSE(l.isMoreSpecificThan(r));
+ EXPECT_TRUE(r.isMoreSpecificThan(l));
+
+ fillIn("en", "US", NULL, NULL, &l);
+ fillIn("en", "US", "Latn", NULL, &r);
+ EXPECT_FALSE(l.isMoreSpecificThan(r));
+ EXPECT_TRUE(r.isMoreSpecificThan(l));
+
+ fillIn("en", "US", NULL, NULL, &l);
+ fillIn("en", "US", NULL, "POSIX", &r);
+ EXPECT_FALSE(l.isMoreSpecificThan(r));
+ EXPECT_TRUE(r.isMoreSpecificThan(l));
+
+ fillIn("en", "US", "Latn", NULL, &l);
+ fillIn("en", "US", NULL, "POSIX", &r);
+ EXPECT_FALSE(l.isMoreSpecificThan(r));
+ EXPECT_TRUE(r.isMoreSpecificThan(l));
+}
+
+TEST(ResourceTypesTest, setLocale) {
+ ResTable_config test;
+ test.setBcp47Locale("en-US");
+ EXPECT_EQ('e', test.language[0]);
+ EXPECT_EQ('n', test.language[1]);
+ EXPECT_EQ('U', test.country[0]);
+ EXPECT_EQ('S', test.country[1]);
+ EXPECT_EQ(0, test.localeScript[0]);
+ EXPECT_EQ(0, test.localeVariant[0]);
+
+ test.setBcp47Locale("eng-419");
+ char out[4] = { 1, 1, 1, 1};
+ test.unpackLanguage(out);
+ EXPECT_EQ('e', out[0]);
+ EXPECT_EQ('n', out[1]);
+ EXPECT_EQ('g', out[2]);
+ EXPECT_EQ(0, out[3]);
+ memset(out, 1, 4);
+ test.unpackRegion(out);
+ EXPECT_EQ('4', out[0]);
+ EXPECT_EQ('1', out[1]);
+ EXPECT_EQ('9', out[2]);
+
+
+ test.setBcp47Locale("en-Latn-419");
+ memset(out, 1, 4);
+ EXPECT_EQ('e', test.language[0]);
+ EXPECT_EQ('n', test.language[1]);
+
+ EXPECT_EQ(0, memcmp("Latn", test.localeScript, 4));
+ test.unpackRegion(out);
+ EXPECT_EQ('4', out[0]);
+ EXPECT_EQ('1', out[1]);
+ EXPECT_EQ('9', out[2]);
+}
+
+} // namespace android.
diff --git a/libs/hwui/utils/TinyHashMap.h b/libs/hwui/utils/TinyHashMap.h
index 8855140..4ff9a42 100644
--- a/libs/hwui/utils/TinyHashMap.h
+++ b/libs/hwui/utils/TinyHashMap.h
@@ -24,8 +24,6 @@
/**
* A very simple hash map that doesn't allow duplicate keys, overwriting the older entry.
- *
- * Currently, expects simple keys that are handled by hash_t()
*/
template <typename TKey, typename TValue>
class TinyHashMap {
@@ -36,7 +34,7 @@
* Puts an entry in the hash, removing any existing entry with the same key
*/
void put(TKey key, TValue value) {
- hash_t hash = hash_t(key);
+ hash_t hash = android::hash_type(key);
ssize_t index = mTable.find(-1, hash, key);
if (index != -1) {
@@ -51,7 +49,7 @@
* Return true if key is in the map, in which case stores the value in the output ref
*/
bool get(TKey key, TValue& outValue) {
- hash_t hash = hash_t(key);
+ hash_t hash = android::hash_type(key);
ssize_t index = mTable.find(-1, hash, key);
if (index == -1) {
return false;
diff --git a/media/jni/android_media_MediaMuxer.cpp b/media/jni/android_media_MediaMuxer.cpp
index 2c16a05..3561b06 100644
--- a/media/jni/android_media_MediaMuxer.cpp
+++ b/media/jni/android_media_MediaMuxer.cpp
@@ -132,7 +132,7 @@
}
// Constructor counterpart.
-static jint android_media_MediaMuxer_native_setup(
+static jlong android_media_MediaMuxer_native_setup(
JNIEnv *env, jclass clazz, jobject fileDescriptor,
jint format) {
int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
@@ -142,7 +142,7 @@
static_cast<MediaMuxer::OutputFormat>(format);
sp<MediaMuxer> muxer = new MediaMuxer(fd, fileFormat);
muxer->incStrong(clazz);
- return int(muxer.get());
+ return reinterpret_cast<jlong>(muxer.get());
}
static void android_media_MediaMuxer_setOrientationHint(
diff --git a/opengl/java/com/google/android/gles_jni/EGLConfigImpl.java b/opengl/java/com/google/android/gles_jni/EGLConfigImpl.java
index c2f4400..1902a40 100644
--- a/opengl/java/com/google/android/gles_jni/EGLConfigImpl.java
+++ b/opengl/java/com/google/android/gles_jni/EGLConfigImpl.java
@@ -19,13 +19,13 @@
import javax.microedition.khronos.egl.*;
public class EGLConfigImpl extends EGLConfig {
- private int mEGLConfig;
+ private long mEGLConfig;
- EGLConfigImpl(int config) {
+ EGLConfigImpl(long config) {
mEGLConfig = config;
}
- int get() {
+ long get() {
return mEGLConfig;
}
}
diff --git a/opengl/java/com/google/android/gles_jni/EGLContextImpl.java b/opengl/java/com/google/android/gles_jni/EGLContextImpl.java
index cd36099..47369ac 100644
--- a/opengl/java/com/google/android/gles_jni/EGLContextImpl.java
+++ b/opengl/java/com/google/android/gles_jni/EGLContextImpl.java
@@ -21,13 +21,13 @@
public class EGLContextImpl extends EGLContext {
private GLImpl mGLContext;
- int mEGLContext;
-
- public EGLContextImpl(int ctx) {
+ long mEGLContext;
+
+ public EGLContextImpl(long ctx) {
mEGLContext = ctx;
mGLContext = new GLImpl();
}
-
+
@Override
public GL getGL() {
return mGLContext;
@@ -45,6 +45,12 @@
@Override
public int hashCode() {
- return mEGLContext;
+ /*
+ * Based on the algorithm suggested in
+ * http://developer.android.com/reference/java/lang/Object.html
+ */
+ int result = 17;
+ result = 31 * result + (int) (mEGLContext ^ (mEGLContext >>> 32));
+ return result;
}
}
diff --git a/opengl/java/com/google/android/gles_jni/EGLDisplayImpl.java b/opengl/java/com/google/android/gles_jni/EGLDisplayImpl.java
index e6c9817..9b932fc 100644
--- a/opengl/java/com/google/android/gles_jni/EGLDisplayImpl.java
+++ b/opengl/java/com/google/android/gles_jni/EGLDisplayImpl.java
@@ -19,9 +19,9 @@
import javax.microedition.khronos.egl.*;
public class EGLDisplayImpl extends EGLDisplay {
- int mEGLDisplay;
+ long mEGLDisplay;
- public EGLDisplayImpl(int dpy) {
+ public EGLDisplayImpl(long dpy) {
mEGLDisplay = dpy;
}
@@ -38,6 +38,12 @@
@Override
public int hashCode() {
- return mEGLDisplay;
+ /*
+ * Based on the algorithm suggested in
+ * http://developer.android.com/reference/java/lang/Object.html
+ */
+ int result = 17;
+ result = 31 * result + (int) (mEGLDisplay ^ (mEGLDisplay >>> 32));
+ return result;
}
}
diff --git a/opengl/java/com/google/android/gles_jni/EGLImpl.java b/opengl/java/com/google/android/gles_jni/EGLImpl.java
index 64a54c2..41fb072 100644
--- a/opengl/java/com/google/android/gles_jni/EGLImpl.java
+++ b/opengl/java/com/google/android/gles_jni/EGLImpl.java
@@ -51,7 +51,7 @@
public static native int getInitCount(EGLDisplay display);
public EGLContext eglCreateContext(EGLDisplay display, EGLConfig config, EGLContext share_context, int[] attrib_list) {
- int eglContextId = _eglCreateContext(display, config, share_context, attrib_list);
+ long eglContextId = _eglCreateContext(display, config, share_context, attrib_list);
if (eglContextId == 0) {
return EGL10.EGL_NO_CONTEXT;
}
@@ -59,7 +59,7 @@
}
public EGLSurface eglCreatePbufferSurface(EGLDisplay display, EGLConfig config, int[] attrib_list) {
- int eglSurfaceId = _eglCreatePbufferSurface(display, config, attrib_list);
+ long eglSurfaceId = _eglCreatePbufferSurface(display, config, attrib_list);
if (eglSurfaceId == 0) {
return EGL10.EGL_NO_SURFACE;
}
@@ -87,7 +87,7 @@
sur = (Surface) native_window;
}
- int eglSurfaceId;
+ long eglSurfaceId;
if (sur != null) {
eglSurfaceId = _eglCreateWindowSurface(display, config, sur, attrib_list);
} else if (native_window instanceof SurfaceTexture) {
@@ -106,7 +106,7 @@
}
public synchronized EGLDisplay eglGetDisplay(Object native_display) {
- int value = _eglGetDisplay(native_display);
+ long value = _eglGetDisplay(native_display);
if (value == 0) {
return EGL10.EGL_NO_DISPLAY;
}
@@ -116,7 +116,7 @@
}
public synchronized EGLContext eglGetCurrentContext() {
- int value = _eglGetCurrentContext();
+ long value = _eglGetCurrentContext();
if (value == 0) {
return EGL10.EGL_NO_CONTEXT;
}
@@ -126,7 +126,7 @@
}
public synchronized EGLDisplay eglGetCurrentDisplay() {
- int value = _eglGetCurrentDisplay();
+ long value = _eglGetCurrentDisplay();
if (value == 0) {
return EGL10.EGL_NO_DISPLAY;
}
@@ -136,7 +136,7 @@
}
public synchronized EGLSurface eglGetCurrentSurface(int readdraw) {
- int value = _eglGetCurrentSurface(readdraw);
+ long value = _eglGetCurrentSurface(readdraw);
if (value == 0) {
return EGL10.EGL_NO_SURFACE;
}
@@ -145,15 +145,15 @@
return mSurface;
}
- private native int _eglCreateContext(EGLDisplay display, EGLConfig config, EGLContext share_context, int[] attrib_list);
- private native int _eglCreatePbufferSurface(EGLDisplay display, EGLConfig config, int[] attrib_list);
+ private native long _eglCreateContext(EGLDisplay display, EGLConfig config, EGLContext share_context, int[] attrib_list);
+ private native long _eglCreatePbufferSurface(EGLDisplay display, EGLConfig config, int[] attrib_list);
private native void _eglCreatePixmapSurface(EGLSurface sur, EGLDisplay display, EGLConfig config, Object native_pixmap, int[] attrib_list);
- private native int _eglCreateWindowSurface(EGLDisplay display, EGLConfig config, Object native_window, int[] attrib_list);
- private native int _eglCreateWindowSurfaceTexture(EGLDisplay display, EGLConfig config, Object native_window, int[] attrib_list);
- private native int _eglGetDisplay(Object native_display);
- private native int _eglGetCurrentContext();
- private native int _eglGetCurrentDisplay();
- private native int _eglGetCurrentSurface(int readdraw);
+ private native long _eglCreateWindowSurface(EGLDisplay display, EGLConfig config, Object native_window, int[] attrib_list);
+ private native long _eglCreateWindowSurfaceTexture(EGLDisplay display, EGLConfig config, Object native_window, int[] attrib_list);
+ private native long _eglGetDisplay(Object native_display);
+ private native long _eglGetCurrentContext();
+ private native long _eglGetCurrentDisplay();
+ private native long _eglGetCurrentSurface(int readdraw);
native private static void _nativeClassInit();
static { _nativeClassInit(); }
diff --git a/opengl/java/com/google/android/gles_jni/EGLSurfaceImpl.java b/opengl/java/com/google/android/gles_jni/EGLSurfaceImpl.java
index e7f15dc..7a3ed24 100644
--- a/opengl/java/com/google/android/gles_jni/EGLSurfaceImpl.java
+++ b/opengl/java/com/google/android/gles_jni/EGLSurfaceImpl.java
@@ -19,13 +19,13 @@
import javax.microedition.khronos.egl.*;
public class EGLSurfaceImpl extends EGLSurface {
- int mEGLSurface;
- private int mNativePixelRef;
+ long mEGLSurface;
+ private long mNativePixelRef;
public EGLSurfaceImpl() {
mEGLSurface = 0;
mNativePixelRef = 0;
}
- public EGLSurfaceImpl(int surface) {
+ public EGLSurfaceImpl(long surface) {
mEGLSurface = surface;
mNativePixelRef = 0;
}
@@ -43,6 +43,12 @@
@Override
public int hashCode() {
- return mEGLSurface;
+ /*
+ * Based on the algorithm suggested in
+ * http://developer.android.com/reference/java/lang/Object.html
+ */
+ int result = 17;
+ result = 31 * result + (int) (mEGLSurface ^ (mEGLSurface >>> 32));
+ return result;
}
}
diff --git a/rs/java/android/renderscript/Allocation.java b/rs/java/android/renderscript/Allocation.java
index 67d94f9..c2bab91 100644
--- a/rs/java/android/renderscript/Allocation.java
+++ b/rs/java/android/renderscript/Allocation.java
@@ -81,6 +81,65 @@
new HashMap<Long, Allocation>();
OnBufferAvailableListener mBufferNotifier;
+ private Element.DataType validateObjectIsPrimitiveArray(Object d, boolean checkType) {
+ final Class c = d.getClass();
+ if (!c.isArray()) {
+ throw new RSIllegalArgumentException("Object passed is not an array of primitives.");
+ }
+ final Class cmp = c.getComponentType();
+ if (!cmp.isPrimitive()) {
+ throw new RSIllegalArgumentException("Object passed is not an Array of primitives.");
+ }
+
+ if (cmp == Long.TYPE) {
+ if (checkType) {
+ validateIsInt64();
+ return mType.mElement.mType;
+ }
+ return Element.DataType.SIGNED_64;
+ }
+
+ if (cmp == Integer.TYPE) {
+ if (checkType) {
+ validateIsInt32();
+ return mType.mElement.mType;
+ }
+ return Element.DataType.SIGNED_32;
+ }
+
+ if (cmp == Short.TYPE) {
+ if (checkType) {
+ validateIsInt16();
+ return mType.mElement.mType;
+ }
+ return Element.DataType.SIGNED_16;
+ }
+
+ if (cmp == Byte.TYPE) {
+ if (checkType) {
+ validateIsInt8();
+ return mType.mElement.mType;
+ }
+ return Element.DataType.SIGNED_8;
+ }
+
+ if (cmp == Float.TYPE) {
+ if (checkType) {
+ validateIsFloat32();
+ }
+ return Element.DataType.FLOAT_32;
+ }
+
+ if (cmp == Double.TYPE) {
+ if (checkType) {
+ validateIsFloat64();
+ }
+ return Element.DataType.FLOAT_64;
+ }
+ return null;
+ }
+
+
/**
* The usage of the Allocation. These signal to RenderScript where to place
* the Allocation in memory.
@@ -292,6 +351,15 @@
super.finalize();
}
+ private void validateIsInt64() {
+ if ((mType.mElement.mType == Element.DataType.SIGNED_64) ||
+ (mType.mElement.mType == Element.DataType.UNSIGNED_64)) {
+ return;
+ }
+ throw new RSIllegalArgumentException(
+ "64 bit integer source does not match allocation type " + mType.mElement.mType);
+ }
+
private void validateIsInt32() {
if ((mType.mElement.mType == Element.DataType.SIGNED_32) ||
(mType.mElement.mType == Element.DataType.UNSIGNED_32)) {
@@ -327,6 +395,14 @@
"32 bit float source does not match allocation type " + mType.mElement.mType);
}
+ private void validateIsFloat64() {
+ if (mType.mElement.mType == Element.DataType.FLOAT_64) {
+ return;
+ }
+ throw new RSIllegalArgumentException(
+ "64 bit float source does not match allocation type " + mType.mElement.mType);
+ }
+
private void validateIsObject() {
if ((mType.mElement.mType == Element.DataType.RS_ELEMENT) ||
(mType.mElement.mType == Element.DataType.RS_TYPE) ||
@@ -507,6 +583,34 @@
}
}
+ private void copyFromUnchecked(Object array, Element.DataType dt, int arrayLen) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyFromUnchecked");
+ mRS.validate();
+ if (mCurrentDimZ > 0) {
+ copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, array, dt, arrayLen);
+ } else if (mCurrentDimY > 0) {
+ copy2DRangeFromUnchecked(0, 0, mCurrentDimX, mCurrentDimY, array, dt, arrayLen);
+ } else {
+ copy1DRangeFromUnchecked(0, mCurrentCount, array, dt, arrayLen);
+ }
+ Trace.traceEnd(RenderScript.TRACE_TAG);
+ }
+
+ /**
+ * Copy into this Allocation from an array. This method does not guarantee
+ * that the Allocation is compatible with the input buffer; it copies memory
+ * without reinterpretation.
+ *
+ * @param array The source data array
+ * @hide
+ */
+ public void copyFromUnchecked(Object array) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyFromUnchecked");
+ copyFromUnchecked(array, validateObjectIsPrimitiveArray(array, false),
+ java.lang.reflect.Array.getLength(array));
+ Trace.traceEnd(RenderScript.TRACE_TAG);
+ }
+
/**
* Copy into this Allocation from an array. This method does not guarantee
* that the Allocation is compatible with the input buffer; it copies memory
@@ -515,16 +619,7 @@
* @param d the source data array
*/
public void copyFromUnchecked(int[] d) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copyFromUnchecked");
- mRS.validate();
- if (mCurrentDimZ > 0) {
- copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
- } else if (mCurrentDimY > 0) {
- copy2DRangeFromUnchecked(0, 0, mCurrentDimX, mCurrentDimY, d);
- } else {
- copy1DRangeFromUnchecked(0, mCurrentCount, d);
- }
- Trace.traceEnd(RenderScript.TRACE_TAG);
+ copyFromUnchecked(d, Element.DataType.SIGNED_32, d.length);
}
/**
@@ -535,16 +630,7 @@
* @param d the source data array
*/
public void copyFromUnchecked(short[] d) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copyFromUnchecked");
- mRS.validate();
- if (mCurrentDimZ > 0) {
- copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
- } else if (mCurrentDimY > 0) {
- copy2DRangeFromUnchecked(0, 0, mCurrentDimX, mCurrentDimY, d);
- } else {
- copy1DRangeFromUnchecked(0, mCurrentCount, d);
- }
- Trace.traceEnd(RenderScript.TRACE_TAG);
+ copyFromUnchecked(d, Element.DataType.SIGNED_16, d.length);
}
/**
@@ -555,16 +641,7 @@
* @param d the source data array
*/
public void copyFromUnchecked(byte[] d) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copyFromUnchecked");
- mRS.validate();
- if (mCurrentDimZ > 0) {
- copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
- } else if (mCurrentDimY > 0) {
- copy2DRangeFromUnchecked(0, 0, mCurrentDimX, mCurrentDimY, d);
- } else {
- copy1DRangeFromUnchecked(0, mCurrentCount, d);
- }
- Trace.traceEnd(RenderScript.TRACE_TAG);
+ copyFromUnchecked(d, Element.DataType.SIGNED_8, d.length);
}
/**
@@ -575,37 +652,36 @@
* @param d the source data array
*/
public void copyFromUnchecked(float[] d) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copyFromUnchecked");
- mRS.validate();
- if (mCurrentDimZ > 0) {
- copy3DRangeFromUnchecked(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
- } else if (mCurrentDimY > 0) {
- copy2DRangeFromUnchecked(0, 0, mCurrentDimX, mCurrentDimY, d);
- } else {
- copy1DRangeFromUnchecked(0, mCurrentCount, d);
- }
- Trace.traceEnd(RenderScript.TRACE_TAG);
+ copyFromUnchecked(d, Element.DataType.FLOAT_32, d.length);
}
/**
* Copy into this Allocation from an array. This variant is type checked
* and will generate exceptions if the Allocation's {@link
+ * android.renderscript.Element} does not match the array's
+ * primitive type.
+ *
+ * @param d the source data array
+ * @hide
+ */
+ public void copyFrom(Object array) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
+ copyFromUnchecked(array, validateObjectIsPrimitiveArray(array, true),
+ java.lang.reflect.Array.getLength(array));
+ Trace.traceEnd(RenderScript.TRACE_TAG);
+ }
+
+ /**
+ * Copy into this Allocation from an array. This variant is type checked
+ * and will generate exceptions if the Allocation's {@link
* android.renderscript.Element} is not a 32 bit integer type.
*
* @param d the source data array
*/
public void copyFrom(int[] d) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
- mRS.validate();
- if (mCurrentDimZ > 0) {
- copy3DRangeFrom(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
- } else if (mCurrentDimY > 0) {
- copy2DRangeFrom(0, 0, mCurrentDimX, mCurrentDimY, d);
- } else {
- copy1DRangeFrom(0, mCurrentCount, d);
- }
- Trace.traceEnd(RenderScript.TRACE_TAG);
+ validateIsInt32();
+ copyFromUnchecked(d, Element.DataType.SIGNED_32, d.length);
}
/**
@@ -616,16 +692,8 @@
* @param d the source data array
*/
public void copyFrom(short[] d) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
- mRS.validate();
- if (mCurrentDimZ > 0) {
- copy3DRangeFrom(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
- } else if (mCurrentDimY > 0) {
- copy2DRangeFrom(0, 0, mCurrentDimX, mCurrentDimY, d);
- } else {
- copy1DRangeFrom(0, mCurrentCount, d);
- }
- Trace.traceEnd(RenderScript.TRACE_TAG);
+ validateIsInt16();
+ copyFromUnchecked(d, Element.DataType.SIGNED_16, d.length);
}
/**
@@ -636,16 +704,8 @@
* @param d the source data array
*/
public void copyFrom(byte[] d) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
- mRS.validate();
- if (mCurrentDimZ > 0) {
- copy3DRangeFrom(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
- } else if (mCurrentDimY > 0) {
- copy2DRangeFrom(0, 0, mCurrentDimX, mCurrentDimY, d);
- } else {
- copy1DRangeFrom(0, mCurrentCount, d);
- }
- Trace.traceEnd(RenderScript.TRACE_TAG);
+ validateIsInt8();
+ copyFromUnchecked(d, Element.DataType.SIGNED_8, d.length);
}
/**
@@ -656,16 +716,8 @@
* @param d the source data array
*/
public void copyFrom(float[] d) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copyFrom");
- mRS.validate();
- if (mCurrentDimZ > 0) {
- copy3DRangeFrom(0, 0, 0, mCurrentDimX, mCurrentDimY, mCurrentDimZ, d);
- } else if (mCurrentDimY > 0) {
- copy2DRangeFrom(0, 0, mCurrentDimX, mCurrentDimY, d);
- } else {
- copy1DRangeFrom(0, mCurrentCount, d);
- }
- Trace.traceEnd(RenderScript.TRACE_TAG);
+ validateIsFloat32();
+ copyFromUnchecked(d, Element.DataType.FLOAT_32, d.length);
}
/**
@@ -794,6 +846,30 @@
mRS.nAllocationGenerateMipmaps(getID(mRS));
}
+ private void copy1DRangeFromUnchecked(int off, int count, Object array,
+ Element.DataType dt, int arrayLen) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeFromUnchecked");
+ final int dataSize = mType.mElement.getBytesSize() * count;
+ data1DChecks(off, count, arrayLen * dt.mSize, dataSize);
+ mRS.nAllocationData1D(getIDSafe(), off, mSelectedLOD, count, array, dataSize, dt);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
+ }
+
+ /**
+ * Copy an array into part of this Allocation. This method does not
+ * guarantee that the Allocation is compatible with the input buffer.
+ *
+ * @param off The offset of the first element to be copied.
+ * @param count The number of elements to be copied.
+ * @param array The source data array
+ * @hide
+ */
+ public void copy1DRangeFromUnchecked(int off, int count, Object array) {
+ copy1DRangeFromUnchecked(off, count, array,
+ validateObjectIsPrimitiveArray(array, false),
+ java.lang.reflect.Array.getLength(array));
+ }
+
/**
* Copy an array into part of this Allocation. This method does not
* guarantee that the Allocation is compatible with the input buffer.
@@ -803,11 +879,7 @@
* @param d the source data array
*/
public void copy1DRangeFromUnchecked(int off, int count, int[] d) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeFromUnchecked");
- int dataSize = mType.mElement.getBytesSize() * count;
- data1DChecks(off, count, d.length * 4, dataSize);
- mRS.nAllocationData1D(getIDSafe(), off, mSelectedLOD, count, d, dataSize, Element.DataType.SIGNED_32);
- Trace.traceEnd(RenderScript.TRACE_TAG);
+ copy1DRangeFromUnchecked(off, count, (Object)d, Element.DataType.SIGNED_32, d.length);
}
/**
@@ -819,11 +891,7 @@
* @param d the source data array
*/
public void copy1DRangeFromUnchecked(int off, int count, short[] d) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeFromUnchecked");
- int dataSize = mType.mElement.getBytesSize() * count;
- data1DChecks(off, count, d.length * 2, dataSize);
- mRS.nAllocationData1D(getIDSafe(), off, mSelectedLOD, count, d, dataSize, Element.DataType.SIGNED_16);
- Trace.traceEnd(RenderScript.TRACE_TAG);
+ copy1DRangeFromUnchecked(off, count, (Object)d, Element.DataType.SIGNED_16, d.length);
}
/**
@@ -835,11 +903,7 @@
* @param d the source data array
*/
public void copy1DRangeFromUnchecked(int off, int count, byte[] d) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeFromUnchecked");
- int dataSize = mType.mElement.getBytesSize() * count;
- data1DChecks(off, count, d.length, dataSize);
- mRS.nAllocationData1D(getIDSafe(), off, mSelectedLOD, count, d, dataSize, Element.DataType.SIGNED_8);
- Trace.traceEnd(RenderScript.TRACE_TAG);
+ copy1DRangeFromUnchecked(off, count, (Object)d, Element.DataType.SIGNED_8, d.length);
}
/**
@@ -851,11 +915,24 @@
* @param d the source data array
*/
public void copy1DRangeFromUnchecked(int off, int count, float[] d) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeFromUnchecked");
- int dataSize = mType.mElement.getBytesSize() * count;
- data1DChecks(off, count, d.length * 4, dataSize);
- mRS.nAllocationData1D(getIDSafe(), off, mSelectedLOD, count, d, dataSize, Element.DataType.FLOAT_32);
- Trace.traceEnd(RenderScript.TRACE_TAG);
+ copy1DRangeFromUnchecked(off, count, (Object)d, Element.DataType.FLOAT_32, d.length);
+ }
+
+
+ /**
+ * Copy an array into part of this Allocation. This variant is type checked
+ * and will generate exceptions if the Allocation type does not
+ * match the component type of the array passed in.
+ *
+ * @param off The offset of the first element to be copied.
+ * @param count The number of elements to be copied.
+ * @param array The source data array.
+ * @hide
+ */
+ public void copy1DRangeFrom(int off, int count, Object array) {
+ copy1DRangeFromUnchecked(off, count, array,
+ validateObjectIsPrimitiveArray(array, true),
+ java.lang.reflect.Array.getLength(array));
}
/**
@@ -868,10 +945,8 @@
* @param d the source data array
*/
public void copy1DRangeFrom(int off, int count, int[] d) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeFrom");
validateIsInt32();
- copy1DRangeFromUnchecked(off, count, d);
- Trace.traceEnd(RenderScript.TRACE_TAG);
+ copy1DRangeFromUnchecked(off, count, d, Element.DataType.SIGNED_32, d.length);
}
/**
@@ -884,10 +959,8 @@
* @param d the source data array
*/
public void copy1DRangeFrom(int off, int count, short[] d) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeFrom");
validateIsInt16();
- copy1DRangeFromUnchecked(off, count, d);
- Trace.traceEnd(RenderScript.TRACE_TAG);
+ copy1DRangeFromUnchecked(off, count, d, Element.DataType.SIGNED_16, d.length);
}
/**
@@ -900,10 +973,8 @@
* @param d the source data array
*/
public void copy1DRangeFrom(int off, int count, byte[] d) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeFrom");
validateIsInt8();
- copy1DRangeFromUnchecked(off, count, d);
- Trace.traceEnd(RenderScript.TRACE_TAG);
+ copy1DRangeFromUnchecked(off, count, d, Element.DataType.SIGNED_8, d.length);
}
/**
@@ -916,11 +987,10 @@
* @param d the source data array.
*/
public void copy1DRangeFrom(int off, int count, float[] d) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copy1DRangeFrom");
validateIsFloat32();
- copy1DRangeFromUnchecked(off, count, d);
- Trace.traceEnd(RenderScript.TRACE_TAG);
+ copy1DRangeFromUnchecked(off, count, d, Element.DataType.FLOAT_32, d.length);
}
+
/**
* Copy part of an Allocation into this Allocation.
*
@@ -955,39 +1025,32 @@
}
}
- void copy2DRangeFromUnchecked(int xoff, int yoff, int w, int h, byte[] data) {
+ void copy2DRangeFromUnchecked(int xoff, int yoff, int w, int h, Object array,
+ Element.DataType dt, int arrayLen) {
Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFromUnchecked");
mRS.validate();
validate2DRange(xoff, yoff, w, h);
- mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID,
- w, h, data, data.length, Element.DataType.SIGNED_8);
+ mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, w, h,
+ array, arrayLen * dt.mSize, dt);
Trace.traceEnd(RenderScript.TRACE_TAG);
}
- void copy2DRangeFromUnchecked(int xoff, int yoff, int w, int h, short[] data) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFromUnchecked");
- mRS.validate();
- validate2DRange(xoff, yoff, w, h);
- mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID,
- w, h, data, data.length * 2, Element.DataType.SIGNED_16);
- Trace.traceEnd(RenderScript.TRACE_TAG);
- }
-
- void copy2DRangeFromUnchecked(int xoff, int yoff, int w, int h, int[] data) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFromUnchecked");
- mRS.validate();
- validate2DRange(xoff, yoff, w, h);
- mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID,
- w, h, data, data.length * 4, Element.DataType.SIGNED_32);
- Trace.traceEnd(RenderScript.TRACE_TAG);
- }
-
- void copy2DRangeFromUnchecked(int xoff, int yoff, int w, int h, float[] data) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFromUnchecked");
- mRS.validate();
- validate2DRange(xoff, yoff, w, h);
- mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID,
- w, h, data, data.length * 4, Element.DataType.FLOAT_32);
+ /**
+ * Copy from an array into a rectangular region in this Allocation. The
+ * array is assumed to be tightly packed.
+ *
+ * @param xoff X offset of the region to update in this Allocation
+ * @param yoff Y offset of the region to update in this Allocation
+ * @param w Width of the region to update
+ * @param h Height of the region to update
+ * @param data to be placed into the Allocation
+ * @hide
+ */
+ public void copy2DRangeFrom(int xoff, int yoff, int w, int h, Object array) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFrom");
+ copy2DRangeFromUnchecked(xoff, yoff, w, h, array,
+ validateObjectIsPrimitiveArray(array, true),
+ java.lang.reflect.Array.getLength(array));
Trace.traceEnd(RenderScript.TRACE_TAG);
}
@@ -1002,10 +1065,9 @@
* @param data to be placed into the Allocation
*/
public void copy2DRangeFrom(int xoff, int yoff, int w, int h, byte[] data) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFrom");
validateIsInt8();
- copy2DRangeFromUnchecked(xoff, yoff, w, h, data);
- Trace.traceEnd(RenderScript.TRACE_TAG);
+ copy2DRangeFromUnchecked(xoff, yoff, w, h, data,
+ Element.DataType.SIGNED_8, data.length);
}
/**
@@ -1019,10 +1081,9 @@
* @param data to be placed into the Allocation
*/
public void copy2DRangeFrom(int xoff, int yoff, int w, int h, short[] data) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFrom");
validateIsInt16();
- copy2DRangeFromUnchecked(xoff, yoff, w, h, data);
- Trace.traceEnd(RenderScript.TRACE_TAG);
+ copy2DRangeFromUnchecked(xoff, yoff, w, h, data,
+ Element.DataType.SIGNED_16, data.length);
}
/**
@@ -1036,10 +1097,9 @@
* @param data to be placed into the Allocation
*/
public void copy2DRangeFrom(int xoff, int yoff, int w, int h, int[] data) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFrom");
validateIsInt32();
- copy2DRangeFromUnchecked(xoff, yoff, w, h, data);
- Trace.traceEnd(RenderScript.TRACE_TAG);
+ copy2DRangeFromUnchecked(xoff, yoff, w, h, data,
+ Element.DataType.SIGNED_32, data.length);
}
/**
@@ -1053,10 +1113,9 @@
* @param data to be placed into the Allocation
*/
public void copy2DRangeFrom(int xoff, int yoff, int w, int h, float[] data) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copy2DRangeFrom");
validateIsFloat32();
- copy2DRangeFromUnchecked(xoff, yoff, w, h, data);
- Trace.traceEnd(RenderScript.TRACE_TAG);
+ copy2DRangeFromUnchecked(xoff, yoff, w, h, data,
+ Element.DataType.FLOAT_32, data.length);
}
/**
@@ -1129,49 +1188,18 @@
* @hide
*
*/
- void copy3DRangeFromUnchecked(int xoff, int yoff, int zoff, int w, int h, int d, byte[] data) {
+ private void copy3DRangeFromUnchecked(int xoff, int yoff, int zoff, int w, int h, int d,
+ Object array, Element.DataType dt, int arrayLen) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy3DRangeFromUnchecked");
mRS.validate();
validate3DRange(xoff, yoff, zoff, w, h, d);
- mRS.nAllocationData3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD,
- w, h, d, data, data.length, Element.DataType.SIGNED_8);
+ mRS.nAllocationData3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD, w, h, d,
+ array, arrayLen * dt.mSize, dt);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
* @hide
- *
- */
- void copy3DRangeFromUnchecked(int xoff, int yoff, int zoff, int w, int h, int d, short[] data) {
- mRS.validate();
- validate3DRange(xoff, yoff, zoff, w, h, d);
- mRS.nAllocationData3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD,
- w, h, d, data, data.length * 2, Element.DataType.SIGNED_16);
- }
-
- /**
- * @hide
- *
- */
- void copy3DRangeFromUnchecked(int xoff, int yoff, int zoff, int w, int h, int d, int[] data) {
- mRS.validate();
- validate3DRange(xoff, yoff, zoff, w, h, d);
- mRS.nAllocationData3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD,
- w, h, d, data, data.length * 4, Element.DataType.SIGNED_32);
- }
-
- /**
- * @hide
- *
- */
- void copy3DRangeFromUnchecked(int xoff, int yoff, int zoff, int w, int h, int d, float[] data) {
- mRS.validate();
- validate3DRange(xoff, yoff, zoff, w, h, d);
- mRS.nAllocationData3D(getIDSafe(), xoff, yoff, zoff, mSelectedLOD,
- w, h, d, data, data.length * 4, Element.DataType.FLOAT_32);
- }
-
-
- /**
- * @hide
* Copy a rectangular region from the array into the allocation.
* The array is assumed to be tightly packed.
*
@@ -1183,36 +1211,12 @@
* @param d Depth of the region to update
* @param data to be placed into the allocation
*/
- public void copy3DRangeFrom(int xoff, int yoff, int zoff, int w, int h, int d, byte[] data) {
- validateIsInt8();
- copy3DRangeFromUnchecked(xoff, yoff, zoff, w, h, d, data);
- }
-
- /**
- * @hide
- *
- */
- public void copy3DRangeFrom(int xoff, int yoff, int zoff, int w, int h, int d, short[] data) {
- validateIsInt16();
- copy3DRangeFromUnchecked(xoff, yoff, zoff, w, h, d, data);
- }
-
- /**
- * @hide
- *
- */
- public void copy3DRangeFrom(int xoff, int yoff, int zoff, int w, int h, int d, int[] data) {
- validateIsInt32();
- copy3DRangeFromUnchecked(xoff, yoff, zoff, w, h, d, data);
- }
-
- /**
- * @hide
- *
- */
- public void copy3DRangeFrom(int xoff, int yoff, int zoff, int w, int h, int d, float[] data) {
- validateIsFloat32();
- copy3DRangeFromUnchecked(xoff, yoff, zoff, w, h, d, data);
+ public void copy3DRangeFrom(int xoff, int yoff, int zoff, int w, int h, int d, Object array) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copy3DRangeFrom");
+ copy3DRangeFromUnchecked(xoff, yoff, zoff, w, h, d, array,
+ validateObjectIsPrimitiveArray(array, true),
+ java.lang.reflect.Array.getLength(array));
+ Trace.traceEnd(RenderScript.TRACE_TAG);
}
/**
@@ -1256,6 +1260,27 @@
Trace.traceEnd(RenderScript.TRACE_TAG);
}
+ private void copyTo(Object array, Element.DataType dt, int arrayLen) {
+ Trace.traceBegin(RenderScript.TRACE_TAG, "copyTo");
+ mRS.validate();
+ mRS.nAllocationRead(getID(mRS), array, dt);
+ Trace.traceEnd(RenderScript.TRACE_TAG);
+ }
+
+ /**
+ * Copy from the Allocation into an array. The array must be at
+ * least as large as the Allocation. The
+ * {@link android.renderscript.Element} must match the component
+ * type of the array passed in.
+ *
+ * @param array The array to be set from the Allocation.
+ * @hide
+ */
+ public void copyTo(Object array) {
+ copyTo(array, validateObjectIsPrimitiveArray(array, true),
+ java.lang.reflect.Array.getLength(array));
+ }
+
/**
* Copy from the Allocation into a byte array. The array must be at least
* as large as the Allocation. The allocation must be of an 8 bit integer
@@ -1264,11 +1289,8 @@
* @param d The array to be set from the Allocation.
*/
public void copyTo(byte[] d) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copyTo");
validateIsInt8();
- mRS.validate();
- mRS.nAllocationRead(getID(mRS), d, Element.DataType.SIGNED_8);
- Trace.traceEnd(RenderScript.TRACE_TAG);
+ copyTo(d, Element.DataType.SIGNED_8, d.length);
}
/**
@@ -1279,11 +1301,8 @@
* @param d The array to be set from the Allocation.
*/
public void copyTo(short[] d) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copyTo");
validateIsInt16();
- mRS.validate();
- mRS.nAllocationRead(getID(mRS), d, Element.DataType.SIGNED_16);
- Trace.traceEnd(RenderScript.TRACE_TAG);
+ copyTo(d, Element.DataType.SIGNED_16, d.length);
}
/**
@@ -1294,11 +1313,8 @@
* @param d The array to be set from the Allocation.
*/
public void copyTo(int[] d) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copyTo");
validateIsInt32();
- mRS.validate();
- mRS.nAllocationRead(getID(mRS), d, Element.DataType.SIGNED_32);
- Trace.traceEnd(RenderScript.TRACE_TAG);
+ copyTo(d, Element.DataType.SIGNED_32, d.length);
}
/**
@@ -1309,11 +1325,8 @@
* @param d The array to be set from the Allocation.
*/
public void copyTo(float[] d) {
- Trace.traceBegin(RenderScript.TRACE_TAG, "copyTo");
validateIsFloat32();
- mRS.validate();
- mRS.nAllocationRead(getID(mRS), d, Element.DataType.FLOAT_32);
- Trace.traceEnd(RenderScript.TRACE_TAG);
+ copyTo(d, Element.DataType.FLOAT_32, d.length);
}
/**
diff --git a/rs/java/android/renderscript/AllocationAdapter.java b/rs/java/android/renderscript/AllocationAdapter.java
index b77d087..fd20cae 100644
--- a/rs/java/android/renderscript/AllocationAdapter.java
+++ b/rs/java/android/renderscript/AllocationAdapter.java
@@ -224,7 +224,6 @@
}
static public AllocationAdapter create2D(RenderScript rs, Allocation a) {
- android.util.Log.e("rs", "create2d " + a);
rs.validate();
AllocationAdapter aa = new AllocationAdapter(0, rs, a);
aa.mConstrainedLOD = true;
diff --git a/rs/java/android/renderscript/BaseObj.java b/rs/java/android/renderscript/BaseObj.java
index 842aa23..b386dd7 100644
--- a/rs/java/android/renderscript/BaseObj.java
+++ b/rs/java/android/renderscript/BaseObj.java
@@ -122,7 +122,8 @@
// must include nObjDestroy in the critical section
ReentrantReadWriteLock.ReadLock rlock = mRS.mRWLock.readLock();
rlock.lock();
- if(mRS.isAlive()) {
+ // AllocationAdapters are BaseObjs with an ID of 0 but should not be passed to nObjDestroy
+ if(mRS.isAlive() && mID != 0) {
mRS.nObjDestroy(mID);
}
rlock.unlock();
diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java
index 8618764..b211c5a 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -103,6 +103,20 @@
static File mCacheDir;
+ // this should be a monotonically increasing ID
+ // used in conjunction with the API version of a device
+ static final long sMinorID = 1;
+
+ /**
+ * Returns an identifier that can be used to identify a particular
+ * minor version of RS.
+ *
+ * @hide
+ */
+ public static long getMinorID() {
+ return sMinorID;
+ }
+
/**
* Sets the directory to use as a persistent storage for the
* renderscript object file cache.
diff --git a/services/java/com/android/server/ConsumerIrService.java b/services/java/com/android/server/ConsumerIrService.java
index 783dff1..583f1bc 100644
--- a/services/java/com/android/server/ConsumerIrService.java
+++ b/services/java/com/android/server/ConsumerIrService.java
@@ -49,13 +49,13 @@
private static final int MAX_XMIT_TIME = 2000000; /* in microseconds */
- private static native int halOpen();
- private static native int halTransmit(int halObject, int carrierFrequency, int[] pattern);
- private static native int[] halGetCarrierFrequencies(int halObject);
+ private static native long halOpen();
+ private static native int halTransmit(long halObject, int carrierFrequency, int[] pattern);
+ private static native int[] halGetCarrierFrequencies(long halObject);
private final Context mContext;
private final PowerManager.WakeLock mWakeLock;
- private final int mHal;
+ private final long mNativeHal;
private final Object mHalLock = new Object();
ConsumerIrService(Context context) {
@@ -65,23 +65,23 @@
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
mWakeLock.setReferenceCounted(true);
- mHal = halOpen();
+ mNativeHal = halOpen();
if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CONSUMER_IR)) {
- if (mHal == 0) {
+ if (mNativeHal == 0) {
throw new RuntimeException("FEATURE_CONSUMER_IR present, but no IR HAL loaded!");
}
- } else if (mHal != 0) {
+ } else if (mNativeHal != 0) {
throw new RuntimeException("IR HAL present, but FEATURE_CONSUMER_IR is not set!");
}
}
@Override
public boolean hasIrEmitter() {
- return mHal != 0;
+ return mNativeHal != 0;
}
private void throwIfNoIrEmitter() {
- if (mHal == 0) {
+ if (mNativeHal == 0) {
throw new UnsupportedOperationException("IR emitter not available");
}
}
@@ -111,7 +111,7 @@
// Right now there is no mechanism to ensure fair queing of IR requests
synchronized (mHalLock) {
- int err = halTransmit(mHal, carrierFrequency, pattern);
+ int err = halTransmit(mNativeHal, carrierFrequency, pattern);
if (err < 0) {
Slog.e(TAG, "Error transmitting: " + err);
@@ -129,7 +129,7 @@
throwIfNoIrEmitter();
synchronized(mHalLock) {
- return halGetCarrierFrequencies(mHal);
+ return halGetCarrierFrequencies(mNativeHal);
}
}
}
diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java
index 699d79e..77f5182 100644
--- a/services/java/com/android/server/TelephonyRegistry.java
+++ b/services/java/com/android/server/TelephonyRegistry.java
@@ -37,6 +37,10 @@
import android.telephony.SignalStrength;
import android.telephony.CellInfo;
import android.telephony.TelephonyManager;
+import android.telephony.DisconnectCause;
+import android.telephony.PreciseCallState;
+import android.telephony.PreciseDataConnectionState;
+import android.telephony.PreciseDisconnectCause;
import android.text.TextUtils;
import android.util.Slog;
@@ -125,6 +129,17 @@
private List<CellInfo> mCellInfo = null;
+ private int mRingingCallState = PreciseCallState.PRECISE_CALL_STATE_IDLE;
+
+ private int mForegroundCallState = PreciseCallState.PRECISE_CALL_STATE_IDLE;
+
+ private int mBackgroundCallState = PreciseCallState.PRECISE_CALL_STATE_IDLE;
+
+ private PreciseCallState mPreciseCallState = new PreciseCallState();
+
+ private PreciseDataConnectionState mPreciseDataConnectionState =
+ new PreciseDataConnectionState();
+
static final int PHONE_STATE_PERMISSION_MASK =
PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR |
PhoneStateListener.LISTEN_CALL_STATE |
@@ -132,6 +147,10 @@
PhoneStateListener.LISTEN_DATA_CONNECTION_STATE |
PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR;
+ static final int PRECISE_PHONE_STATE_PERMISSION_MASK =
+ PhoneStateListener.LISTEN_PRECISE_CALL_STATE |
+ PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE;
+
private static final int MSG_USER_SWITCHED = 1;
private final Handler mHandler = new Handler() {
@@ -305,6 +324,21 @@
remove(r.binder);
}
}
+ if ((events & PhoneStateListener.LISTEN_PRECISE_CALL_STATE) != 0) {
+ try {
+ r.callback.onPreciseCallStateChanged(mPreciseCallState);
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
+ }
+ if ((events & PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) != 0) {
+ try {
+ r.callback.onPreciseDataConnectionStateChanged(
+ mPreciseDataConnectionState);
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
+ }
}
}
} else {
@@ -533,30 +567,47 @@
}
handleRemoveListLocked();
}
+ mPreciseDataConnectionState = new PreciseDataConnectionState(state, networkType,
+ apnType, apn, reason, linkProperties, "");
+ for (Record r : mRecords) {
+ if ((r.events & PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) != 0) {
+ try {
+ r.callback.onPreciseDataConnectionStateChanged(mPreciseDataConnectionState);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ }
+ handleRemoveListLocked();
}
broadcastDataConnectionStateChanged(state, isDataConnectivityPossible, reason, apn,
apnType, linkProperties, linkCapabilities, roaming);
+ broadcastPreciseDataConnectionStateChanged(state, networkType, apnType, apn, reason,
+ linkProperties, "");
}
public void notifyDataConnectionFailed(String reason, String apnType) {
if (!checkNotifyPermission("notifyDataConnectionFailed()")) {
return;
}
- /*
- * This is commented out because there is no onDataConnectionFailed callback
- * in PhoneStateListener. There should be.
synchronized (mRecords) {
- mDataConnectionFailedReason = reason;
- final int N = mRecords.size();
- for (int i=N-1; i>=0; i--) {
- Record r = mRecords.get(i);
- if ((r.events & PhoneStateListener.LISTEN_DATA_CONNECTION_FAILED) != 0) {
- // XXX
+ mPreciseDataConnectionState = new PreciseDataConnectionState(
+ TelephonyManager.DATA_UNKNOWN,TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ apnType, "", reason, null, "");
+ for (Record r : mRecords) {
+ if ((r.events & PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) != 0) {
+ try {
+ r.callback.onPreciseDataConnectionStateChanged(mPreciseDataConnectionState);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
}
}
+ handleRemoveListLocked();
}
- */
broadcastDataConnectionFailed(reason, apnType);
+ broadcastPreciseDataConnectionStateChanged(TelephonyManager.DATA_UNKNOWN,
+ TelephonyManager.NETWORK_TYPE_UNKNOWN, apnType, "", reason, null, "");
}
public void notifyCellLocation(Bundle cellLocation) {
@@ -602,6 +653,81 @@
}
}
+ public void notifyPreciseCallState(int ringingCallState, int foregroundCallState,
+ int backgroundCallState) {
+ if (!checkNotifyPermission("notifyPreciseCallState()")) {
+ return;
+ }
+ synchronized (mRecords) {
+ mRingingCallState = ringingCallState;
+ mForegroundCallState = foregroundCallState;
+ mBackgroundCallState = backgroundCallState;
+ mPreciseCallState = new PreciseCallState(ringingCallState, foregroundCallState,
+ backgroundCallState,
+ DisconnectCause.NOT_VALID,
+ PreciseDisconnectCause.NOT_VALID);
+ for (Record r : mRecords) {
+ if ((r.events & PhoneStateListener.LISTEN_PRECISE_CALL_STATE) != 0) {
+ try {
+ r.callback.onPreciseCallStateChanged(mPreciseCallState);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ }
+ handleRemoveListLocked();
+ }
+ broadcastPreciseCallStateChanged(ringingCallState, foregroundCallState, backgroundCallState,
+ DisconnectCause.NOT_VALID,
+ PreciseDisconnectCause.NOT_VALID);
+ }
+
+ public void notifyDisconnectCause(int disconnectCause, int preciseDisconnectCause) {
+ if (!checkNotifyPermission("notifyDisconnectCause()")) {
+ return;
+ }
+ synchronized (mRecords) {
+ mPreciseCallState = new PreciseCallState(mRingingCallState, mForegroundCallState,
+ mBackgroundCallState, disconnectCause, preciseDisconnectCause);
+ for (Record r : mRecords) {
+ if ((r.events & PhoneStateListener.LISTEN_PRECISE_CALL_STATE) != 0) {
+ try {
+ r.callback.onPreciseCallStateChanged(mPreciseCallState);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ }
+ handleRemoveListLocked();
+ }
+ broadcastPreciseCallStateChanged(mRingingCallState, mForegroundCallState,
+ mBackgroundCallState, disconnectCause, preciseDisconnectCause);
+ }
+
+ public void notifyPreciseDataConnectionFailed(String reason, String apnType,
+ String apn, String failCause) {
+ if (!checkNotifyPermission("notifyPreciseDataConnectionFailed()")) {
+ return;
+ }
+ synchronized (mRecords) {
+ mPreciseDataConnectionState = new PreciseDataConnectionState(
+ TelephonyManager.DATA_UNKNOWN, TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ apnType, apn, reason, null, failCause);
+ for (Record r : mRecords) {
+ if ((r.events & PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE) != 0) {
+ try {
+ r.callback.onPreciseDataConnectionStateChanged(mPreciseDataConnectionState);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ }
+ handleRemoveListLocked();
+ }
+ broadcastPreciseDataConnectionStateChanged(TelephonyManager.DATA_UNKNOWN,
+ TelephonyManager.NETWORK_TYPE_UNKNOWN, apnType, apn, reason, null, failCause);
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
@@ -738,6 +864,33 @@
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
+ private void broadcastPreciseCallStateChanged(int ringingCallState, int foregroundCallState,
+ int backgroundCallState, int disconnectCause, int preciseDisconnectCause) {
+ Intent intent = new Intent(TelephonyManager.ACTION_PRECISE_CALL_STATE_CHANGED);
+ intent.putExtra(TelephonyManager.EXTRA_RINGING_CALL_STATE, ringingCallState);
+ intent.putExtra(TelephonyManager.EXTRA_FOREGROUND_CALL_STATE, foregroundCallState);
+ intent.putExtra(TelephonyManager.EXTRA_BACKGROUND_CALL_STATE, backgroundCallState);
+ intent.putExtra(TelephonyManager.EXTRA_DISCONNECT_CAUSE, disconnectCause);
+ intent.putExtra(TelephonyManager.EXTRA_PRECISE_DISCONNECT_CAUSE, preciseDisconnectCause);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+ android.Manifest.permission.READ_PRECISE_PHONE_STATE);
+ }
+
+ private void broadcastPreciseDataConnectionStateChanged(int state, int networkType,
+ String apnType, String apn, String reason, LinkProperties linkProperties, String failCause) {
+ Intent intent = new Intent(TelephonyManager.ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED);
+ intent.putExtra(PhoneConstants.STATE_KEY, state);
+ intent.putExtra(PhoneConstants.DATA_NETWORK_TYPE_KEY, networkType);
+ if (reason != null) intent.putExtra(PhoneConstants.STATE_CHANGE_REASON_KEY, reason);
+ if (apnType != null) intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnType);
+ if (apn != null) intent.putExtra(PhoneConstants.DATA_APN_KEY, apn);
+ if (linkProperties != null) intent.putExtra(PhoneConstants.DATA_LINK_PROPERTIES_KEY, linkProperties);
+ if (failCause != null) intent.putExtra(PhoneConstants.DATA_FAILURE_CAUSE_KEY, failCause);
+
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+ android.Manifest.permission.READ_PRECISE_PHONE_STATE);
+ }
+
private boolean checkNotifyPermission(String method) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
== PackageManager.PERMISSION_GRANTED) {
@@ -766,6 +919,12 @@
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.READ_PHONE_STATE, null);
}
+
+ if ((events & PRECISE_PHONE_STATE_PERMISSION_MASK) != 0) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_PRECISE_PHONE_STATE, null);
+
+ }
}
private void handleRemoveListLocked() {
diff --git a/services/java/com/android/server/firewall/IntentFirewall.java b/services/java/com/android/server/firewall/IntentFirewall.java
index aaa0b58..6df1dbd 100644
--- a/services/java/com/android/server/firewall/IntentFirewall.java
+++ b/services/java/com/android/server/firewall/IntentFirewall.java
@@ -268,11 +268,13 @@
}
File[] files = rulesDir.listFiles();
- for (int i=0; i<files.length; i++) {
- File file = files[i];
+ if (files != null) {
+ for (int i=0; i<files.length; i++) {
+ File file = files[i];
- if (file.getName().endsWith(".xml")) {
- readRules(file, resolvers);
+ if (file.getName().endsWith(".xml")) {
+ readRules(file, resolvers);
+ }
}
}
diff --git a/services/java/com/android/server/pm/Installer.java b/services/java/com/android/server/pm/Installer.java
index 0d2b503..11a6498 100644
--- a/services/java/com/android/server/pm/Installer.java
+++ b/services/java/com/android/server/pm/Installer.java
@@ -211,6 +211,17 @@
return execute(builder.toString());
}
+ public int idmap(String targetApkPath, String overlayApkPath, int uid) {
+ StringBuilder builder = new StringBuilder("idmap");
+ builder.append(' ');
+ builder.append(targetApkPath);
+ builder.append(' ');
+ builder.append(overlayApkPath);
+ builder.append(' ');
+ builder.append(uid);
+ return execute(builder.toString());
+ }
+
public int movedex(String srcPath, String dstPath) {
StringBuilder builder = new StringBuilder("movedex");
builder.append(' ');
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 7ea1fce..74a66d6 100755
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -217,6 +217,7 @@
static final int SCAN_UPDATE_TIME = 1<<6;
static final int SCAN_DEFER_DEX = 1<<7;
static final int SCAN_BOOTING = 1<<8;
+ static final int SCAN_TRUSTED_OVERLAY = 1<<9;
static final int REMOVE_CHATTY = 1<<16;
@@ -257,8 +258,13 @@
private static final String LIB_DIR_NAME = "lib";
+ private static final String VENDOR_OVERLAY_DIR = "/vendor/overlay";
+
static final String mTempContainerPrefix = "smdl2tmp";
+ private static final String IDMAP_PREFIX = "/data/resource-cache/";
+ private static final String IDMAP_SUFFIX = "@idmap";
+
final HandlerThread mHandlerThread = new HandlerThread("PackageManager",
Process.THREAD_PRIORITY_BACKGROUND);
final PackageHandler mHandler;
@@ -296,6 +302,9 @@
// This is the object monitoring the system app dir.
final FileObserver mVendorInstallObserver;
+ // This is the object monitoring the vendor overlay package dir.
+ final FileObserver mVendorOverlayInstallObserver;
+
// This is the object monitoring mAppInstallDir.
final FileObserver mAppInstallObserver;
@@ -343,6 +352,10 @@
final HashMap<String, PackageParser.Package> mPackages =
new HashMap<String, PackageParser.Package>();
+ // Tracks available target package names -> overlay package paths.
+ final HashMap<String, HashMap<String, PackageParser.Package>> mOverlays =
+ new HashMap<String, HashMap<String, PackageParser.Package>>();
+
final Settings mSettings;
boolean mRestoredSettings;
@@ -1273,6 +1286,17 @@
}
}
+ // Collect vendor overlay packages.
+ // (Do this before scanning any apps.)
+ // For security and version matching reason, only consider
+ // overlay packages if they reside in VENDOR_OVERLAY_DIR.
+ File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR);
+ mVendorOverlayInstallObserver = new AppDirObserver(
+ vendorOverlayDir.getPath(), OBSERVER_EVENTS, true, false);
+ mVendorOverlayInstallObserver.startWatching();
+ scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM
+ | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode | SCAN_TRUSTED_OVERLAY, 0);
+
// Find base frameworks (resource packages without code).
mFrameworkInstallObserver = new AppDirObserver(
frameworkDir.getPath(), OBSERVER_EVENTS, true, false);
@@ -3475,6 +3499,56 @@
return finalList;
}
+ private void createIdmapsForPackageLI(PackageParser.Package pkg) {
+ HashMap<String, PackageParser.Package> overlays = mOverlays.get(pkg.packageName);
+ if (overlays == null) {
+ Slog.w(TAG, "Unable to create idmap for " + pkg.packageName + ": no overlay packages");
+ return;
+ }
+ for (PackageParser.Package opkg : overlays.values()) {
+ // Not much to do if idmap fails: we already logged the error
+ // and we certainly don't want to abort installation of pkg simply
+ // because an overlay didn't fit properly. For these reasons,
+ // ignore the return value of createIdmapForPackagePairLI.
+ createIdmapForPackagePairLI(pkg, opkg);
+ }
+ }
+
+ private boolean createIdmapForPackagePairLI(PackageParser.Package pkg,
+ PackageParser.Package opkg) {
+ if (!opkg.mTrustedOverlay) {
+ Slog.w(TAG, "Skipping target and overlay pair " + pkg.mScanPath + " and " +
+ opkg.mScanPath + ": overlay not trusted");
+ return false;
+ }
+ HashMap<String, PackageParser.Package> overlaySet = mOverlays.get(pkg.packageName);
+ if (overlaySet == null) {
+ Slog.e(TAG, "was about to create idmap for " + pkg.mScanPath + " and " +
+ opkg.mScanPath + " but target package has no known overlays");
+ return false;
+ }
+ final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
+ if (mInstaller.idmap(pkg.mScanPath, opkg.mScanPath, sharedGid) != 0) {
+ Slog.e(TAG, "Failed to generate idmap for " + pkg.mScanPath + " and " + opkg.mScanPath);
+ return false;
+ }
+ PackageParser.Package[] overlayArray =
+ overlaySet.values().toArray(new PackageParser.Package[0]);
+ Comparator<PackageParser.Package> cmp = new Comparator<PackageParser.Package>() {
+ public int compare(PackageParser.Package p1, PackageParser.Package p2) {
+ return p1.mOverlayPriority - p2.mOverlayPriority;
+ }
+ };
+ Arrays.sort(overlayArray, cmp);
+
+ pkg.applicationInfo.resourceDirs = new String[overlayArray.length];
+ int i = 0;
+ for (PackageParser.Package p : overlayArray) {
+ pkg.applicationInfo.resourceDirs[i++] = p.applicationInfo.sourceDir;
+ }
+ return true;
+ }
+
private void scanDirLI(File dir, int flags, int scanMode, long currentTime) {
String[] files = dir.list();
if (files == null) {
@@ -3572,7 +3646,7 @@
pp.setSeparateProcesses(mSeparateProcesses);
pp.setOnlyCoreApps(mOnlyCore);
final PackageParser.Package pkg = pp.parsePackage(scanFile,
- scanPath, mMetrics, parseFlags);
+ scanPath, mMetrics, parseFlags, (scanMode & SCAN_TRUSTED_OVERLAY) != 0);
if (pkg == null) {
mLastScanError = pp.getParseError();
@@ -5081,6 +5155,29 @@
}
pkgSetting.setTimeStamp(scanFileTime);
+
+ // Create idmap files for pairs of (packages, overlay packages).
+ // Note: "android", ie framework-res.apk, is handled by native layers.
+ if (pkg.mOverlayTarget != null) {
+ // This is an overlay package.
+ if (pkg.mOverlayTarget != null && !pkg.mOverlayTarget.equals("android")) {
+ if (!mOverlays.containsKey(pkg.mOverlayTarget)) {
+ mOverlays.put(pkg.mOverlayTarget,
+ new HashMap<String, PackageParser.Package>());
+ }
+ HashMap<String, PackageParser.Package> map = mOverlays.get(pkg.mOverlayTarget);
+ map.put(pkg.packageName, pkg);
+ PackageParser.Package orig = mPackages.get(pkg.mOverlayTarget);
+ if (orig != null && !createIdmapForPackagePairLI(orig, pkg)) {
+ mLastScanError = PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
+ return null;
+ }
+ }
+ } else if (mOverlays.containsKey(pkg.packageName) &&
+ !pkg.packageName.equals("android")) {
+ // This is a regular package, with one or more known overlay packages.
+ createIdmapsForPackageLI(pkg);
+ }
}
return pkg;
diff --git a/services/jni/com_android_server_ConsumerIrService.cpp b/services/jni/com_android_server_ConsumerIrService.cpp
index 004c0aa..3a50ff7 100644
--- a/services/jni/com_android_server_ConsumerIrService.cpp
+++ b/services/jni/com_android_server_ConsumerIrService.cpp
@@ -29,7 +29,7 @@
namespace android {
-static jint halOpen(JNIEnv *env, jobject obj) {
+static jlong halOpen(JNIEnv *env, jobject obj) {
hw_module_t const* module;
consumerir_device_t *dev;
int err;
@@ -47,10 +47,10 @@
return 0;
}
- return reinterpret_cast<jint>(dev);
+ return reinterpret_cast<jlong>(dev);
}
-static jint halTransmit(JNIEnv *env, jobject obj, jint halObject,
+static jint halTransmit(JNIEnv *env, jobject obj, jlong halObject,
jint carrierFrequency, jintArray pattern) {
int ret;
@@ -67,8 +67,8 @@
}
static jintArray halGetCarrierFrequencies(JNIEnv *env, jobject obj,
- jint halObject) {
- consumerir_device_t *dev = (consumerir_device_t *) halObject;
+ jlong halObject) {
+ consumerir_device_t *dev = reinterpret_cast<consumerir_device_t*>(halObject);
consumerir_freq_range_t *ranges;
int len;
@@ -101,9 +101,9 @@
}
static JNINativeMethod method_table[] = {
- { "halOpen", "()I", (void *)halOpen },
- { "halTransmit", "(II[I)I", (void *)halTransmit },
- { "halGetCarrierFrequencies", "(I)[I", (void *)halGetCarrierFrequencies},
+ { "halOpen", "()J", (void *)halOpen },
+ { "halTransmit", "(JI[I)I", (void *)halTransmit },
+ { "halGetCarrierFrequencies", "(J)[I", (void *)halGetCarrierFrequencies},
};
int register_android_server_ConsumerIrService(JNIEnv *env) {
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
new file mode 100644
index 0000000..323e0ac
--- /dev/null
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+/**
+ * Contains disconnect call causes generated by the
+ * framework and the RIL.
+ *
+ * @hide
+ */
+public class DisconnectCause {
+
+ /** The disconnect cause is not valid (Not received a disconnect cause) */
+ public static final int NOT_VALID = -1;
+ /** Has not yet disconnected */
+ public static final int NOT_DISCONNECTED = 0;
+ /** An incoming call that was missed and never answered */
+ public static final int INCOMING_MISSED = 1;
+ /** Normal; Remote hangup*/
+ public static final int NORMAL = 2;
+ /** Normal; Local hangup */
+ public static final int LOCAL = 3;
+ /** Outgoing call to busy line */
+ public static final int BUSY = 4;
+ /** Outgoing call to congested network */
+ public static final int CONGESTION = 5;
+ /** Not presently used */
+ public static final int MMI = 6;
+ /** Invalid dial string */
+ public static final int INVALID_NUMBER = 7;
+ /** Cannot reach the peer */
+ public static final int NUMBER_UNREACHABLE = 8;
+ /** Cannot reach the server */
+ public static final int SERVER_UNREACHABLE = 9;
+ /** Invalid credentials */
+ public static final int INVALID_CREDENTIALS = 10;
+ /** Calling from out of network is not allowed */
+ public static final int OUT_OF_NETWORK = 11;
+ /** Server error */
+ public static final int SERVER_ERROR = 12;
+ /** Client timed out */
+ public static final int TIMED_OUT = 13;
+ /** Client went out of network range */
+ public static final int LOST_SIGNAL = 14;
+ /** GSM or CDMA ACM limit exceeded */
+ public static final int LIMIT_EXCEEDED = 15;
+ /** An incoming call that was rejected */
+ public static final int INCOMING_REJECTED = 16;
+ /** Radio is turned off explicitly */
+ public static final int POWER_OFF = 17;
+ /** Out of service */
+ public static final int OUT_OF_SERVICE = 18;
+ /** No ICC, ICC locked, or other ICC error */
+ public static final int ICC_ERROR = 19;
+ /** Call was blocked by call barring */
+ public static final int CALL_BARRED = 20;
+ /** Call was blocked by fixed dial number */
+ public static final int FDN_BLOCKED = 21;
+ /** Call was blocked by restricted all voice access */
+ public static final int CS_RESTRICTED = 22;
+ /** Call was blocked by restricted normal voice access */
+ public static final int CS_RESTRICTED_NORMAL = 23;
+ /** Call was blocked by restricted emergency voice access */
+ public static final int CS_RESTRICTED_EMERGENCY = 24;
+ /** Unassigned number */
+ public static final int UNOBTAINABLE_NUMBER = 25;
+ /** MS is locked until next power cycle */
+ public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 26;
+ /** Drop call*/
+ public static final int CDMA_DROP = 27;
+ /** INTERCEPT order received, MS state idle entered */
+ public static final int CDMA_INTERCEPT = 28;
+ /** MS has been redirected, call is cancelled */
+ public static final int CDMA_REORDER = 29;
+ /** Service option rejection */
+ public static final int CDMA_SO_REJECT = 30;
+ /** Requested service is rejected, retry delay is set */
+ public static final int CDMA_RETRY_ORDER = 31;
+ /** Unable to obtain access to the CDMA system */
+ public static final int CDMA_ACCESS_FAILURE = 32;
+ /** Not a preempted call */
+ public static final int CDMA_PREEMPTED = 33;
+ /** Not an emergency call */
+ public static final int CDMA_NOT_EMERGENCY = 34;
+ /** Access Blocked by CDMA network */
+ public static final int CDMA_ACCESS_BLOCKED = 35;
+ /** Unknown error or not specified */
+ public static final int ERROR_UNSPECIFIED = 36;
+
+ /** Private constructor to avoid class instantiation. */
+ private DisconnectCause() {
+ // Do nothing.
+ }
+}
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index ff77fc0..538548d 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -24,6 +24,8 @@
import android.telephony.CellLocation;
import android.telephony.CellInfo;
import android.telephony.Rlog;
+import android.telephony.PreciseCallState;
+import android.telephony.PreciseDataConnectionState;
import com.android.internal.telephony.IPhoneStateListener;
@@ -165,6 +167,27 @@
*/
public static final int LISTEN_CELL_INFO = 0x00000400;
+ /**
+ * Listen for precise changes and fails to the device calls (cellular).
+ * {@more}
+ * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+ * READ_PRECISE_PHONE_STATE}
+ *
+ * @hide
+ */
+ public static final int LISTEN_PRECISE_CALL_STATE = 0x00000800;
+
+ /**
+ * Listen for precise changes and fails on the data connection (cellular).
+ * {@more}
+ * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
+ * READ_PRECISE_PHONE_STATE}
+ *
+ * @see #onPreciseDataConnectionStateChanged
+ * @hide
+ */
+ public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 0x00001000;
+
public PhoneStateListener() {
}
@@ -293,6 +316,25 @@
}
/**
+ * Callback invoked when precise device call state changes.
+ *
+ * @hide
+ */
+ public void onPreciseCallStateChanged(PreciseCallState callState) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when data connection state changes with precise information.
+ *
+ * @hide
+ */
+ public void onPreciseDataConnectionStateChanged(
+ PreciseDataConnectionState dataConnectionState) {
+ // default implementation empty
+ }
+
+ /**
* The callback methods need to be called on the handler thread where
* this object was created. If the binder did that for us it'd be nice.
*/
@@ -344,6 +386,16 @@
public void onCellInfoChanged(List<CellInfo> cellInfo) {
Message.obtain(mHandler, LISTEN_CELL_INFO, 0, 0, cellInfo).sendToTarget();
}
+
+ public void onPreciseCallStateChanged(PreciseCallState callState) {
+ Message.obtain(mHandler, LISTEN_PRECISE_CALL_STATE, 0, 0, callState).sendToTarget();
+ }
+
+ public void onPreciseDataConnectionStateChanged(
+ PreciseDataConnectionState dataConnectionState) {
+ Message.obtain(mHandler, LISTEN_PRECISE_DATA_CONNECTION_STATE, 0, 0,
+ dataConnectionState).sendToTarget();
+ }
};
Handler mHandler = new Handler() {
@@ -383,6 +435,12 @@
break;
case LISTEN_CELL_INFO:
PhoneStateListener.this.onCellInfoChanged((List<CellInfo>)msg.obj);
+ break;
+ case LISTEN_PRECISE_CALL_STATE:
+ PhoneStateListener.this.onPreciseCallStateChanged((PreciseCallState)msg.obj);
+ break;
+ case LISTEN_PRECISE_DATA_CONNECTION_STATE:
+ PhoneStateListener.this.onPreciseDataConnectionStateChanged((PreciseDataConnectionState)msg.obj);
}
}
};
diff --git a/telephony/java/android/telephony/PreciseCallState.aidl b/telephony/java/android/telephony/PreciseCallState.aidl
new file mode 100644
index 0000000..447f29b
--- /dev/null
+++ b/telephony/java/android/telephony/PreciseCallState.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.telephony;
+
+parcelable PreciseCallState;
\ No newline at end of file
diff --git a/telephony/java/android/telephony/PreciseCallState.java b/telephony/java/android/telephony/PreciseCallState.java
new file mode 100644
index 0000000..a85df15
--- /dev/null
+++ b/telephony/java/android/telephony/PreciseCallState.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Rlog;
+import android.telephony.DisconnectCause;
+import android.telephony.PreciseDisconnectCause;
+
+/**
+ * Contains precise call state and call fail causes generated by the
+ * framework and the RIL.
+ *
+ * The following call information is included in returned PreciseCallState:
+ *
+ * <ul>
+ * <li>Ringing call state.
+ * <li>Foreground call state.
+ * <li>Background call state.
+ * <li>Disconnect cause; generated by the framework.
+ * <li>Precise disconnect cause; generated by the RIL.
+ * </ul>
+ *
+ * @hide
+ */
+public class PreciseCallState implements Parcelable {
+
+ /** Call state is not valid (Not received a call state). */
+ public static final int PRECISE_CALL_STATE_NOT_VALID = -1;
+ /** Call state: No activity. */
+ public static final int PRECISE_CALL_STATE_IDLE = 0;
+ /** Call state: Active. */
+ public static final int PRECISE_CALL_STATE_ACTIVE = 1;
+ /** Call state: On hold. */
+ public static final int PRECISE_CALL_STATE_HOLDING = 2;
+ /** Call state: Dialing. */
+ public static final int PRECISE_CALL_STATE_DIALING = 3;
+ /** Call state: Alerting. */
+ public static final int PRECISE_CALL_STATE_ALERTING = 4;
+ /** Call state: Incoming. */
+ public static final int PRECISE_CALL_STATE_INCOMING = 5;
+ /** Call state: Waiting. */
+ public static final int PRECISE_CALL_STATE_WAITING = 6;
+ /** Call state: Disconnected. */
+ public static final int PRECISE_CALL_STATE_DISCONNECTED = 7;
+ /** Call state: Disconnecting. */
+ public static final int PRECISE_CALL_STATE_DISCONNECTING = 8;
+
+ private int mRingingCallState = PRECISE_CALL_STATE_NOT_VALID;
+ private int mForegroundCallState = PRECISE_CALL_STATE_NOT_VALID;
+ private int mBackgroundCallState = PRECISE_CALL_STATE_NOT_VALID;
+ private int mDisconnectCause = DisconnectCause.NOT_VALID;
+ private int mPreciseDisconnectCause = PreciseDisconnectCause.NOT_VALID;
+
+ /**
+ * Constructor
+ *
+ * @hide
+ */
+ public PreciseCallState(int ringingCall, int foregroundCall, int backgroundCall,
+ int disconnectCause, int preciseDisconnectCause) {
+ mRingingCallState = ringingCall;
+ mForegroundCallState = foregroundCall;
+ mBackgroundCallState = backgroundCall;
+ mDisconnectCause = disconnectCause;
+ mPreciseDisconnectCause = preciseDisconnectCause;
+ }
+
+ /**
+ * Empty Constructor
+ *
+ * @hide
+ */
+ public PreciseCallState() {
+ }
+
+ /**
+ * Construct a PreciseCallState object from the given parcel.
+ */
+ private PreciseCallState(Parcel in) {
+ mRingingCallState = in.readInt();
+ mForegroundCallState = in.readInt();
+ mBackgroundCallState = in.readInt();
+ mDisconnectCause = in.readInt();
+ mPreciseDisconnectCause = in.readInt();
+ }
+
+ /**
+ * Get precise ringing call state
+ *
+ * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
+ * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
+ * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE
+ * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING
+ * @see PreciseCallState#PRECISE_CALL_STATE_DIALING
+ * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING
+ * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING
+ * @see PreciseCallState#PRECISE_CALL_STATE_WAITING
+ * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED
+ * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING
+ */
+ public int getRingingCallState() {
+ return mRingingCallState;
+ }
+
+ /**
+ * Get precise foreground call state
+ *
+ * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
+ * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
+ * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE
+ * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING
+ * @see PreciseCallState#PRECISE_CALL_STATE_DIALING
+ * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING
+ * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING
+ * @see PreciseCallState#PRECISE_CALL_STATE_WAITING
+ * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED
+ * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING
+ */
+ public int getForegroundCallState() {
+ return mForegroundCallState;
+ }
+
+ /**
+ * Get precise background call state
+ *
+ * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
+ * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
+ * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE
+ * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING
+ * @see PreciseCallState#PRECISE_CALL_STATE_DIALING
+ * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING
+ * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING
+ * @see PreciseCallState#PRECISE_CALL_STATE_WAITING
+ * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED
+ * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING
+ */
+ public int getBackgroundCallState() {
+ return mBackgroundCallState;
+ }
+
+ /**
+ * Get disconnect cause generated by the framework
+ *
+ * @see DisconnectCause#NOT_VALID
+ * @see DisconnectCause#NOT_DISCONNECTED
+ * @see DisconnectCause#INCOMING_MISSED
+ * @see DisconnectCause#NORMAL
+ * @see DisconnectCause#LOCAL
+ * @see DisconnectCause#BUSY
+ * @see DisconnectCause#CONGESTION
+ * @see DisconnectCause#MMI
+ * @see DisconnectCause#INVALID_NUMBER
+ * @see DisconnectCause#NUMBER_UNREACHABLE
+ * @see DisconnectCause#SERVER_UNREACHABLE
+ * @see DisconnectCause#INVALID_CREDENTIALS
+ * @see DisconnectCause#OUT_OF_NETWORK
+ * @see DisconnectCause#SERVER_ERROR
+ * @see DisconnectCause#TIMED_OUT
+ * @see DisconnectCause#LOST_SIGNAL
+ * @see DisconnectCause#LIMIT_EXCEEDED
+ * @see DisconnectCause#INCOMING_REJECTED
+ * @see DisconnectCause#POWER_OFF
+ * @see DisconnectCause#OUT_OF_SERVICE
+ * @see DisconnectCause#ICC_ERROR
+ * @see DisconnectCause#CALL_BARRED
+ * @see DisconnectCause#FDN_BLOCKED
+ * @see DisconnectCause#CS_RESTRICTED
+ * @see DisconnectCause#CS_RESTRICTED_NORMAL
+ * @see DisconnectCause#CS_RESTRICTED_EMERGENCY
+ * @see DisconnectCause#UNOBTAINABLE_NUMBER
+ * @see DisconnectCause#CDMA_LOCKED_UNTIL_POWER_CYCLE
+ * @see DisconnectCause#CDMA_DROP
+ * @see DisconnectCause#CDMA_INTERCEPT
+ * @see DisconnectCause#CDMA_REORDER
+ * @see DisconnectCause#CDMA_SO_REJECT
+ * @see DisconnectCause#CDMA_RETRY_ORDER
+ * @see DisconnectCause#CDMA_ACCESS_FAILURE
+ * @see DisconnectCause#CDMA_PREEMPTED
+ * @see DisconnectCause#CDMA_NOT_EMERGENCY
+ * @see DisconnectCause#CDMA_ACCESS_BLOCKED
+ * @see DisconnectCause#ERROR_UNSPECIFIED
+ */
+ public int getDisconnectCause() {
+ return mDisconnectCause;
+ }
+
+ /**
+ * Get disconnect cause generated by the RIL
+ *
+ * @see PreciseDisconnectCause#NOT_VALID
+ * @see PreciseDisconnectCause#NO_DISCONNECT_CAUSE_AVAILABLE
+ * @see PreciseDisconnectCause#UNOBTAINABLE_NUMBER
+ * @see PreciseDisconnectCause#NORMAL
+ * @see PreciseDisconnectCause#BUSY
+ * @see PreciseDisconnectCause#NUMBER_CHANGED
+ * @see PreciseDisconnectCause#STATUS_ENQUIRY
+ * @see PreciseDisconnectCause#NORMAL_UNSPECIFIED
+ * @see PreciseDisconnectCause#NO_CIRCUIT_AVAIL
+ * @see PreciseDisconnectCause#TEMPORARY_FAILURE
+ * @see PreciseDisconnectCause#SWITCHING_CONGESTION
+ * @see PreciseDisconnectCause#CHANNEL_NOT_AVAIL
+ * @see PreciseDisconnectCause#QOS_NOT_AVAIL
+ * @see PreciseDisconnectCause#BEARER_NOT_AVAIL
+ * @see PreciseDisconnectCause#ACM_LIMIT_EXCEEDED
+ * @see PreciseDisconnectCause#CALL_BARRED
+ * @see PreciseDisconnectCause#FDN_BLOCKED
+ * @see PreciseDisconnectCause#IMSI_UNKNOWN_IN_VLR
+ * @see PreciseDisconnectCause#IMEI_NOT_ACCEPTED
+ * @see PreciseDisconnectCause#CDMA_LOCKED_UNTIL_POWER_CYCLE
+ * @see PreciseDisconnectCause#CDMA_DROP
+ * @see PreciseDisconnectCause#CDMA_INTERCEPT
+ * @see PreciseDisconnectCause#CDMA_REORDER
+ * @see PreciseDisconnectCause#CDMA_SO_REJECT
+ * @see PreciseDisconnectCause#CDMA_RETRY_ORDER
+ * @see PreciseDisconnectCause#CDMA_ACCESS_FAILURE
+ * @see PreciseDisconnectCause#CDMA_PREEMPTED
+ * @see PreciseDisconnectCause#CDMA_NOT_EMERGENCY
+ * @see PreciseDisconnectCause#CDMA_ACCESS_BLOCKED
+ * @see PreciseDisconnectCause#ERROR_UNSPECIFIED
+ */
+ public int getPreciseDisconnectCause() {
+ return mPreciseDisconnectCause;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mRingingCallState);
+ out.writeInt(mForegroundCallState);
+ out.writeInt(mBackgroundCallState);
+ out.writeInt(mDisconnectCause);
+ out.writeInt(mPreciseDisconnectCause);
+ }
+
+ public static final Parcelable.Creator<PreciseCallState> CREATOR
+ = new Parcelable.Creator<PreciseCallState>() {
+
+ public PreciseCallState createFromParcel(Parcel in) {
+ return new PreciseCallState(in);
+ }
+
+ public PreciseCallState[] newArray(int size) {
+ return new PreciseCallState[size];
+ }
+ };
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + mRingingCallState;
+ result = prime * result + mForegroundCallState;
+ result = prime * result + mBackgroundCallState;
+ result = prime * result + mDisconnectCause;
+ result = prime * result + mPreciseDisconnectCause;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ PreciseCallState other = (PreciseCallState) obj;
+ return (mRingingCallState != other.mRingingCallState &&
+ mForegroundCallState != other.mForegroundCallState &&
+ mBackgroundCallState != other.mBackgroundCallState &&
+ mDisconnectCause != other.mDisconnectCause &&
+ mPreciseDisconnectCause != other.mPreciseDisconnectCause);
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append("Ringing call state: " + mRingingCallState);
+ sb.append(", Foreground call state: " + mForegroundCallState);
+ sb.append(", Background call state: " + mBackgroundCallState);
+ sb.append(", Disconnect cause: " + mDisconnectCause);
+ sb.append(", Precise disconnect cause: " + mPreciseDisconnectCause);
+
+ return sb.toString();
+ }
+}
diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.aidl b/telephony/java/android/telephony/PreciseDataConnectionState.aidl
new file mode 100644
index 0000000..07ad762
--- /dev/null
+++ b/telephony/java/android/telephony/PreciseDataConnectionState.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.telephony;
+
+parcelable PreciseDataConnectionState;
\ No newline at end of file
diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java
new file mode 100644
index 0000000..87529fe
--- /dev/null
+++ b/telephony/java/android/telephony/PreciseDataConnectionState.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Rlog;
+import android.telephony.TelephonyManager;
+import android.net.LinkProperties;
+
+/**
+ * Contains precise data connection state.
+ *
+ * The following data connection information is included in returned PreciseDataConnectionState:
+ *
+ * <ul>
+ * <li>Data connection state.
+ * <li>Network type of the connection.
+ * <li>APN type.
+ * <li>APN.
+ * <li>Data connection change reason.
+ * <li>The properties of the network link.
+ * <li>Data connection fail cause.
+ * </ul>
+ *
+ * @hide
+ */
+public class PreciseDataConnectionState implements Parcelable {
+
+ private int mState = TelephonyManager.DATA_UNKNOWN;
+ private int mNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ private String mAPNType = "";
+ private String mAPN = "";
+ private String mReason = "";
+ private LinkProperties mLinkProperties = null;
+ private String mFailCause = "";
+
+ /**
+ * Constructor
+ *
+ * @hide
+ */
+ public PreciseDataConnectionState(int state, int networkType,
+ String apnType, String apn, String reason,
+ LinkProperties linkProperties, String failCause) {
+ mState = state;
+ mNetworkType = networkType;
+ mAPNType = apnType;
+ mAPN = apn;
+ mReason = reason;
+ mLinkProperties = linkProperties;
+ mFailCause = failCause;
+ }
+
+ /**
+ * Empty Constructor
+ *
+ * @hide
+ */
+ public PreciseDataConnectionState() {
+ }
+
+ /**
+ * Construct a PreciseDataConnectionState object from the given parcel.
+ */
+ private PreciseDataConnectionState(Parcel in) {
+ mState = in.readInt();
+ mNetworkType = in.readInt();
+ mAPNType = in.readString();
+ mAPN = in.readString();
+ mReason = in.readString();
+ mLinkProperties = (LinkProperties)in.readParcelable(null);
+ mFailCause = in.readString();
+ }
+
+ /**
+ * Get data connection state
+ *
+ * @see TelephonyManager#DATA_UNKNOWN
+ * @see TelephonyManager#DATA_DISCONNECTED
+ * @see TelephonyManager#DATA_CONNECTING
+ * @see TelephonyManager#DATA_CONNECTED
+ * @see TelephonyManager#DATA_SUSPENDED
+ */
+ public int getDataConnectionState() {
+ return mState;
+ }
+
+ /**
+ * Get data connection network type
+ *
+ * @see TelephonyManager#NETWORK_TYPE_UNKNOWN
+ * @see TelephonyManager#NETWORK_TYPE_GPRS
+ * @see TelephonyManager#NETWORK_TYPE_EDGE
+ * @see TelephonyManager#NETWORK_TYPE_UMTS
+ * @see TelephonyManager#NETWORK_TYPE_CDMA
+ * @see TelephonyManager#NETWORK_TYPE_EVDO_0
+ * @see TelephonyManager#NETWORK_TYPE_EVDO_A
+ * @see TelephonyManager#NETWORK_TYPE_1xRTT
+ * @see TelephonyManager#NETWORK_TYPE_HSDPA
+ * @see TelephonyManager#NETWORK_TYPE_HSUPA
+ * @see TelephonyManager#NETWORK_TYPE_HSPA
+ * @see TelephonyManager#NETWORK_TYPE_IDEN
+ * @see TelephonyManager#NETWORK_TYPE_EVDO_B
+ * @see TelephonyManager#NETWORK_TYPE_LTE
+ * @see TelephonyManager#NETWORK_TYPE_EHRPD
+ * @see TelephonyManager#NETWORK_TYPE_HSPAP
+ */
+ public int getDataConnectionNetworkType() {
+ return mNetworkType;
+ }
+
+ /**
+ * Get data connection APN type
+ */
+ public String getDataConnectionAPNType() {
+ return mAPNType;
+ }
+
+ /**
+ * Get data connection APN.
+ */
+ public String getDataConnectionAPN() {
+ return mAPN;
+ }
+
+ /**
+ * Get data connection change reason.
+ */
+ public String getDataConnectionChangeReason() {
+ return mReason;
+ }
+
+ /**
+ * Get the properties of the network link.
+ */
+ public LinkProperties getDataConnectionLinkProperties() {
+ return mLinkProperties;
+ }
+
+ /**
+ * Get data connection fail cause, in case there was a failure.
+ */
+ public String getDataConnectionFailCause() {
+ return mFailCause;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mState);
+ out.writeInt(mNetworkType);
+ out.writeString(mAPNType);
+ out.writeString(mAPN);
+ out.writeString(mReason);
+ out.writeParcelable(mLinkProperties, flags);
+ out.writeString(mFailCause);
+ }
+
+ public static final Parcelable.Creator<PreciseDataConnectionState> CREATOR
+ = new Parcelable.Creator<PreciseDataConnectionState>() {
+
+ public PreciseDataConnectionState createFromParcel(Parcel in) {
+ return new PreciseDataConnectionState(in);
+ }
+
+ public PreciseDataConnectionState[] newArray(int size) {
+ return new PreciseDataConnectionState[size];
+ }
+ };
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + mState;
+ result = prime * result + mNetworkType;
+ result = prime * result + ((mAPNType == null) ? 0 : mAPNType.hashCode());
+ result = prime * result + ((mAPN == null) ? 0 : mAPN.hashCode());
+ result = prime * result + ((mReason == null) ? 0 : mReason.hashCode());
+ result = prime * result + ((mLinkProperties == null) ? 0 : mLinkProperties.hashCode());
+ result = prime * result + ((mFailCause == null) ? 0 : mFailCause.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ PreciseDataConnectionState other = (PreciseDataConnectionState) obj;
+ if (mAPN == null) {
+ if (other.mAPN != null) {
+ return false;
+ }
+ } else if (!mAPN.equals(other.mAPN)) {
+ return false;
+ }
+ if (mAPNType == null) {
+ if (other.mAPNType != null) {
+ return false;
+ }
+ } else if (!mAPNType.equals(other.mAPNType)) {
+ return false;
+ }
+ if (mFailCause == null) {
+ if (other.mFailCause != null) {
+ return false;
+ }
+ } else if (!mFailCause.equals(other.mFailCause)) {
+ return false;
+ }
+ if (mLinkProperties == null) {
+ if (other.mLinkProperties != null) {
+ return false;
+ }
+ } else if (!mLinkProperties.equals(other.mLinkProperties)) {
+ return false;
+ }
+ if (mNetworkType != other.mNetworkType) {
+ return false;
+ }
+ if (mReason == null) {
+ if (other.mReason != null) {
+ return false;
+ }
+ } else if (!mReason.equals(other.mReason)) {
+ return false;
+ }
+ if (mState != other.mState) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("Data Connection state: " + mState);
+ sb.append(", Network type: " + mNetworkType);
+ sb.append(", APN type: " + mAPNType);
+ sb.append(", APN: " + mAPN);
+ sb.append(", Change reason: " + mReason);
+ sb.append(", Link properties: " + mLinkProperties);
+ sb.append(", Fail cause: " + mFailCause);
+
+ return sb.toString();
+ }
+}
diff --git a/telephony/java/android/telephony/PreciseDisconnectCause.java b/telephony/java/android/telephony/PreciseDisconnectCause.java
new file mode 100644
index 0000000..54ab19d
--- /dev/null
+++ b/telephony/java/android/telephony/PreciseDisconnectCause.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+/**
+ * Contains precise disconnect call causes generated by the
+ * framework and the RIL.
+ *
+ * @hide
+ */
+public class PreciseDisconnectCause {
+
+ /** The disconnect cause is not valid (Not received a disconnect cause)*/
+ public static final int NOT_VALID = -1;
+ /** No disconnect cause provided. Generally a local disconnect or an incoming missed call */
+ public static final int NO_DISCONNECT_CAUSE_AVAILABLE = 0;
+ /**
+ * The destination cannot be reached because the number, although valid,
+ * is not currently assigned
+ */
+ public static final int UNOBTAINABLE_NUMBER = 1;
+ /** One of the users involved in the call has requested that the call is cleared */
+ public static final int NORMAL = 16;
+ /** The called user is unable to accept another call */
+ public static final int BUSY = 17;
+ /** The called number is no longer assigned */
+ public static final int NUMBER_CHANGED = 22;
+ /** Provided in response to a STATUS ENQUIRY message */
+ public static final int STATUS_ENQUIRY = 30;
+ /** Reports a normal disconnect only when no other normal cause applies */
+ public static final int NORMAL_UNSPECIFIED = 31;
+ /** There is no channel presently available to handle the call */
+ public static final int NO_CIRCUIT_AVAIL = 34;
+ /**
+ * The network is not functioning correctly and the condition is not likely to last
+ * a long period of time
+ */
+ public static final int TEMPORARY_FAILURE = 41;
+ /** The switching equipment is experiencing a period of high traffic */
+ public static final int SWITCHING_CONGESTION = 42;
+ /** The channel cannot be provided */
+ public static final int CHANNEL_NOT_AVAIL = 44;
+ /** The requested quality of service (ITU-T X.213) cannot be provided */
+ public static final int QOS_NOT_AVAIL = 49;
+ /** The requested bearer capability is not available at this time */
+ public static final int BEARER_NOT_AVAIL = 58;
+ /** The call clearing is due to ACM being greater than or equal to ACMmax */
+ public static final int ACM_LIMIT_EXCEEDED = 68;
+ /** The call is restricted */
+ public static final int CALL_BARRED = 240;
+ /** The call is blocked by the Fixed Dialing Number list */
+ public static final int FDN_BLOCKED = 241;
+ /** The given IMSI is not known at the VLR */
+ /** TS 24.008 cause 4 */
+ public static final int IMSI_UNKNOWN_IN_VLR = 242;
+ /**
+ * The network does not accept emergency call establishment using an IMEI or not accept attach
+ * procedure for emergency services using an IMEI
+ */
+ public static final int IMEI_NOT_ACCEPTED = 243;
+ /** MS is locked until next power cycle */
+ public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 1000;
+ /** Drop call*/
+ public static final int CDMA_DROP = 1001;
+ /** INTERCEPT order received, MS state idle entered */
+ public static final int CDMA_INTERCEPT = 1002;
+ /** MS has been redirected, call is cancelled */
+ public static final int CDMA_REORDER = 1003;
+ /** Service option rejection */
+ public static final int CDMA_SO_REJECT = 1004;
+ /** Requested service is rejected, retry delay is set */
+ public static final int CDMA_RETRY_ORDER = 1005;
+ /** Unable to obtain access to the CDMA system */
+ public static final int CDMA_ACCESS_FAILURE = 1006;
+ /** Not a preempted call */
+ public static final int CDMA_PREEMPTED = 1007;
+ /** Not an emergency call */
+ public static final int CDMA_NOT_EMERGENCY = 1008;
+ /** Access Blocked by CDMA network */
+ public static final int CDMA_ACCESS_BLOCKED = 1009;
+ /** Disconnected due to unspecified reasons */
+ public static final int ERROR_UNSPECIFIED = 0xffff;
+
+ /** Private constructor to avoid class instantiation. */
+ private PreciseDisconnectCause() {
+ // Do nothing.
+ }
+}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 8f17e72..ea22bc4 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -211,6 +211,267 @@
*/
public static final String EXTRA_INCOMING_NUMBER = "incoming_number";
+ /**
+ * Broadcast intent action indicating that a precise call state
+ * (cellular) on the device has changed.
+ *
+ * <p>
+ * The {@link #EXTRA_RINGING_CALL_STATE} extra indicates the ringing call state.
+ * The {@link #EXTRA_FOREGROUND_CALL_STATE} extra indicates the foreground call state.
+ * The {@link #EXTRA_BACKGROUND_CALL_STATE} extra indicates the background call state.
+ * The {@link #EXTRA_DISCONNECT_CAUSE} extra indicates the disconnect cause.
+ * The {@link #EXTRA_PRECISE_DISCONNECT_CAUSE} extra indicates the precise disconnect cause.
+ *
+ * <p class="note">
+ * Requires the READ_PRECISE_PHONE_STATE permission.
+ *
+ * @see #EXTRA_RINGING_CALL_STATE
+ * @see #EXTRA_FOREGROUND_CALL_STATE
+ * @see #EXTRA_BACKGROUND_CALL_STATE
+ * @see #EXTRA_DISCONNECT_CAUSE
+ * @see #EXTRA_PRECISE_DISCONNECT_CAUSE
+ *
+ * <p class="note">
+ * Requires the READ_PRECISE_PHONE_STATE permission.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_PRECISE_CALL_STATE_CHANGED =
+ "android.intent.action.PRECISE_CALL_STATE";
+
+ /**
+ * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast
+ * for an integer containing the state of the current ringing call.
+ *
+ * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
+ * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
+ * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE
+ * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING
+ * @see PreciseCallState#PRECISE_CALL_STATE_DIALING
+ * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING
+ * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING
+ * @see PreciseCallState#PRECISE_CALL_STATE_WAITING
+ * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED
+ * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING
+ *
+ * <p class="note">
+ * Retrieve with
+ * {@link android.content.Intent#getIntExtra(String name, int defaultValue)}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_RINGING_CALL_STATE = "ringing_state";
+
+ /**
+ * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast
+ * for an integer containing the state of the current foreground call.
+ *
+ * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
+ * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
+ * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE
+ * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING
+ * @see PreciseCallState#PRECISE_CALL_STATE_DIALING
+ * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING
+ * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING
+ * @see PreciseCallState#PRECISE_CALL_STATE_WAITING
+ * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED
+ * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING
+ *
+ * <p class="note">
+ * Retrieve with
+ * {@link android.content.Intent#getIntExtra(String name, int defaultValue)}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_FOREGROUND_CALL_STATE = "foreground_state";
+
+ /**
+ * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast
+ * for an integer containing the state of the current background call.
+ *
+ * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
+ * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
+ * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE
+ * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING
+ * @see PreciseCallState#PRECISE_CALL_STATE_DIALING
+ * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING
+ * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING
+ * @see PreciseCallState#PRECISE_CALL_STATE_WAITING
+ * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED
+ * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING
+ *
+ * <p class="note">
+ * Retrieve with
+ * {@link android.content.Intent#getIntExtra(String name, int defaultValue)}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_BACKGROUND_CALL_STATE = "background_state";
+
+ /**
+ * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast
+ * for an integer containing the disconnect cause.
+ *
+ * @see DisconnectCause
+ *
+ * <p class="note">
+ * Retrieve with
+ * {@link android.content.Intent#getIntExtra(String name, int defaultValue)}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_DISCONNECT_CAUSE = "disconnect_cause";
+
+ /**
+ * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast
+ * for an integer containing the disconnect cause provided by the RIL.
+ *
+ * @see PreciseDisconnectCause
+ *
+ * <p class="note">
+ * Retrieve with
+ * {@link android.content.Intent#getIntExtra(String name, int defaultValue)}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_PRECISE_DISCONNECT_CAUSE = "precise_disconnect_cause";
+
+ /**
+ * Broadcast intent action indicating a data connection has changed,
+ * providing precise information about the connection.
+ *
+ * <p>
+ * The {@link #EXTRA_DATA_STATE} extra indicates the connection state.
+ * The {@link #EXTRA_DATA_NETWORK_TYPE} extra indicates the connection network type.
+ * The {@link #EXTRA_DATA_APN_TYPE} extra indicates the APN type.
+ * The {@link #EXTRA_DATA_APN} extra indicates the APN.
+ * The {@link #EXTRA_DATA_CHANGE_REASON} extra indicates the connection change reason.
+ * The {@link #EXTRA_DATA_IFACE_PROPERTIES} extra indicates the connection interface.
+ * The {@link #EXTRA_DATA_FAILURE_CAUSE} extra indicates the connection fail cause.
+ *
+ * <p class="note">
+ * Requires the READ_PRECISE_PHONE_STATE permission.
+ *
+ * @see #EXTRA_DATA_STATE
+ * @see #EXTRA_DATA_NETWORK_TYPE
+ * @see #EXTRA_DATA_APN_TYPE
+ * @see #EXTRA_DATA_APN
+ * @see #EXTRA_DATA_CHANGE_REASON
+ * @see #EXTRA_DATA_IFACE
+ * @see #EXTRA_DATA_FAILURE_CAUSE
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED =
+ "android.intent.action.PRECISE_DATA_CONNECTION_STATE_CHANGED";
+
+ /**
+ * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
+ * for an integer containing the state of the current data connection.
+ *
+ * @see TelephonyManager#DATA_UNKNOWN
+ * @see TelephonyManager#DATA_DISCONNECTED
+ * @see TelephonyManager#DATA_CONNECTING
+ * @see TelephonyManager#DATA_CONNECTED
+ * @see TelephonyManager#DATA_SUSPENDED
+ *
+ * <p class="note">
+ * Retrieve with
+ * {@link android.content.Intent#getIntExtra(String name, int defaultValue)}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_DATA_STATE = PhoneConstants.STATE_KEY;
+
+ /**
+ * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
+ * for an integer containing the network type.
+ *
+ * @see TelephonyManager#NETWORK_TYPE_UNKNOWN
+ * @see TelephonyManager#NETWORK_TYPE_GPRS
+ * @see TelephonyManager#NETWORK_TYPE_EDGE
+ * @see TelephonyManager#NETWORK_TYPE_UMTS
+ * @see TelephonyManager#NETWORK_TYPE_CDMA
+ * @see TelephonyManager#NETWORK_TYPE_EVDO_0
+ * @see TelephonyManager#NETWORK_TYPE_EVDO_A
+ * @see TelephonyManager#NETWORK_TYPE_1xRTT
+ * @see TelephonyManager#NETWORK_TYPE_HSDPA
+ * @see TelephonyManager#NETWORK_TYPE_HSUPA
+ * @see TelephonyManager#NETWORK_TYPE_HSPA
+ * @see TelephonyManager#NETWORK_TYPE_IDEN
+ * @see TelephonyManager#NETWORK_TYPE_EVDO_B
+ * @see TelephonyManager#NETWORK_TYPE_LTE
+ * @see TelephonyManager#NETWORK_TYPE_EHRPD
+ * @see TelephonyManager#NETWORK_TYPE_HSPAP
+ *
+ * <p class="note">
+ * Retrieve with
+ * {@link android.content.Intent#getIntExtra(String name, int defaultValue)}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_DATA_NETWORK_TYPE = PhoneConstants.DATA_NETWORK_TYPE_KEY;
+
+ /**
+ * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
+ * for an String containing the data APN type.
+ *
+ * <p class="note">
+ * Retrieve with
+ * {@link android.content.Intent#getStringExtra(String name)}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_DATA_APN_TYPE = PhoneConstants.DATA_APN_TYPE_KEY;
+
+ /**
+ * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
+ * for an String containing the data APN.
+ *
+ * <p class="note">
+ * Retrieve with
+ * {@link android.content.Intent#getStringExtra(String name)}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_DATA_APN = PhoneConstants.DATA_APN_KEY;
+
+ /**
+ * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
+ * for an String representation of the change reason.
+ *
+ * <p class="note">
+ * Retrieve with
+ * {@link android.content.Intent#getStringExtra(String name)}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_DATA_CHANGE_REASON = PhoneConstants.STATE_CHANGE_REASON_KEY;
+
+ /**
+ * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
+ * for an String representation of the data interface.
+ *
+ * <p class="note">
+ * Retrieve with
+ * {@link android.content.Intent#getParcelableExtra(String name)}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_DATA_LINK_PROPERTIES_KEY = PhoneConstants.DATA_LINK_PROPERTIES_KEY;
+
+ /**
+ * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
+ * for the data connection fail cause.
+ *
+ * <p class="note">
+ * Retrieve with
+ * {@link android.content.Intent#getStringExtra(String name)}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_DATA_FAILURE_CAUSE = PhoneConstants.DATA_FAILURE_CAUSE_KEY;
//
//
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 4be11b8..59bdf64 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -97,6 +97,7 @@
public static final int CMD_ENABLE_MOBILE_PROVISIONING = BASE + 37;
public static final int CMD_IS_PROVISIONING_APN = BASE + 38;
public static final int EVENT_PROVISIONING_APN_ALARM = BASE + 39;
+ public static final int CMD_NET_STAT_POLL = BASE + 40;
/***** Constants *****/
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 3a04ceb..f228d4e 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -20,6 +20,8 @@
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.CellInfo;
+import android.telephony.PreciseCallState;
+import android.telephony.PreciseDataConnectionState;
oneway interface IPhoneStateListener {
void onServiceStateChanged(in ServiceState serviceState);
@@ -35,5 +37,7 @@
void onSignalStrengthsChanged(in SignalStrength signalStrength);
void onOtaspChanged(in int otaspMode);
void onCellInfoChanged(in List<CellInfo> cellInfo);
+ void onPreciseCallStateChanged(in PreciseCallState callState);
+ void onPreciseDataConnectionStateChanged(in PreciseDataConnectionState dataConnectionState);
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 59c8472..546ce17 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -41,4 +41,9 @@
void notifyCellLocation(in Bundle cellLocation);
void notifyOtaspChanged(in int otaspMode);
void notifyCellInfo(in List<CellInfo> cellInfo);
+ void notifyPreciseCallState(int ringingCallState, int foregroundCallState,
+ int backgroundCallState);
+ void notifyDisconnectCause(int disconnectCause, int preciseDisconnectCause);
+ void notifyPreciseDataConnectionFailed(String reason, String apnType, String apn,
+ String failCause);
}
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index 4163255..1fed417d 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -73,6 +73,8 @@
public static final String PHONE_NAME_KEY = "phoneName";
public static final String FAILURE_REASON_KEY = "reason";
public static final String STATE_CHANGE_REASON_KEY = "reason";
+ public static final String DATA_NETWORK_TYPE_KEY = "networkType";
+ public static final String DATA_FAILURE_CAUSE_KEY = "failCause";
public static final String DATA_APN_TYPE_KEY = "apnType";
public static final String DATA_APN_KEY = "apn";
public static final String DATA_LINK_PROPERTIES_KEY = "linkProperties";
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index ebf4538..19532e8 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -149,205 +149,506 @@
// =========================================================================
// =========================================================================
-status_t
-AaptGroupEntry::parseNamePart(const String8& part, int* axis, uint32_t* value)
+/* static */ void AaptLocaleValue::splitAndLowerCase(const char* const chars,
+ Vector<String8>* parts, const char separator) {
+ const char *p = chars;
+ const char *q;
+ while (NULL != (q = strchr(p, separator))) {
+ String8 val(p, q - p);
+ val.toLower();
+ parts->add(val);
+ p = q+1;
+ }
+
+ if (p < chars + strlen(chars)) {
+ String8 val(p);
+ val.toLower();
+ parts->add(val);
+ }
+}
+
+/* static */
+inline bool isAlpha(const String8& string) {
+ const size_t length = string.length();
+ for (size_t i = 0; i < length; ++i) {
+ if (!isalpha(string[i])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/* static */
+inline bool isNumber(const String8& string) {
+ const size_t length = string.length();
+ for (size_t i = 0; i < length; ++i) {
+ if (!isdigit(string[i])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void AaptLocaleValue::setLanguage(const char* languageChars) {
+ size_t i = 0;
+ while ((*languageChars) != '\0') {
+ language[i++] = tolower(*languageChars);
+ languageChars++;
+ }
+}
+
+void AaptLocaleValue::setRegion(const char* regionChars) {
+ size_t i = 0;
+ while ((*regionChars) != '\0') {
+ region[i++] = toupper(*regionChars);
+ regionChars++;
+ }
+}
+
+void AaptLocaleValue::setScript(const char* scriptChars) {
+ size_t i = 0;
+ while ((*scriptChars) != '\0') {
+ if (i == 0) {
+ script[i++] = toupper(*scriptChars);
+ } else {
+ script[i++] = tolower(*scriptChars);
+ }
+ scriptChars++;
+ }
+}
+
+void AaptLocaleValue::setVariant(const char* variantChars) {
+ size_t i = 0;
+ while ((*variantChars) != '\0') {
+ variant[i++] = *variantChars;
+ variantChars++;
+ }
+}
+
+bool AaptLocaleValue::initFromFilterString(const String8& str) {
+ // A locale (as specified in the filter) is an underscore separated name such
+ // as "en_US", "en_Latn_US", or "en_US_POSIX".
+ Vector<String8> parts;
+ splitAndLowerCase(str.string(), &parts, '_');
+
+ const int numTags = parts.size();
+ bool valid = false;
+ if (numTags >= 1) {
+ const String8& lang = parts[0];
+ if (isAlpha(lang) && (lang.length() == 2 || lang.length() == 3)) {
+ setLanguage(lang.string());
+ valid = true;
+ }
+ }
+
+ if (!valid || numTags == 1) {
+ return valid;
+ }
+
+ // At this point, valid == true && numTags > 1.
+ const String8& part2 = parts[1];
+ if ((part2.length() == 2 && isAlpha(part2)) ||
+ (part2.length() == 3 && isNumber(part2))) {
+ setRegion(part2.string());
+ } else if (part2.length() == 4 && isAlpha(part2)) {
+ setScript(part2.string());
+ } else if (part2.length() >= 5 && part2.length() <= 8) {
+ setVariant(part2.string());
+ } else {
+ valid = false;
+ }
+
+ if (!valid || numTags == 2) {
+ return valid;
+ }
+
+ // At this point, valid == true && numTags > 1.
+ const String8& part3 = parts[2];
+ if (((part3.length() == 2 && isAlpha(part3)) ||
+ (part3.length() == 3 && isNumber(part3))) && script[0]) {
+ setRegion(part3.string());
+ } else if (part3.length() >= 5 && part3.length() <= 8) {
+ setVariant(part3.string());
+ } else {
+ valid = false;
+ }
+
+ if (!valid || numTags == 3) {
+ return valid;
+ }
+
+ const String8& part4 = parts[3];
+ if (part4.length() >= 5 && part4.length() <= 8) {
+ setVariant(part4.string());
+ } else {
+ valid = false;
+ }
+
+ if (!valid || numTags > 4) {
+ return false;
+ }
+
+ return true;
+}
+
+int AaptLocaleValue::initFromDirName(const Vector<String8>& parts, const int startIndex) {
+ const int size = parts.size();
+ int currentIndex = startIndex;
+
+ String8 part = parts[currentIndex];
+ if (part[0] == 'b' && part[1] == '+') {
+ // This is a "modified" BCP-47 language tag. Same semantics as BCP-47 tags,
+ // except that the separator is "+" and not "-".
+ Vector<String8> subtags;
+ AaptLocaleValue::splitAndLowerCase(part.string(), &subtags, '+');
+ subtags.removeItemsAt(0);
+ if (subtags.size() == 1) {
+ setLanguage(subtags[0]);
+ } else if (subtags.size() == 2) {
+ setLanguage(subtags[0]);
+
+ // The second tag can either be a region, a variant or a script.
+ switch (subtags[1].size()) {
+ case 2:
+ case 3:
+ setRegion(subtags[1]);
+ break;
+ case 4:
+ setScript(subtags[1]);
+ break;
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ setVariant(subtags[1]);
+ break;
+ default:
+ fprintf(stderr, "ERROR: Invalid BCP-47 tag in directory name %s\n",
+ part.string());
+ return -1;
+ }
+ } else if (subtags.size() == 3) {
+ // The language is always the first subtag.
+ setLanguage(subtags[0]);
+
+ // The second subtag can either be a script or a region code.
+ // If its size is 4, it's a script code, else it's a region code.
+ bool hasRegion = false;
+ if (subtags[1].size() == 4) {
+ setScript(subtags[1]);
+ } else if (subtags[1].size() == 2 || subtags[1].size() == 3) {
+ setRegion(subtags[1]);
+ hasRegion = true;
+ } else {
+ fprintf(stderr, "ERROR: Invalid BCP-47 tag in directory name %s\n", part.string());
+ return -1;
+ }
+
+ // The third tag can either be a region code (if the second tag was
+ // a script), else a variant code.
+ if (subtags[2].size() > 4) {
+ setVariant(subtags[2]);
+ } else {
+ setRegion(subtags[2]);
+ }
+ } else if (subtags.size() == 4) {
+ setLanguage(subtags[0]);
+ setScript(subtags[1]);
+ setRegion(subtags[2]);
+ setVariant(subtags[3]);
+ } else {
+ fprintf(stderr, "ERROR: Invalid BCP-47 tag in directory name: %s\n", part.string());
+ return -1;
+ }
+
+ return ++currentIndex;
+ } else {
+ if ((part.length() == 2 || part.length() == 3) && isAlpha(part)) {
+ setLanguage(part);
+ if (++currentIndex == size) {
+ return size;
+ }
+ } else {
+ return currentIndex;
+ }
+
+ part = parts[currentIndex];
+ if (part.string()[0] == 'r' && part.length() == 3) {
+ setRegion(part.string() + 1);
+ if (++currentIndex == size) {
+ return size;
+ }
+ }
+ }
+
+ return currentIndex;
+}
+
+
+String8 AaptLocaleValue::toDirName() const {
+ String8 dirName("");
+ if (language[0]) {
+ dirName += language;
+ } else {
+ return dirName;
+ }
+
+ if (script[0]) {
+ dirName += "-s";
+ dirName += script;
+ }
+
+ if (region[0]) {
+ dirName += "-r";
+ dirName += region;
+ }
+
+ if (variant[0]) {
+ dirName += "-v";
+ dirName += variant;
+ }
+
+ return dirName;
+}
+
+void AaptLocaleValue::initFromResTable(const ResTable_config& config) {
+ config.unpackLanguage(language);
+ config.unpackRegion(region);
+ if (config.localeScript[0]) {
+ memcpy(script, config.localeScript, sizeof(config.localeScript));
+ }
+
+ if (config.localeVariant[0]) {
+ memcpy(variant, config.localeVariant, sizeof(config.localeVariant));
+ }
+}
+
+void AaptLocaleValue::writeTo(ResTable_config* out) const {
+ out->packLanguage(language);
+ out->packRegion(region);
+
+ if (script[0]) {
+ memcpy(out->localeScript, script, sizeof(out->localeScript));
+ }
+
+ if (variant[0]) {
+ memcpy(out->localeVariant, variant, sizeof(out->localeVariant));
+ }
+}
+
+
+/* static */ bool
+AaptGroupEntry::parseFilterNamePart(const String8& part, int* axis, AxisValue* value)
{
ResTable_config config;
+ memset(&config, 0, sizeof(ResTable_config));
// IMSI - MCC
if (getMccName(part.string(), &config)) {
*axis = AXIS_MCC;
- *value = config.mcc;
- return 0;
+ value->intValue = config.mcc;
+ return true;
}
// IMSI - MNC
if (getMncName(part.string(), &config)) {
*axis = AXIS_MNC;
- *value = config.mnc;
- return 0;
+ value->intValue = config.mnc;
+ return true;
}
// locale - language
- if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) {
- *axis = AXIS_LANGUAGE;
- *value = part[1] << 8 | part[0];
- return 0;
- }
-
- // locale - language_REGION
- if (part.length() == 5 && isalpha(part[0]) && isalpha(part[1])
- && part[2] == '_' && isalpha(part[3]) && isalpha(part[4])) {
- *axis = AXIS_LANGUAGE;
- *value = (part[4] << 24) | (part[3] << 16) | (part[1] << 8) | (part[0]);
- return 0;
+ if (value->localeValue.initFromFilterString(part)) {
+ *axis = AXIS_LOCALE;
+ return true;
}
// layout direction
if (getLayoutDirectionName(part.string(), &config)) {
*axis = AXIS_LAYOUTDIR;
- *value = (config.screenLayout&ResTable_config::MASK_LAYOUTDIR);
- return 0;
+ value->intValue = (config.screenLayout&ResTable_config::MASK_LAYOUTDIR);
+ return true;
}
// smallest screen dp width
if (getSmallestScreenWidthDpName(part.string(), &config)) {
*axis = AXIS_SMALLESTSCREENWIDTHDP;
- *value = config.smallestScreenWidthDp;
- return 0;
+ value->intValue = config.smallestScreenWidthDp;
+ return true;
}
// screen dp width
if (getScreenWidthDpName(part.string(), &config)) {
*axis = AXIS_SCREENWIDTHDP;
- *value = config.screenWidthDp;
- return 0;
+ value->intValue = config.screenWidthDp;
+ return true;
}
// screen dp height
if (getScreenHeightDpName(part.string(), &config)) {
*axis = AXIS_SCREENHEIGHTDP;
- *value = config.screenHeightDp;
- return 0;
+ value->intValue = config.screenHeightDp;
+ return true;
}
// screen layout size
if (getScreenLayoutSizeName(part.string(), &config)) {
*axis = AXIS_SCREENLAYOUTSIZE;
- *value = (config.screenLayout&ResTable_config::MASK_SCREENSIZE);
- return 0;
+ value->intValue = (config.screenLayout&ResTable_config::MASK_SCREENSIZE);
+ return true;
}
// screen layout long
if (getScreenLayoutLongName(part.string(), &config)) {
*axis = AXIS_SCREENLAYOUTLONG;
- *value = (config.screenLayout&ResTable_config::MASK_SCREENLONG);
- return 0;
+ value->intValue = (config.screenLayout&ResTable_config::MASK_SCREENLONG);
+ return true;
}
// orientation
if (getOrientationName(part.string(), &config)) {
*axis = AXIS_ORIENTATION;
- *value = config.orientation;
- return 0;
+ value->intValue = config.orientation;
+ return true;
}
// ui mode type
if (getUiModeTypeName(part.string(), &config)) {
*axis = AXIS_UIMODETYPE;
- *value = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
- return 0;
+ value->intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
+ return true;
}
// ui mode night
if (getUiModeNightName(part.string(), &config)) {
*axis = AXIS_UIMODENIGHT;
- *value = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
- return 0;
+ value->intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
+ return true;
}
// density
if (getDensityName(part.string(), &config)) {
*axis = AXIS_DENSITY;
- *value = config.density;
- return 0;
+ value->intValue = config.density;
+ return true;
}
// touchscreen
if (getTouchscreenName(part.string(), &config)) {
*axis = AXIS_TOUCHSCREEN;
- *value = config.touchscreen;
- return 0;
+ value->intValue = config.touchscreen;
+ return true;
}
// keyboard hidden
if (getKeysHiddenName(part.string(), &config)) {
*axis = AXIS_KEYSHIDDEN;
- *value = config.inputFlags;
- return 0;
+ value->intValue = config.inputFlags;
+ return true;
}
// keyboard
if (getKeyboardName(part.string(), &config)) {
*axis = AXIS_KEYBOARD;
- *value = config.keyboard;
- return 0;
+ value->intValue = config.keyboard;
+ return true;
}
// navigation hidden
if (getNavHiddenName(part.string(), &config)) {
*axis = AXIS_NAVHIDDEN;
- *value = config.inputFlags;
+ value->intValue = config.inputFlags;
return 0;
}
// navigation
if (getNavigationName(part.string(), &config)) {
*axis = AXIS_NAVIGATION;
- *value = config.navigation;
- return 0;
+ value->intValue = config.navigation;
+ return true;
}
// screen size
if (getScreenSizeName(part.string(), &config)) {
*axis = AXIS_SCREENSIZE;
- *value = config.screenSize;
- return 0;
+ value->intValue = config.screenSize;
+ return true;
}
// version
if (getVersionName(part.string(), &config)) {
*axis = AXIS_VERSION;
- *value = config.version;
- return 0;
+ value->intValue = config.version;
+ return true;
}
- return 1;
+ return false;
}
-uint32_t
+AxisValue
AaptGroupEntry::getConfigValueForAxis(const ResTable_config& config, int axis)
{
+ AxisValue value;
switch (axis) {
case AXIS_MCC:
- return config.mcc;
+ value.intValue = config.mcc;
+ break;
case AXIS_MNC:
- return config.mnc;
- case AXIS_LANGUAGE:
- return (((uint32_t)config.country[1]) << 24) | (((uint32_t)config.country[0]) << 16)
- | (((uint32_t)config.language[1]) << 8) | (config.language[0]);
+ value.intValue = config.mnc;
+ break;
+ case AXIS_LOCALE:
+ value.localeValue.initFromResTable(config);
+ break;
case AXIS_LAYOUTDIR:
- return config.screenLayout&ResTable_config::MASK_LAYOUTDIR;
+ value.intValue = config.screenLayout&ResTable_config::MASK_LAYOUTDIR;
+ break;
case AXIS_SCREENLAYOUTSIZE:
- return config.screenLayout&ResTable_config::MASK_SCREENSIZE;
+ value.intValue = config.screenLayout&ResTable_config::MASK_SCREENSIZE;
+ break;
case AXIS_ORIENTATION:
- return config.orientation;
+ value.intValue = config.orientation;
+ break;
case AXIS_UIMODETYPE:
- return (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
+ value.intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
+ break;
case AXIS_UIMODENIGHT:
- return (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
+ value.intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
+ break;
case AXIS_DENSITY:
- return config.density;
+ value.intValue = config.density;
+ break;
case AXIS_TOUCHSCREEN:
- return config.touchscreen;
+ value.intValue = config.touchscreen;
+ break;
case AXIS_KEYSHIDDEN:
- return config.inputFlags;
+ value.intValue = config.inputFlags;
+ break;
case AXIS_KEYBOARD:
- return config.keyboard;
+ value.intValue = config.keyboard;
+ break;
case AXIS_NAVIGATION:
- return config.navigation;
+ value.intValue = config.navigation;
+ break;
case AXIS_SCREENSIZE:
- return config.screenSize;
+ value.intValue = config.screenSize;
+ break;
case AXIS_SMALLESTSCREENWIDTHDP:
- return config.smallestScreenWidthDp;
+ value.intValue = config.smallestScreenWidthDp;
+ break;
case AXIS_SCREENWIDTHDP:
- return config.screenWidthDp;
+ value.intValue = config.screenWidthDp;
+ break;
case AXIS_SCREENHEIGHTDP:
- return config.screenHeightDp;
+ value.intValue = config.screenHeightDp;
+ break;
case AXIS_VERSION:
- return config.version;
+ value.intValue = config.version;
+ break;
}
- return 0;
+
+ return value;
}
bool
@@ -371,24 +672,14 @@
mParamsChanged = true;
Vector<String8> parts;
+ AaptLocaleValue::splitAndLowerCase(dir, &parts, '-');
- String8 mcc, mnc, loc, layoutsize, layoutlong, orient, den;
+ String8 mcc, mnc, layoutsize, layoutlong, orient, den;
String8 touch, key, keysHidden, nav, navHidden, size, layoutDir, vers;
String8 uiModeType, uiModeNight, smallestwidthdp, widthdp, heightdp;
- const char *p = dir;
- const char *q;
- while (NULL != (q = strchr(p, '-'))) {
- String8 val(p, q-p);
- val.toLower();
- parts.add(val);
- //printf("part: %s\n", parts[parts.size()-1].string());
- p = q+1;
- }
- String8 val(p);
- val.toLower();
- parts.add(val);
- //printf("part: %s\n", parts[parts.size()-1].string());
+ AaptLocaleValue locale;
+ int numLocaleComponents = 0;
const int N = parts.size();
int index = 0;
@@ -429,38 +720,18 @@
}
part = parts[index];
} else {
- //printf("not mcc: %s\n", part.string());
+ //printf("not mnc: %s\n", part.string());
}
- // locale - language
- if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) {
- loc = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not language: %s\n", part.string());
+ index = locale.initFromDirName(parts, index);
+ if (index == -1) {
+ return false;
+ }
+ if (index >= N){
+ goto success;
}
- // locale - region
- if (loc.length() > 0
- && part.length() == 3 && part[0] == 'r' && part[0] && part[1]) {
- loc += "-";
- part.toUpper();
- loc += part.string() + 1;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not region: %s\n", part.string());
- }
-
+ part = parts[index];
if (getLayoutDirectionName(part.string())) {
layoutDir = part;
@@ -679,7 +950,7 @@
success:
this->mcc = mcc;
this->mnc = mnc;
- this->locale = loc;
+ this->locale = locale;
this->screenLayoutSize = layoutsize;
this->screenLayoutLong = layoutlong;
this->smallestScreenWidthDp = smallestwidthdp;
@@ -711,7 +982,7 @@
s += ",";
s += this->mnc;
s += ",";
- s += this->locale;
+ s += locale.toDirName();
s += ",";
s += layoutDirection;
s += ",";
@@ -765,12 +1036,15 @@
}
s += mnc;
}
- if (this->locale != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += locale;
+
+ const String8 localeComponent = locale.toDirName();
+ if (localeComponent != "") {
+ if (s.length() > 0) {
+ s += "-";
+ }
+ s += localeComponent;
}
+
if (this->layoutDirection != "") {
if (s.length() > 0) {
s += "-";
@@ -942,55 +1216,6 @@
return true;
}
-/*
- * Does this directory name fit the pattern of a locale dir ("en-rUS" or
- * "default")?
- *
- * TODO: Should insist that the first two letters are lower case, and the
- * second two are upper.
- */
-bool AaptGroupEntry::getLocaleName(const char* fileName,
- ResTable_config* out)
-{
- if (strcmp(fileName, kWildcardName) == 0
- || strcmp(fileName, kDefaultLocale) == 0) {
- if (out) {
- out->language[0] = 0;
- out->language[1] = 0;
- out->country[0] = 0;
- out->country[1] = 0;
- }
- return true;
- }
-
- if (strlen(fileName) == 2 && isalpha(fileName[0]) && isalpha(fileName[1])) {
- if (out) {
- out->language[0] = fileName[0];
- out->language[1] = fileName[1];
- out->country[0] = 0;
- out->country[1] = 0;
- }
- return true;
- }
-
- if (strlen(fileName) == 5 &&
- isalpha(fileName[0]) &&
- isalpha(fileName[1]) &&
- fileName[2] == '-' &&
- isalpha(fileName[3]) &&
- isalpha(fileName[4])) {
- if (out) {
- out->language[0] = fileName[0];
- out->language[1] = fileName[1];
- out->country[0] = fileName[3];
- out->country[1] = fileName[4];
- }
- return true;
- }
-
- return false;
-}
-
bool AaptGroupEntry::getLayoutDirectionName(const char* name, ResTable_config* out)
{
if (strcmp(name, kWildcardName) == 0) {
@@ -1496,18 +1721,18 @@
return v;
}
-const ResTable_config& AaptGroupEntry::toParams() const
+const ResTable_config AaptGroupEntry::toParams() const
{
if (!mParamsChanged) {
return mParams;
}
mParamsChanged = false;
- ResTable_config& params(mParams);
- memset(¶ms, 0, sizeof(params));
+ ResTable_config& params = mParams;
+ memset(¶ms, 0, sizeof(ResTable_config));
getMccName(mcc.string(), ¶ms);
getMncName(mnc.string(), ¶ms);
- getLocaleName(locale.string(), ¶ms);
+ locale.writeTo(¶ms);
getLayoutDirectionName(layoutDirection.string(), ¶ms);
getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), ¶ms);
getScreenWidthDpName(screenWidthDp.string(), ¶ms);
@@ -1982,7 +2207,9 @@
AaptAssets::AaptAssets()
: AaptDir(String8(), String8()),
- mChanged(false), mHaveIncludedAssets(false), mRes(NULL)
+ mHavePrivateSymbols(false),
+ mChanged(false), mHaveIncludedAssets(false),
+ mRes(NULL)
{
}
@@ -2491,9 +2718,9 @@
// If our preferred density is hdpi but we only have mdpi and xhdpi resources, we
// pick xhdpi.
uint32_t preferredDensity = 0;
- const SortedVector<uint32_t>* preferredConfigs = prefFilter.configsForAxis(AXIS_DENSITY);
+ const SortedVector<AxisValue>* preferredConfigs = prefFilter.configsForAxis(AXIS_DENSITY);
if (preferredConfigs != NULL && preferredConfigs->size() > 0) {
- preferredDensity = (*preferredConfigs)[0];
+ preferredDensity = (*preferredConfigs)[0].intValue;
}
// Now deal with preferred configurations.
diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h
index 5cfa913..9733b6d 100644
--- a/tools/aapt/AaptAssets.h
+++ b/tools/aapt/AaptAssets.h
@@ -13,7 +13,6 @@
#include <utils/RefBase.h>
#include <utils/SortedVector.h>
#include <utils/String8.h>
-#include <utils/String8.h>
#include <utils/Vector.h>
#include "ZipFile.h"
@@ -34,8 +33,7 @@
AXIS_NONE = 0,
AXIS_MCC = 1,
AXIS_MNC,
- AXIS_LANGUAGE,
- AXIS_REGION,
+ AXIS_LOCALE,
AXIS_SCREENLAYOUTSIZE,
AXIS_SCREENLAYOUTLONG,
AXIS_ORIENTATION,
@@ -58,6 +56,73 @@
AXIS_END = AXIS_VERSION,
};
+struct AaptLocaleValue {
+ char language[4];
+ char region[4];
+ char script[4];
+ char variant[8];
+
+ AaptLocaleValue() {
+ memset(this, 0, sizeof(AaptLocaleValue));
+ }
+
+ // Initialize this AaptLocaleValue from a config string.
+ bool initFromFilterString(const String8& config);
+
+ int initFromDirName(const Vector<String8>& parts, const int startIndex);
+
+ // Initialize this AaptLocaleValue from a ResTable_config.
+ void initFromResTable(const ResTable_config& config);
+
+ void writeTo(ResTable_config* out) const;
+
+ String8 toDirName() const;
+
+ int compare(const AaptLocaleValue& other) const {
+ return memcmp(this, &other, sizeof(AaptLocaleValue));
+ }
+
+ static void splitAndLowerCase(const char* const chars, Vector<String8>* parts,
+ const char separator);
+
+ inline bool operator<(const AaptLocaleValue& o) const { return compare(o) < 0; }
+ inline bool operator<=(const AaptLocaleValue& o) const { return compare(o) <= 0; }
+ inline bool operator==(const AaptLocaleValue& o) const { return compare(o) == 0; }
+ inline bool operator!=(const AaptLocaleValue& o) const { return compare(o) != 0; }
+ inline bool operator>=(const AaptLocaleValue& o) const { return compare(o) >= 0; }
+ inline bool operator>(const AaptLocaleValue& o) const { return compare(o) > 0; }
+private:
+ void setLanguage(const char* language);
+ void setRegion(const char* language);
+ void setScript(const char* script);
+ void setVariant(const char* variant);
+};
+
+struct AxisValue {
+ // Used for all axes except AXIS_LOCALE, which is represented
+ // as a AaptLocaleValue value.
+ int intValue;
+ AaptLocaleValue localeValue;
+
+ AxisValue() : intValue(0) {
+ }
+
+ inline int compare(const AxisValue &other) const {
+ if (intValue != other.intValue) {
+ return intValue - other.intValue;
+ }
+
+ return localeValue.compare(other.localeValue);
+ }
+
+ inline bool operator<(const AxisValue& o) const { return compare(o) < 0; }
+ inline bool operator<=(const AxisValue& o) const { return compare(o) <= 0; }
+ inline bool operator==(const AxisValue& o) const { return compare(o) == 0; }
+ inline bool operator!=(const AxisValue& o) const { return compare(o) != 0; }
+ inline bool operator>=(const AxisValue& o) const { return compare(o) >= 0; }
+ inline bool operator>(const AxisValue& o) const { return compare(o) > 0; }
+};
+
/**
* This structure contains a specific variation of a single file out
* of all the variations it can have that we can have.
@@ -65,22 +130,38 @@
struct AaptGroupEntry
{
public:
- AaptGroupEntry() : mParamsChanged(true) { }
- AaptGroupEntry(const String8& _locale, const String8& _vendor)
- : locale(_locale), vendor(_vendor), mParamsChanged(true) { }
+ AaptGroupEntry() : mParamsChanged(true) {
+ memset(&mParams, 0, sizeof(ResTable_config));
+ }
bool initFromDirName(const char* dir, String8* resType);
- static status_t parseNamePart(const String8& part, int* axis, uint32_t* value);
+ static bool parseFilterNamePart(const String8& part, int* axis, AxisValue* value);
- static uint32_t getConfigValueForAxis(const ResTable_config& config, int axis);
+ static AxisValue getConfigValueForAxis(const ResTable_config& config, int axis);
static bool configSameExcept(const ResTable_config& config,
const ResTable_config& otherConfig, int axis);
+ int compare(const AaptGroupEntry& o) const;
+
+ const ResTable_config toParams() const;
+
+ inline bool operator<(const AaptGroupEntry& o) const { return compare(o) < 0; }
+ inline bool operator<=(const AaptGroupEntry& o) const { return compare(o) <= 0; }
+ inline bool operator==(const AaptGroupEntry& o) const { return compare(o) == 0; }
+ inline bool operator!=(const AaptGroupEntry& o) const { return compare(o) != 0; }
+ inline bool operator>=(const AaptGroupEntry& o) const { return compare(o) >= 0; }
+ inline bool operator>(const AaptGroupEntry& o) const { return compare(o) > 0; }
+
+ String8 toString() const;
+ String8 toDirName(const String8& resType) const;
+
+ const String8& getVersionString() const { return version; }
+
+private:
static bool getMccName(const char* name, ResTable_config* out = NULL);
static bool getMncName(const char* name, ResTable_config* out = NULL);
- static bool getLocaleName(const char* name, ResTable_config* out = NULL);
static bool getScreenLayoutSizeName(const char* name, ResTable_config* out = NULL);
static bool getScreenLayoutLongName(const char* name, ResTable_config* out = NULL);
static bool getOrientationName(const char* name, ResTable_config* out = NULL);
@@ -99,26 +180,9 @@
static bool getLayoutDirectionName(const char* name, ResTable_config* out = NULL);
static bool getVersionName(const char* name, ResTable_config* out = NULL);
- int compare(const AaptGroupEntry& o) const;
-
- const ResTable_config& toParams() const;
-
- inline bool operator<(const AaptGroupEntry& o) const { return compare(o) < 0; }
- inline bool operator<=(const AaptGroupEntry& o) const { return compare(o) <= 0; }
- inline bool operator==(const AaptGroupEntry& o) const { return compare(o) == 0; }
- inline bool operator!=(const AaptGroupEntry& o) const { return compare(o) != 0; }
- inline bool operator>=(const AaptGroupEntry& o) const { return compare(o) >= 0; }
- inline bool operator>(const AaptGroupEntry& o) const { return compare(o) > 0; }
-
- String8 toString() const;
- String8 toDirName(const String8& resType) const;
-
- const String8& getVersionString() const { return version; }
-
-private:
String8 mcc;
String8 mnc;
- String8 locale;
+ AaptLocaleValue locale;
String8 vendor;
String8 smallestScreenWidthDp;
String8 screenWidthDp;
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 8a6faed..c7cce96 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -517,6 +517,7 @@
// the API version because key resources like icons will have an implicit
// version if they are using newer config types like density.
ResTable_config config;
+ memset(&config, 0, sizeof(ResTable_config));
config.language[0] = 'e';
config.language[1] = 'n';
config.country[0] = 'U';
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 57d44f2..4d29ff7 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -80,6 +80,7 @@
ResourceDirIterator(const sp<ResourceTypeSet>& set, const String8& resType)
: mResType(resType), mSet(set), mSetPos(0), mGroupPos(0)
{
+ memset(&mParams, 0, sizeof(ResTable_config));
}
inline const sp<AaptGroup>& getGroup() const { return mGroup; }
diff --git a/tools/aapt/ResourceFilter.cpp b/tools/aapt/ResourceFilter.cpp
index 8cfd2a5..e8a2be4 100644
--- a/tools/aapt/ResourceFilter.cpp
+++ b/tools/aapt/ResourceFilter.cpp
@@ -28,8 +28,8 @@
mContainsPseudo = true;
}
int axis;
- uint32_t value;
- if (AaptGroupEntry::parseNamePart(part, &axis, &value)) {
+ AxisValue value;
+ if (!AaptGroupEntry::parseFilterNamePart(part, &axis, &value)) {
fprintf(stderr, "Invalid configuration: %s\n", arg);
fprintf(stderr, " ");
for (int i=0; i<p-arg; i++) {
@@ -44,15 +44,20 @@
ssize_t index = mData.indexOfKey(axis);
if (index < 0) {
- mData.add(axis, SortedVector<uint32_t>());
+ mData.add(axis, SortedVector<AxisValue>());
}
- SortedVector<uint32_t>& sv = mData.editValueFor(axis);
+ SortedVector<AxisValue>& sv = mData.editValueFor(axis);
sv.add(value);
- // if it's a locale with a region, also match an unmodified locale of the
- // same language
- if (axis == AXIS_LANGUAGE) {
- if (value & 0xffff0000) {
- sv.add(value & 0x0000ffff);
+
+ // If it's a locale with a region, script or variant, we should also match an
+ // unmodified locale of the same language
+ if (axis == AXIS_LOCALE) {
+ if (value.localeValue.region[0] || value.localeValue.script[0] ||
+ value.localeValue.variant[0]) {
+ AxisValue copy;
+ memcpy(copy.localeValue.language, value.localeValue.language,
+ sizeof(value.localeValue.language));
+ sv.add(copy);
}
}
p = q;
@@ -70,9 +75,9 @@
}
bool
-ResourceFilter::match(int axis, uint32_t value) const
+ResourceFilter::match(int axis, const AxisValue& value) const
{
- if (value == 0) {
+ if (value.intValue == 0 && (value.localeValue.language[0] == 0)) {
// they didn't specify anything so take everything
return true;
}
@@ -81,7 +86,7 @@
// we didn't request anything on this axis so take everything
return true;
}
- const SortedVector<uint32_t>& sv = mData.valueAt(index);
+ const SortedVector<AxisValue>& sv = mData.valueAt(index);
return sv.indexOf(value) >= 0;
}
@@ -102,7 +107,7 @@
return true;
}
-const SortedVector<uint32_t>* ResourceFilter::configsForAxis(int axis) const
+const SortedVector<AxisValue>* ResourceFilter::configsForAxis(int axis) const
{
ssize_t index = mData.indexOfKey(axis);
if (index < 0) {
diff --git a/tools/aapt/ResourceFilter.h b/tools/aapt/ResourceFilter.h
index 647b7bb..0d127ba 100644
--- a/tools/aapt/ResourceFilter.h
+++ b/tools/aapt/ResourceFilter.h
@@ -19,14 +19,15 @@
ResourceFilter() : mData(), mContainsPseudo(false) {}
status_t parse(const char* arg);
bool isEmpty() const;
- bool match(int axis, uint32_t value) const;
bool match(int axis, const ResTable_config& config) const;
bool match(const ResTable_config& config) const;
- const SortedVector<uint32_t>* configsForAxis(int axis) const;
+ const SortedVector<AxisValue>* configsForAxis(int axis) const;
inline bool containsPseudo() const { return mContainsPseudo; }
private:
- KeyedVector<int,SortedVector<uint32_t> > mData;
+ bool match(int axis, const AxisValue& value) const;
+
+ KeyedVector<int,SortedVector<AxisValue> > mData;
bool mContainsPseudo;
};
diff --git a/tools/aapt/ResourceIdCache.h b/tools/aapt/ResourceIdCache.h
index 65f7781..e6bcda2 100644
--- a/tools/aapt/ResourceIdCache.h
+++ b/tools/aapt/ResourceIdCache.h
@@ -7,7 +7,6 @@
#define RESOURCE_ID_CACHE_H
namespace android {
-class android::String16;
class ResourceIdCache {
public:
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 6ced8b3..38bf540 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -1292,8 +1292,8 @@
curIsStyled = true;
} else if (strcmp16(block.getElementName(&len), string16.string()) == 0) {
// Note the existence and locale of every string we process
- char rawLocale[16];
- curParams.getLocale(rawLocale);
+ char rawLocale[RESTABLE_MAX_LOCALE_LEN];
+ curParams.getBcp47Locale(rawLocale);
String8 locale(rawLocale);
String16 name;
String16 translatable;