diff --git a/Android.mk b/Android.mk index 0314084260e0..47af83d128ef 100644 --- a/Android.mk +++ b/Android.mk @@ -126,6 +126,7 @@ LOCAL_SRC_FILES += \ core/java/android/content/pm/IPackageMoveObserver.aidl \ core/java/android/content/pm/IPackageStatsObserver.aidl \ core/java/android/content/res/IThemeChangeListener.aidl \ + core/java/android/content/res/IThemeProcessingListener.aidl \ core/java/android/content/res/IThemeService.aidl \ core/java/android/database/IContentObserver.aidl \ core/java/android/hardware/ICameraService.aidl \ diff --git a/Gesture.aidl b/Gesture.aidl new file mode 100644 index 000000000000..4383ac051d8d --- /dev/null +++ b/Gesture.aidl @@ -0,0 +1,3 @@ +package android.gesture; + +parcelable Gesture; diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 8c7e69cbaba1..556f6cc5f6db 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -233,6 +233,18 @@ status_t BootAnimation::readyToRun() { status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo); if (status) return -1; + char value[PROPERTY_VALUE_MAX]; + property_get("persist.panel.orientation", value, "0"); + int orient = atoi(value) / 90; + + if(orient == eOrientation90 || orient == eOrientation270) { + int temp = dinfo.h; + dinfo.h = dinfo.w; + dinfo.w = temp; + } + + Rect destRect(dinfo.w, dinfo.h); + mSession->setDisplayProjection(dtoken, orient, destRect, destRect); // create the native surface sp control = session()->createSurface(String8("BootAnimation"), diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h index a328b9990366..4bc298954d32 100644 --- a/cmds/bootanimation/BootAnimation.h +++ b/cmds/bootanimation/BootAnimation.h @@ -39,6 +39,12 @@ class SurfaceControl; class BootAnimation : public Thread, public IBinder::DeathRecipient { public: + enum { + eOrientationDefault = 0, + eOrientation90 = 1, + eOrientation180 = 2, + eOrientation270 = 3, + }; BootAnimation(); virtual ~BootAnimation(); diff --git a/cmds/idmap/Android.mk b/cmds/idmap/Android.mk new file mode 100644 index 000000000000..ffa83f2ba428 --- /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 000000000000..81b6dc321dfa --- /dev/null +++ b/cmds/idmap/create.cpp @@ -0,0 +1,243 @@ +#include "idmap.h" + +#include +#include +#include +#include + +#include +#include + +using namespace android; + +namespace { + int get_zip_entry_crc(const char *zip_path, const char *entry_name, uint32_t *crc) + { + ZipFileRO zip; + if (zip.open(zip_path) != NO_ERROR) { + return -1; + } + const ZipEntryRO entry = zip.findEntryByName(entry_name); + if (entry == NULL) { + return -1; + } + if (!zip.getEntryInfo(entry, NULL, NULL, NULL, NULL, NULL, (long*)crc)) { + return -1; + } + 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 target_hash, uint32_t overlay_hash, Vector& targets, + Vector& overlays, uint32_t **data, size_t *size) + { + uint32_t target_crc, overlay_crc; + + // In the original implementation, crc of the res tables are generated + // theme apks however do not need a restable, everything is in assets/ + // instead timestamps are used + target_crc = 0; + overlay_crc = 0; + + struct stat statbuf; + + AssetManager am; + bool b = am.createIdmap(target_apk_path, overlay_apk_path, target_crc, overlay_crc, + target_hash, overlay_hash, targets, overlays, data, size); + return b ? 0 : -1; + } + + int create_and_write_idmap(const char *target_apk_path, const char *overlay_apk_path, + uint32_t target_hash, uint32_t overlay_hash, + const char *redirections, 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; + } + } + + Vector targets; + Vector overlays; + if (redirections && strlen(redirections)) { + FILE *fp = fopen(redirections, "r"); + char target[280]; + char overlay[280]; + if (fp) { + while (!feof(fp)) { + fscanf(fp, "%279s %279s\n", target, overlay); + targets.push(String8(target)); + overlays.push(String8(overlay)); + } + } + } + + uint32_t *data = NULL; + size_t size; + + if (create_idmap(target_apk_path, overlay_apk_path, target_hash, overlay_hash, + targets, overlays, &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, + uint32_t target_hash, uint32_t overlay_hash, + const char *redirections, 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, target_hash, overlay_hash, + redirections, 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, + uint32_t target_hash, uint32_t overlay_hash, + const char *redirections, int fd) +{ + return create_and_write_idmap(target_apk_path, overlay_apk_path, target_hash, overlay_hash, + redirections, fd, true) == 0 ? + EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/cmds/idmap/idmap.cpp b/cmds/idmap/idmap.cpp new file mode 100644 index 000000000000..36786c12ceb7 --- /dev/null +++ b/cmds/idmap/idmap.cpp @@ -0,0 +1,245 @@ +#include "idmap.h" + +#include // for AID_SYSTEM + +#include +#include + +namespace { + const char *usage = "NAME\n\ + idmap - create or display idmap files\n\ +\n\ +SYNOPSIS \n\ + idmap --help \n\ + idmap --fd target overlay fd redirections \n\ + idmap --path target overlay idmap redirections \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 \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, const char *target_hash_str, const char *overlay_hash_str, + const char *redirections) + { + // 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; + } + int target_hash = strtol(target_hash_str, 0, 10); + int overlay_hash = strtol(overlay_hash_str, 0, 10); + + return idmap_create_fd(target_apk_path, overlay_apk_path, target_hash, overlay_hash, + redirections, idmap_fd); + } + + int maybe_create_path(const char *target_apk_path, const char *overlay_apk_path, + const char *idmap_path, const char *target_hash_str, const char *overlay_hash_str, + const char *redirections) + { + 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; + } + + int target_hash = strtol(target_hash_str, 0, 10); + int overlay_hash = strtol(overlay_hash_str, 0, 10); + return idmap_create_path(target_apk_path, overlay_apk_path, target_hash, overlay_hash, + redirections, 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 == 8 && !strcmp(argv[1], "--fd")) { + return maybe_create_fd(argv[2], argv[3], argv[4], argv[5], argv[6], argv[7]); + } + + if (argc == 8 && !strcmp(argv[1], "--path")) { + return maybe_create_path(argv[2], argv[3], argv[4], argv[5], argv[6], argv[7]); + } + + 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 000000000000..1ba50d642cd6 --- /dev/null +++ b/cmds/idmap/idmap.h @@ -0,0 +1,37 @@ +#ifndef _IDMAP_H_ +#define _IDMAP_H_ + +#define LOG_TAG "idmap" + +#include + +#include +#include + +#ifndef TEMP_FAILURE_RETRY +// Used to retry syscalls that can return EINTR. +#define TEMP_FAILURE_RETRY(exp) ({ \ + typeof (exp) _rc; \ + do { \ + _rc = (exp); \ + } while (_rc == -1 && errno == EINTR); \ + _rc; }) +#endif + +int idmap_create_path(const char *target_apk_path, const char *overlay_apk_path, + uint32_t target_hash, uint32_t overlay_hash, + const char *redirections, const char *idmap_path); + +int idmap_create_fd(const char *target_apk_path, const char *overlay_apk_path, + uint32_t target_hash, uint32_t overlay_hash, + const char *redirections, 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 000000000000..c19b1ba08eec --- /dev/null +++ b/cmds/idmap/inspect.cpp @@ -0,0 +1,297 @@ +#include "idmap.h" + +#include +#include +#include + +#include +#include +#include + +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, ""); + + NEXT(buf, i, o); + print("", "base mtime", i, o, ""); + + NEXT(buf, i, o); + print("", "overlay mtime", 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& 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 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 000000000000..43418e266408 --- /dev/null +++ b/cmds/idmap/scan.cpp @@ -0,0 +1,243 @@ +#include "idmap.h" + +#include +#include +#include +#include // for AID_SYSTEM +#include +#include +#include + +#include + +#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& 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) + { + ZipFileRO zip; + if (zip.open(path) != NO_ERROR) { + 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 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, 0, 0, 0, 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/accounts/ChooseTypeAndAccountActivity.java b/core/java/android/accounts/ChooseTypeAndAccountActivity.java index c407ba9d05db..55f628bcd77b 100644 --- a/core/java/android/accounts/ChooseTypeAndAccountActivity.java +++ b/core/java/android/accounts/ChooseTypeAndAccountActivity.java @@ -318,7 +318,7 @@ protected void onActivityResult(final int requestCode, final int resultCode, } // if canceling out of addAccount and the original state caused us to skip this, // finish this activity - if (mAccounts.isEmpty()) { + if (null == mAccounts || mAccounts.isEmpty()) { setResult(Activity.RESULT_CANCELED); finish(); } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 1408e604e5f3..c58b4056689c 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -2204,13 +2204,10 @@ private Activity performLaunchActivity(ActivityClientRecord r, Intent customInte if (!mInstrumentation.onException(activity, e)) { if (e instanceof InflateException) { Log.e(TAG, "Failed to inflate", e); - String pkg = null; - if (r.packageInfo != null && !TextUtils.isEmpty(r.packageInfo.getPackageName())) { - pkg = r.packageInfo.getPackageName(); - } - Intent intent = new Intent(Intent.ACTION_APP_LAUNCH_FAILURE, - (pkg != null)? Uri.fromParts("package", pkg, null) : null); - getSystemContext().sendBroadcast(intent); + sendAppLaunchFailureBroadcast(r); + } else if (e instanceof Resources.NotFoundException) { + Log.e(TAG, "Failed to find resource", e); + sendAppLaunchFailureBroadcast(r); } throw new RuntimeException( "Unable to start activity " + component @@ -2221,6 +2218,16 @@ private Activity performLaunchActivity(ActivityClientRecord r, Intent customInte return activity; } + private void sendAppLaunchFailureBroadcast(ActivityClientRecord r) { + String pkg = null; + if (r.packageInfo != null && !TextUtils.isEmpty(r.packageInfo.getPackageName())) { + pkg = r.packageInfo.getPackageName(); + } + Intent intent = new Intent(Intent.ACTION_APP_LAUNCH_FAILURE, + (pkg != null)? Uri.fromParts("package", pkg, null) : null); + getSystemContext().sendBroadcast(intent); + } + private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) { ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, r.token); diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 60bb3aec0217..819bf7064b77 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -1415,4 +1415,18 @@ public void updateIconMaps(String pkgName) { Log.e(TAG, "Failed to update icon maps", re); } } + + /** + * @hide + */ + @Override + public int processThemeResources(String themePkgName) { + try { + return mPM.processThemeResources(themePkgName); + } catch (RemoteException e) { + Log.e(TAG, "Unable to process theme resources for " + themePkgName, e); + } + + return 0; + } } diff --git a/core/java/android/app/ComposedIconInfo.java b/core/java/android/app/ComposedIconInfo.java index f7106375bb6a..7fab85241b1e 100644 --- a/core/java/android/app/ComposedIconInfo.java +++ b/core/java/android/app/ComposedIconInfo.java @@ -15,37 +15,42 @@ */ package android.app; -import android.graphics.Bitmap; -import android.graphics.drawable.BitmapDrawable; import android.os.Parcel; import android.os.Parcelable; /** @hide */ public class ComposedIconInfo implements Parcelable { - public BitmapDrawable iconUpon, iconMask; - public BitmapDrawable[] iconBacks; + public int iconUpon, iconMask; + public int[] iconBacks; public float iconScale; public int iconDensity; public int iconSize; + public float[] colorFilter; public ComposedIconInfo() { super(); } private ComposedIconInfo(Parcel source) { - ClassLoader bmpClassLoader = Bitmap.class.getClassLoader(); iconScale = source.readFloat(); iconDensity = source.readInt(); iconSize = source.readInt(); int backCount = source.readInt(); if (backCount > 0) { - iconBacks = new BitmapDrawable[backCount]; + iconBacks = new int[backCount]; for (int i = 0; i < backCount; i++) { - iconBacks[i] = new BitmapDrawable((Bitmap) source.readParcelable(bmpClassLoader)); + iconBacks[i] = source.readInt(); + } + } + iconMask = source.readInt(); + iconUpon = source.readInt(); + int colorFilterSize = source.readInt(); + if (colorFilterSize > 0) { + colorFilter = new float[colorFilterSize]; + for (int i = 0; i < colorFilterSize; i++) { + colorFilter[i] = source.readFloat(); } } - iconMask = new BitmapDrawable((Bitmap) source.readParcelable(bmpClassLoader)); - iconUpon = new BitmapDrawable((Bitmap) source.readParcelable(bmpClassLoader)); } @Override @@ -60,12 +65,20 @@ public void writeToParcel(Parcel dest, int flags) { dest.writeInt(iconSize); dest.writeInt(iconBacks != null ? iconBacks.length : 0); if (iconBacks != null) { - for (BitmapDrawable d : iconBacks) { - dest.writeParcelable(d != null ? d.getBitmap() : null, flags); + for (int resId : iconBacks) { + dest.writeInt(resId); + } + } + dest.writeInt(iconMask); + dest.writeInt(iconUpon); + if (colorFilter != null) { + dest.writeInt(colorFilter.length); + for (float val : colorFilter) { + dest.writeFloat(val); } + } else { + dest.writeInt(0); } - dest.writeParcelable(iconMask != null ? iconMask.getBitmap() : null, flags); - dest.writeParcelable(iconUpon != null ? iconUpon.getBitmap() : null, flags); } public static final Creator CREATOR diff --git a/core/java/android/app/DialogFragment.java b/core/java/android/app/DialogFragment.java index bde5a61f9687..5492a45645cb 100644 --- a/core/java/android/app/DialogFragment.java +++ b/core/java/android/app/DialogFragment.java @@ -461,6 +461,10 @@ public void onActivityCreated(Bundle savedInstanceState) { return; } + if (mDialog == null) { + return; + } + View view = getView(); if (view != null) { if (view.getParent() != null) { diff --git a/core/java/android/app/IconPackHelper.java b/core/java/android/app/IconPackHelper.java index ad5fd55256f4..ceea8987c24c 100644 --- a/core/java/android/app/IconPackHelper.java +++ b/core/java/android/app/IconPackHelper.java @@ -16,9 +16,12 @@ package android.app; import java.io.File; +import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Random; @@ -26,6 +29,9 @@ import android.content.res.IThemeService; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; import android.graphics.Paint; import android.graphics.PaintFlagsDrawFilter; import android.graphics.PorterDuff; @@ -77,6 +83,7 @@ public class IconPackHelper { private Resources mLoadedIconPackResource; private ComposedIconInfo mComposedIconInfo; private int mIconBackCount = 0; + private ColorFilterUtils.Builder mFilterBuilder; static { ICON_BACK_COMPONENT = new ComponentName(ICON_BACK_TAG, ""); @@ -92,6 +99,7 @@ public IconPackHelper(Context context) { mComposedIconInfo = new ComposedIconInfo(); mComposedIconInfo.iconSize = am.getLauncherLargeIconSize(); mComposedIconInfo.iconDensity = am.getLauncherLargeIconDensity(); + mFilterBuilder = new ColorFilterUtils.Builder(); } private void loadResourcesFromXmlParser(XmlPullParser parser, @@ -108,6 +116,10 @@ private void loadResourcesFromXmlParser(XmlPullParser parser, continue; } + if (ColorFilterUtils.parseIconFilter(parser, mFilterBuilder)) { + continue; + } + if (parser.getName().equalsIgnoreCase(ICON_SCALE_TAG)) { String factor = parser.getAttributeValue(null, "factor"); if (factor == null) { @@ -192,8 +204,9 @@ public void loadIconPack(String packageName) throws NameNotFoundException { mLoadedIconPackResource = null; mLoadedIconPackName = null; mComposedIconInfo.iconBacks = null; - mComposedIconInfo.iconMask = mComposedIconInfo.iconUpon = null; + mComposedIconInfo.iconMask = mComposedIconInfo.iconUpon = 0; mComposedIconInfo.iconScale = 0; + mComposedIconInfo.colorFilter = null; } else { mIconBackCount = 0; Resources res = createIconResource(mContext, packageName); @@ -201,6 +214,10 @@ public void loadIconPack(String packageName) throws NameNotFoundException { mLoadedIconPackResource = res; mLoadedIconPackName = packageName; loadComposedIconComponents(); + ColorMatrix cm = mFilterBuilder.build(); + if (cm != null) { + mComposedIconInfo.colorFilter = cm.getArray().clone(); + } } } @@ -209,15 +226,15 @@ public ComposedIconInfo getComposedIconInfo() { } private void loadComposedIconComponents() { - mComposedIconInfo.iconMask = (BitmapDrawable) getDrawableForName(ICON_MASK_COMPONENT); - mComposedIconInfo.iconUpon = (BitmapDrawable) getDrawableForName(ICON_UPON_COMPONENT); + mComposedIconInfo.iconMask = getResourceIdForName(ICON_MASK_COMPONENT); + mComposedIconInfo.iconUpon = getResourceIdForName(ICON_UPON_COMPONENT); // Take care of loading iconback which can have multiple images if (mIconBackCount > 0) { - mComposedIconInfo.iconBacks = new BitmapDrawable[mIconBackCount]; + mComposedIconInfo.iconBacks = new int[mIconBackCount]; for (int i = 0; i < mIconBackCount; i++) { mComposedIconInfo.iconBacks[i] = - (BitmapDrawable) getDrawableForName( + getResourceIdForName( new ComponentName(String.format(ICON_BACK_FORMAT, i), "")); } } @@ -235,17 +252,12 @@ private void loadComposedIconComponents() { } } - private Drawable getDrawableForName(ComponentName component) { - if (isIconPackLoaded()) { - String item = mIconPackResourceMap.get(component); - if (!TextUtils.isEmpty(item)) { - int id = getResourceIdForDrawable(item); - if (id != 0) { - return mLoadedIconPackResource.getDrawable(id); - } - } + private int getResourceIdForName(ComponentName component) { + String item = mIconPackResourceMap.get(component); + if (!TextUtils.isEmpty(item)) { + return getResourceIdForDrawable(item); } - return null; + return 0; } public static Resources createIconResource(Context context, String packageName) throws NameNotFoundException { @@ -414,6 +426,12 @@ public Drawable getDrawableForActivityWithDensity(ActivityInfo info, int density return mLoadedIconPackResource.getDrawableForDensity(id, density, false); } + public static boolean shouldComposeIcon(ComposedIconInfo iconInfo) { + return iconInfo != null && + (iconInfo.iconBacks != null || iconInfo.iconMask != 0 || + iconInfo.iconUpon != 0 || iconInfo.colorFilter != null); + } + public static class IconCustomizer { private static final Random sRandom = new Random(); private static final IThemeService sThemeService; @@ -432,12 +450,12 @@ public static Drawable getComposedIconDrawable(Drawable icon, Context context, public static Drawable getComposedIconDrawable(Drawable icon, Resources res, ComposedIconInfo iconInfo) { if (iconInfo == null) return icon; - Drawable back = null; + int back = 0; if (iconInfo.iconBacks != null && iconInfo.iconBacks.length > 0) { back = iconInfo.iconBacks[sRandom.nextInt(iconInfo.iconBacks.length)]; } Bitmap bmp = createIconBitmap(icon, res, back, iconInfo.iconMask, iconInfo.iconUpon, - iconInfo.iconScale, iconInfo.iconSize); + iconInfo.iconScale, iconInfo.iconSize, iconInfo.colorFilter); return bmp != null ? new BitmapDrawable(res, bmp): null; } @@ -455,13 +473,14 @@ public static void getValue(Resources res, int resId, TypedValue outValue, if (!(new File(outValue.string.toString()).exists())) { // compose the icon and cache it final ComposedIconInfo iconInfo = res.getComposedIconInfo(); - Drawable back = null; + int back = 0; if (iconInfo.iconBacks != null && iconInfo.iconBacks.length > 0) { back = iconInfo.iconBacks[(outValue.string.hashCode() & 0x7fffffff) % iconInfo.iconBacks.length]; } Bitmap bmp = createIconBitmap(baseIcon, res, back, iconInfo.iconMask, - iconInfo.iconUpon, iconInfo.iconScale, iconInfo.iconSize); + iconInfo.iconUpon, iconInfo.iconScale, iconInfo.iconSize, + iconInfo.colorFilter); if (!cacheComposedIcon(bmp, getCachedIconName(pkgName, resId, outValue.density))) { Log.w(TAG, "Unable to cache icon " + outValue.string); // restore the original TypedValue @@ -470,9 +489,9 @@ public static void getValue(Resources res, int resId, TypedValue outValue, } } - private static Bitmap createIconBitmap(Drawable icon, Resources res, Drawable iconBack, - Drawable iconMask, Drawable iconUpon, float scale, - int iconSize) { + private static Bitmap createIconBitmap(Drawable icon, Resources res, int iconBack, + int iconMask, int iconUpon, float scale, + int iconSize, float[] colorFilter) { if (iconSize <= 0) return null; final Canvas canvas = new Canvas(); @@ -497,9 +516,19 @@ private static Bitmap createIconBitmap(Drawable icon, Resources res, Drawable ic } canvas.setDensity(bitmap.getDensity()); - // Respect the original size of an icon - width = bitmap.getWidth(); - height = bitmap.getHeight(); + // If the original size of the icon isn't greater + // than twice the size of recommended large icons + // respect the original size of the icon + // otherwise enormous icons can easily create + // OOM situations. + if ((bitmap.getWidth() < (iconSize * 2)) + && (bitmap.getHeight() < (iconSize * 2))) { + width = bitmap.getWidth(); + height = bitmap.getHeight(); + } else { + width = iconSize; + height = iconSize; + } } if (width <= 0 || height <= 0) return null; @@ -514,27 +543,45 @@ private static Bitmap createIconBitmap(Drawable icon, Resources res, Drawable ic icon.setBounds(0, 0, width, height); canvas.save(); canvas.scale(scale, scale, width / 2, height / 2); + if (colorFilter != null) { + Paint p = null; + if (icon instanceof BitmapDrawable) { + p = ((BitmapDrawable) icon).getPaint(); + } else if (icon instanceof PaintDrawable) { + p = ((PaintDrawable) icon).getPaint(); + } + p.setColorFilter(new ColorMatrixColorFilter(colorFilter)); + } icon.draw(canvas); canvas.restore(); // Mask off the original if iconMask is not null - if (iconMask != null) { - iconMask.setBounds(icon.getBounds()); - ((BitmapDrawable) iconMask).getPaint().setXfermode( - new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); - iconMask.draw(canvas); + if (iconMask != 0) { + Drawable mask = res.getDrawable(iconMask); + if (mask != null) { + mask.setBounds(icon.getBounds()); + ((BitmapDrawable) mask).getPaint().setXfermode( + new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); + mask.draw(canvas); + } } // Draw the iconBacks if not null and then the original (scaled and masked) icon on top - if (iconBack != null) { - iconBack.setBounds(icon.getBounds()); - ((BitmapDrawable) iconBack).getPaint().setXfermode( - new PorterDuffXfermode(PorterDuff.Mode.DST_OVER)); - iconBack.draw(canvas); + if (iconBack != 0) { + Drawable back = res.getDrawable(iconBack); + if (back != null) { + back.setBounds(icon.getBounds()); + ((BitmapDrawable) back).getPaint().setXfermode( + new PorterDuffXfermode(PorterDuff.Mode.DST_OVER)); + back.draw(canvas); + } } // Finally draw the foreground if one was supplied - if (iconUpon != null) { - iconUpon.setBounds(icon.getBounds()); - iconUpon.draw(canvas); + if (iconUpon != 0) { + Drawable upon = res.getDrawable(iconUpon); + if (upon != null) { + upon.setBounds(icon.getBounds()); + upon.draw(canvas); + } } icon.setBounds(oldBounds); bitmap.setDensity(canvas.getDensity()); @@ -557,8 +604,260 @@ private static String getCachedIconPath(String pkgName, int resId, int density) getCachedIconName(pkgName, resId, density)); } + + // Returns paths for all densities + public static String[] getCachedIconPaths(String pkgName) { + File iconCache = new File(ThemeUtils.SYSTEM_THEME_ICON_CACHE_DIR); + final String prefix = String.format("%s", pkgName); + + FilenameFilter filter = new FilenameFilter() { + @Override + public boolean accept(File dir, String filename) { + return filename.startsWith(prefix); + } + }; + + String[] validPaths = iconCache.list(filter); + return validPaths != null ? validPaths : new String[0]; + } + private static String getCachedIconName(String pkgName, int resId, int density) { return String.format("%s_%08x_%d.png", pkgName, resId, density); } } + + public static class ColorFilterUtils { + private static final String TAG_FILTER = "filter"; + private static final String FILTER_HUE = "hue"; + private static final String FILTER_SATURATION = "saturation"; + private static final String FILTER_INVERT = "invert"; + private static final String FILTER_BRIGHTNESS = "brightness"; + private static final String FILTER_CONTRAST = "contrast"; + private static final String FILTER_ALPHA = "alpha"; + private static final String FILTER_TINT = "tint"; + + private static final int MIN_HUE = -180; + private static final int MAX_HUE = 180; + private static final int MIN_SATURATION = 0; + private static final int MAX_SATURATION = 200; + private static final int MIN_BRIGHTNESS = 0; + private static final int MAX_BRIGHTNESS = 200; + private static final int MIN_CONTRAST = -100; + private static final int MAX_CONTRAST = 100; + private static final int MIN_ALPHA = 0; + private static final int MAX_ALPHA = 100; + + public static boolean parseIconFilter(XmlPullParser parser, Builder builder) + throws IOException, XmlPullParserException { + String tag = parser.getName(); + if (!TAG_FILTER.equals(tag)) return false; + + int attrCount = parser.getAttributeCount(); + String attrName; + String attr = null; + int intValue; + while (attrCount-- > 0) { + attrName = parser.getAttributeName(attrCount); + if (attrName.equals("name")) { + attr = parser.getAttributeValue(attrCount); + } + } + String content = parser.nextText(); + if (attr != null && content != null && content.length() > 0) { + content = content.trim(); + if (FILTER_HUE.equalsIgnoreCase(attr)) { + intValue = clampValue(getInt(content, 0),MIN_HUE, MAX_HUE); + builder.hue(intValue); + } else if (FILTER_SATURATION.equalsIgnoreCase(attr)) { + intValue = clampValue(getInt(content, 100), + MIN_SATURATION, MAX_SATURATION); + builder.saturate(intValue); + } else if (FILTER_INVERT.equalsIgnoreCase(attr)) { + if ("true".equalsIgnoreCase(content)) { + builder.invertColors(); + } + } else if (FILTER_BRIGHTNESS.equalsIgnoreCase(attr)) { + intValue = clampValue(getInt(content, 100), + MIN_BRIGHTNESS, MAX_BRIGHTNESS); + builder.brightness(intValue); + } else if (FILTER_CONTRAST.equalsIgnoreCase(attr)) { + intValue = clampValue(getInt(content, 0), + MIN_CONTRAST, MAX_CONTRAST); + builder.contrast(intValue); + } else if (FILTER_ALPHA.equalsIgnoreCase(attr)) { + intValue = clampValue(getInt(content, 100), MIN_ALPHA, MAX_ALPHA); + builder.alpha(intValue); + } else if (FILTER_TINT.equalsIgnoreCase(attr)) { + try { + intValue = Color.parseColor(content); + builder.tint(intValue); + } catch (IllegalArgumentException e) { + Log.w(TAG, "Cannot apply tint, invalid argument: " + content); + } + } + } + return true; + } + + private static int getInt(String value, int defaultValue) { + try { + return Integer.valueOf(value); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + private static int clampValue(int value, int min, int max) { + return Math.min(max, Math.max(min, value)); + } + + /** + * See the following links for reference + * http://groups.google.com/group/android-developers/browse_thread/thread/9e215c83c3819953 + * http://gskinner.com/blog/archives/2007/12/colormatrix_cla.html + * @param value + */ + public static ColorMatrix adjustHue(float value) { + ColorMatrix cm = new ColorMatrix(); + value = value / 180 * (float) Math.PI; + if (value != 0) { + float cosVal = (float) Math.cos(value); + float sinVal = (float) Math.sin(value); + float lumR = 0.213f; + float lumG = 0.715f; + float lumB = 0.072f; + float[] mat = new float[]{ + lumR + cosVal * (1 - lumR) + sinVal * (-lumR), + lumG + cosVal * (-lumG) + sinVal * (-lumG), + lumB + cosVal * (-lumB) + sinVal * (1 - lumB), 0, 0, + lumR + cosVal * (-lumR) + sinVal * (0.143f), + lumG + cosVal * (1 - lumG) + sinVal * (0.140f), + lumB + cosVal * (-lumB) + sinVal * (-0.283f), 0, 0, + lumR + cosVal * (-lumR) + sinVal * (-(1 - lumR)), + lumG + cosVal * (-lumG) + sinVal * (lumG), + lumB + cosVal * (1 - lumB) + sinVal * (lumB), 0, 0, + 0, 0, 0, 1, 0, + 0, 0, 0, 0, 1}; + cm.set(mat); + } + return cm; + } + + public static ColorMatrix adjustSaturation(float saturation) { + saturation = saturation / 100; + ColorMatrix cm = new ColorMatrix(); + cm.setSaturation(saturation); + + return cm; + } + + public static ColorMatrix invertColors() { + float[] matrix = { + -1, 0, 0, 0, 255, //red + 0, -1, 0, 0, 255, //green + 0, 0, -1, 0, 255, //blue + 0, 0, 0, 1, 0 //alpha + }; + + return new ColorMatrix(matrix); + } + + public static ColorMatrix adjustBrightness(float brightness) { + brightness = brightness / 100; + ColorMatrix cm = new ColorMatrix(); + cm.setScale(brightness, brightness, brightness, 1); + + return cm; + } + + public static ColorMatrix adjustContrast(float contrast) { + contrast = contrast / 100 + 1; + float o = (-0.5f * contrast + 0.5f) * 255; + float[] matrix = { + contrast, 0, 0, 0, o, //red + 0, contrast, 0, 0, o, //green + 0, 0, contrast, 0, o, //blue + 0, 0, 0, 1, 0 //alpha + }; + + return new ColorMatrix(matrix); + } + + public static ColorMatrix adjustAlpha(float alpha) { + alpha = alpha / 100; + ColorMatrix cm = new ColorMatrix(); + cm.setScale(1, 1, 1, alpha); + + return cm; + } + + public static ColorMatrix applyTint(int color) { + float alpha = Color.alpha(color) / 255f; + float red = Color.red(color) * alpha; + float green = Color.green(color) * alpha; + float blue = Color.blue(color) * alpha; + + float[] matrix = { + 1, 0, 0, 0, red, //red + 0, 1, 0, 0, green, //green + 0, 0, 1, 0, blue, //blue + 0, 0, 0, 1, 0 //alpha + }; + + return new ColorMatrix(matrix); + } + + public static class Builder { + private List mMatrixList; + + public Builder() { + mMatrixList = new ArrayList(); + } + + public Builder hue(float value) { + mMatrixList.add(adjustHue(value)); + return this; + } + + public Builder saturate(float saturation) { + mMatrixList.add(adjustSaturation(saturation)); + return this; + } + + public Builder brightness(float brightness) { + mMatrixList.add(adjustBrightness(brightness)); + return this; + } + + public Builder contrast(float contrast) { + mMatrixList.add(adjustContrast(contrast)); + return this; + } + + public Builder alpha(float alpha) { + mMatrixList.add(adjustAlpha(alpha)); + return this; + } + + public Builder invertColors() { + mMatrixList.add(ColorFilterUtils.invertColors()); + return this; + } + + public Builder tint(int color) { + mMatrixList.add(applyTint(color)); + return this; + } + + public ColorMatrix build() { + if (mMatrixList == null || mMatrixList.size() == 0) return null; + + ColorMatrix colorMatrix = new ColorMatrix(); + for (ColorMatrix cm : mMatrixList) { + colorMatrix.postConcat(cm); + } + return colorMatrix; + } + } + } } diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index e7220642e6dd..3dd249e3173d 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -364,8 +364,8 @@ private void setActivityIcons(Resources r) { try { ComposedIconInfo iconInfo = pm.getComposedIconInfo(); r.setComposedIconInfo(iconInfo); - } catch (RemoteException e) { - e.printStackTrace(); + } catch (Exception e) { + Log.wtf(TAG, "Failed to retrieve ComposedIconInfo", e); } } @@ -485,9 +485,9 @@ private boolean attachThemeAssets(AssetManager assets, ThemeConfig theme) { PackageInfo piTarget = null; PackageInfo piAndroid = null; - // Some apps run in process of another app (eg keyguard/systemUI) so we must get the package name - // from the res tables. The 0th base package name will be the android group. The - // 1st base package name will be the app group if one is attached. Check if it is there + // Some apps run in process of another app (eg keyguard/systemUI) so we must get the + // package name from the res tables. The 0th base package name will be the android group. + // The 1st base package name will be the app group if one is attached. Check if it is there // first or else the system will crash! String basePackageName = null; String resourcePackageName = null; @@ -619,9 +619,24 @@ private boolean attachIconAssets(AssetManager assets, ThemeConfig theme) { * @return true if succes, false otherwise */ private boolean attachCommonAssets(AssetManager assets, ThemeConfig theme) { + // Some apps run in process of another app (eg keyguard/systemUI) so we must get the + // package name from the res tables. The 0th base package name will be the android group. + // The 1st base package name will be the app group if one is attached. Check if it is there + // first or else the system will crash! + String basePackageName; + int count = assets.getBasePackageCount(); + if (count > 1) { + basePackageName = assets.getBasePackageName(1); + } else if (count == 1) { + basePackageName = assets.getBasePackageName(0); + } else { + return false; + } + PackageInfo piTheme = null; try { - piTheme = getPackageManager().getPackageInfo(theme.getOverlayPkgName(), 0, + piTheme = getPackageManager().getPackageInfo( + theme.getOverlayPkgNameForApp(basePackageName), 0, UserHandle.getCallingUserId()); } catch (RemoteException e) { } diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 184fa39efe19..7eeb265d3059 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -1166,7 +1166,18 @@ public void clearWallpaperOffsets(IBinder windowToken) { * wallpaper. */ public void clear() throws IOException { - setResource(com.android.internal.R.drawable.default_wallpaper); + clear(true); + } + + /** @hide */ + public void clear(boolean setToDefault) throws IOException { + if (setToDefault) { + setResource(com.android.internal.R.drawable.default_wallpaper); + } else { + Bitmap blackBmp = Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565); + blackBmp.setPixel(0, 0, mContext.getResources().getColor(android.R.color.black)); + setBitmap(blackBmp); + } } /** diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index ab825314f013..e9c5de16e3bc 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -272,6 +272,8 @@ public boolean hasGrantedPolicy(ComponentName admin, int usesPolicy) { */ public static final int PASSWORD_QUALITY_COMPLEX = 0x60000; + public static final int PASSWORD_QUALITY_GESTURE_WEAK = 0x80000; + /** * Called by an application that is administering the device to set the * password restrictions it is imposing. After setting this, the user diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java index 58cb1b7a9cf7..023774e60e7e 100644 --- a/core/java/android/bluetooth/BluetoothSocket.java +++ b/core/java/android/bluetooth/BluetoothSocket.java @@ -197,6 +197,11 @@ private BluetoothSocket acceptSocket(String RemoteAddr) throws IOException { throw new IOException("bt socket acept failed"); } as.mSocket = new LocalSocket(fds[0]); + try { + as.mSocket.closeExternalFd(); + } catch (IOException e) { + Log.e(TAG, "closeExternalFd failed"); + } as.mSocketIS = as.mSocket.getInputStream(); as.mSocketOS = as.mSocket.getOutputStream(); as.mAddress = RemoteAddr; diff --git a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java index a9b717693356..bedfd5b6e273 100644 --- a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java +++ b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java @@ -402,13 +402,21 @@ public void handleMessage(Message msg) { } else { ac.sendMessage( AsyncChannel.CMD_CHANNEL_FULL_CONNECTION); + mStackChannel = ac; + if (VDBG) Log.d(TAG, "CMD_CHANNEL_HALF_CONNECTED: " + mStackChannel); } } break; case AsyncChannel.CMD_CHANNEL_DISCONNECTED: if (VDBG) Log.d(TAG, "got CMD_CHANNEL_DISCONNECTED"); - mBtdt.stopReverseTether(); - mBtdt.mAsyncChannel.set(null); + if (mStackChannel != null) { + mBtdt.stopReverseTether(); + mBtdt.mAsyncChannel.set(null); + + Log.d(TAG, "Disconnect CMD_CHANNEL_HALF_CONNECTED: " + mStackChannel); + mStackChannel.disconnect(); + mStackChannel = null; + } break; case NetworkStateTracker.EVENT_NETWORK_CONNECTED: LinkProperties linkProperties = (LinkProperties)(msg.obj); diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index fc04055424a2..6b7b7d6291d0 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2752,6 +2752,19 @@ public static Intent createChooser(Intent target, CharSequence title) { @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_CREATE_DOCUMENT = "android.intent.action.CREATE_DOCUMENT"; + /** + * Broadcast Action: A theme's resources were cached. Includes two extra fields, + * {@link #EXTRA_THEME_PACKAGE_NAME}, containing the package name of the theme that was + * processed, and {@link #EXTRA_THEME_RESULT}, containing the result code. + * + *

This is a protected intent that can only be sent + * by the system.

+ * + * @hide + */ + public static final String ACTION_THEME_RESOURCES_CACHED = + "android.intent.action.THEME_RESOURCES_CACHED"; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Standard intent categories (see addCategory()). @@ -3396,6 +3409,26 @@ public static Intent createChooser(Intent target, CharSequence title) { public static final String EXTRA_SHUTDOWN_USERSPACE_ONLY = "android.intent.extra.SHUTDOWN_USERSPACE_ONLY"; + /** + * Extra for {@link #ACTION_THEME_RESOURCES_CACHED} that provides the return value + * from processThemeResources. A value of 0 indicates a successful caching of resources. + * Error results are: + * {@link android.content.pm.PackageManager#INSTALL_FAILED_THEME_AAPT_ERROR} + * {@link android.content.pm.PackageManager#INSTALL_FAILED_THEME_IDMAP_ERROR} + * {@link android.content.pm.PackageManager#INSTALL_FAILED_THEME_UNKNOWN_ERROR} + * + * @hide + */ + public static final String EXTRA_THEME_RESULT = "android.intent.extra.RESULT"; + + /** + * Extra for {@link #ACTION_THEME_RESOURCES_CACHED} that provides the package name of the + * theme that was processed. + * + * @hide + */ + public static final String EXTRA_THEME_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME"; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Intent flags (see mFlags variable). diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 6e361d41dc08..8fc1f8f1ddb2 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -425,4 +425,5 @@ interface IPackageManager { /** Themes */ void updateIconMapping(String pkgName); ComposedIconInfo getComposedIconInfo(); + int processThemeResources(String themePkgName); } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 22b8726080a3..3a84b002dfac 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3265,4 +3265,16 @@ public static String getDataDirForUser(int userId, String packageName) { * @hide */ public abstract void updateIconMaps(String pkgName); + + /** + * Used to compile theme resources for a given theme + * @param themePkgName + * @return A value of 0 indicates success. Possible errors returned are: + * {@link android.content.pm.PackageManager#INSTALL_FAILED_THEME_AAPT_ERROR}, + * {@link android.content.pm.PackageManager#INSTALL_FAILED_THEME_IDMAP_ERROR}, or + * {@link android.content.pm.PackageManager#INSTALL_FAILED_THEME_UNKNOWN_ERROR} + * + * @hide + */ + public abstract int processThemeResources(String themePkgName); } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 22aa09fd5ae4..1288728d260f 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -946,7 +946,7 @@ public boolean collectCertificates(Package pkg, int flags) { } try { - JarFile jarFile = new JarFile(mArchiveSourcePath); + JarFile jarFile = new JarFile(mArchiveSourcePath, true, true); Certificate[] certs = null; diff --git a/core/java/android/content/pm/ThemeUtils.java b/core/java/android/content/pm/ThemeUtils.java index a89382df636a..08a3de03d1f3 100644 --- a/core/java/android/content/pm/ThemeUtils.java +++ b/core/java/android/content/pm/ThemeUtils.java @@ -15,6 +15,7 @@ */ package android.content.pm; +import android.Manifest; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.ContentValues; @@ -22,7 +23,11 @@ import android.content.ContextWrapper; import android.content.IntentFilter; import android.content.res.AssetManager; +import android.content.res.Configuration; +import android.content.res.ThemeConfig; import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteException; import android.media.RingtoneManager; import android.net.Uri; import android.os.FileUtils; @@ -47,7 +52,9 @@ import java.io.InputStreamReader; import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.zip.CRC32; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; @@ -101,6 +108,10 @@ public class ThemeUtils { public static final String CATEGORY_THEME_COMPONENT_PREFIX = "org.cyanogenmod.intent.category."; + private static final String SETTINGS_DB = + "/data/data/com.android.providers.settings/databases/settings.db"; + private static final String SETTINGS_SECURE_TABLE = "secure"; + // Actions in manifests which identify legacy icon packs public static final String[] sSupportedActions = new String[] { "org.adw.launcher.THEMES", @@ -518,15 +529,31 @@ public static void registerThemeChangeReceiver(final Context context, final Broa public static String getLockscreenWallpaperPath(AssetManager assetManager) throws IOException { String[] assets = assetManager.list("lockscreen"); - if (assets == null || assets.length == 0) return null; - - return "lockscreen/" + assets[0]; + String asset = getFirstNonEmptyAsset(assets); + if (asset == null) return null; + return "lockscreen/" + asset; } public static String getWallpaperPath(AssetManager assetManager) throws IOException { String[] assets = assetManager.list("wallpapers"); - if (assets == null || assets.length == 0) return null; - return "wallpapers/" + assets[0]; + String asset = getFirstNonEmptyAsset(assets); + if (asset == null) return null; + return "wallpapers/" + asset; + } + + // Returns the first non-empty asset name. Empty assets can occur if the APK is built + // with folders included as zip entries in the APK. Searching for files inside "folderName" via + // assetManager.list("folderName") can cause these entries to be included as empty strings. + private static String getFirstNonEmptyAsset(String[] assets) { + if (assets == null) return null; + String filename = null; + for(String asset : assets) { + if (!asset.isEmpty()) { + filename = asset; + break; + } + } + return filename; } public static String getDefaultThemePackageName(Context context) { @@ -602,4 +629,97 @@ public static List getSupportedComponents(Context context, String pkgNam } return supportedComponents; } + + /** + * Get the components from the default theme. If the default theme is not HOLO then any + * components that are not in the default theme will come from HOLO to create a complete + * component map. + * @param context + * @return + */ + public static Map getDefaultComponents(Context context) { + String defaultThemePkg = getDefaultThemePackageName(context); + List defaultComponents = null; + List holoComponents = getSupportedComponents(context, HOLO_DEFAULT); + if (!HOLO_DEFAULT.equals(defaultThemePkg)) { + defaultComponents = getSupportedComponents(context, defaultThemePkg); + } + + Map componentMap = new HashMap(holoComponents.size()); + if (defaultComponents != null) { + for (String component : defaultComponents) { + componentMap.put(component, defaultThemePkg); + } + } + for (String component : holoComponents) { + if (!componentMap.containsKey(component)) { + componentMap.put(component, HOLO_DEFAULT); + } + } + + return componentMap; + } + + /** + * Takes an existing component map and adds any missing components from the default + * map of components. + * @param context + * @param componentMap An existing component map + */ + public static void completeComponentMap(Context context, + Map componentMap) { + if (componentMap == null) return; + + Map defaultComponents = getDefaultComponents(context); + for (String component : defaultComponents.keySet()) { + if (!componentMap.containsKey(component)) { + componentMap.put(component, defaultComponents.get(component)); + } + } + } + + /** + * Get the boot theme by accessing the settings.db directly instead of using a content resolver. + * Only use this when the system is starting up and the settings content provider is not ready. + * + * Note: This method will only succeed if the system is calling this since normal apps will not + * be able to access the settings db path. + * + * @return The boot theme or null if unable to read the database or get the entry for theme + * config + */ + public static ThemeConfig getBootThemeDirty() { + ThemeConfig config = null; + SQLiteDatabase db = null; + try { + db = SQLiteDatabase.openDatabase(SETTINGS_DB, null, + SQLiteDatabase.OPEN_READONLY); + if (db != null) { + String selection = "name=?"; + String[] selectionArgs = + { Configuration.THEME_PKG_CONFIGURATION_PERSISTENCE_PROPERTY }; + String[] columns = {"value"}; + Cursor c = db.query(SETTINGS_SECURE_TABLE, columns, selection, selectionArgs, + null, null, null); + if (c != null) { + if (c.getCount() > 0) { + c.moveToFirst(); + String json = c.getString(0); + if (json != null) { + config = ThemeConfig.fromJson(json); + } + } + c.close(); + } + } + } catch (Exception e) { + Log.w(TAG, "Unable to open " + SETTINGS_DB, e); + } finally { + if (db != null) { + db.close(); + } + } + + return config; + } } diff --git a/core/java/android/content/res/IThemeProcessingListener.aidl b/core/java/android/content/res/IThemeProcessingListener.aidl new file mode 100644 index 000000000000..2e1c16e53bff --- /dev/null +++ b/core/java/android/content/res/IThemeProcessingListener.aidl @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2014 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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.content.res; + +/** {@hide} */ +oneway interface IThemeProcessingListener { + void onFinishedProcessing(String pkgName); +} diff --git a/core/java/android/content/res/IThemeService.aidl b/core/java/android/content/res/IThemeService.aidl index c79379488213..e8bb5c4329ee 100644 --- a/core/java/android/content/res/IThemeService.aidl +++ b/core/java/android/content/res/IThemeService.aidl @@ -16,6 +16,7 @@ package android.content.res; import android.content.res.IThemeChangeListener; +import android.content.res.IThemeProcessingListener; import android.graphics.Bitmap; import java.util.Map; @@ -31,4 +32,9 @@ interface IThemeService { int getProgress(); boolean cacheComposedIcon(in Bitmap icon, String path); + + boolean processThemeResources(String themePkgName); + boolean isThemeBeingProcessed(String themePkgName); + void registerThemeProcessingListener(in IThemeProcessingListener listener); + void unregisterThemeProcessingListener(in IThemeProcessingListener listener); } diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 7cdd57151ed7..1b636a866a30 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -17,6 +17,7 @@ package android.content.res; import android.app.ComposedIconInfo; +import android.app.IconPackHelper; import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; @@ -727,7 +728,22 @@ public Drawable getDrawable(int id, boolean supportComposedIcons) throws NotFoun } getValue(id, value, true, supportComposedIcons); } - Drawable res = loadDrawable(value, id); + Drawable res = null; + try { + res = loadDrawable(value, id); + } catch (NotFoundException e) { + // The below statement will be true if we were trying to load a composed icon. + // Since we received a NotFoundException, try to load the original if this + // condition is true, otherwise throw the original exception. + if (supportComposedIcons && mComposedIconInfo != null && info != null && + info.themedIcon == 0) { + Log.e(TAG, "Failed to retrieve composed icon.", e); + getValue(id, value, true, false); + res = loadDrawable(value, id); + } else { + throw e; + } + } synchronized (mAccessLock) { if (mTmpValue == null) { mTmpValue = value; @@ -1169,8 +1185,8 @@ public void getValue(int id, TypedValue outValue, boolean resolveRefs, } boolean found = mAssets.getResourceValue(id, 0, outValue, resolveRefs); if (found) { - if (supportComposedIcons && mComposedIconInfo != null && info != null && - info.themedIcon == 0) { + if (supportComposedIcons && IconPackHelper.shouldComposeIcon(mComposedIconInfo) + && info != null && info.themedIcon == 0) { Drawable dr = loadDrawable(outValue, id); IconCustomizer.getValue(this, id, outValue, dr); } @@ -1207,9 +1223,29 @@ public void getValueForDensity(int id, int density, TypedValue outValue, boolean } boolean found = mAssets.getResourceValue(id, density, outValue, resolveRefs); if (found) { - if (supportComposedIcons && mComposedIconInfo != null && info != null && - info.themedIcon == 0) { + if (supportComposedIcons && IconPackHelper.shouldComposeIcon(mComposedIconInfo) && + info != null && info.themedIcon == 0) { + int tmpDensity = outValue.density; + /* + * Pretend the requested density is actually the display density. If + * the drawable returned is not the requested density, then force it + * to be scaled later by dividing its density by the ratio of + * requested density to actual device density. Drawables that have + * undefined density or no density don't need to be handled here. + */ + if (outValue.density > 0 && outValue.density != TypedValue.DENSITY_NONE) { + if (outValue.density == density) { + outValue.density = mMetrics.densityDpi; + } else { + outValue.density = (outValue.density * mMetrics.densityDpi) / density; + } + } Drawable dr = loadDrawable(outValue, id); + + // Return to original density. If we do not do this then + // the caller will get the wrong density for the given id and perform + // more of its own scaling in loadDrawable + outValue.density = tmpDensity; IconCustomizer.getValue(this, id, outValue, dr); } return; @@ -2081,14 +2117,19 @@ public LongSparseArray getPreloadedDrawables() { return sPreloadedDrawables[0]; } + static private final int CONFIG_FONT_SCALE = ActivityInfo.activityInfoConfigToNative( + ActivityInfo.CONFIG_FONT_SCALE); + static private final int CONFIG_DENSITY = ActivityInfo.activityInfoConfigToNative( + ActivityInfo.CONFIG_DENSITY); + static private final int SPEC_PUBLIC = 0x40000000; + private boolean verifyPreloadConfig(int changingConfigurations, int allowVarying, int resourceId, String name) { // We allow preloading of resources even if they vary by font scale (which // doesn't impact resource selection) or density (which we handle specially by // simply turning off all preloading), as well as any other configs specified // by the caller. - if (((changingConfigurations&~(ActivityInfo.CONFIG_FONT_SCALE | - ActivityInfo.CONFIG_DENSITY)) & ~allowVarying) != 0) { + if (((changingConfigurations&~(CONFIG_FONT_SCALE | CONFIG_DENSITY | SPEC_PUBLIC)) & ~allowVarying) != 0) { String resName; try { resName = getResourceName(resourceId); diff --git a/core/java/android/content/res/ThemeManager.java b/core/java/android/content/res/ThemeManager.java index b201bb7f7ed8..b01029a5b953 100644 --- a/core/java/android/content/res/ThemeManager.java +++ b/core/java/android/content/res/ThemeManager.java @@ -22,8 +22,10 @@ import android.os.RemoteException; import android.util.Log; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -37,9 +39,12 @@ public class ThemeManager { private IThemeService mService; private Handler mHandler; - private Set mListeners = + private Set mChangeListeners = new HashSet(); + private Set mProcessingListeners = + new HashSet(); + public ThemeManager(Context context, IThemeService service) { mContext = context; mService = service; @@ -52,11 +57,21 @@ public void onProgress(final int progress) throws RemoteException { mHandler.post(new Runnable() { @Override public void run() { - for (ThemeChangeListener listener : mListeners) { - try { - listener.onProgress(progress); - } catch (Throwable e) { - Log.w(TAG, "Unable to update theme change progress", e); + synchronized (mChangeListeners) { + List listenersToRemove = new ArrayList + (); + for (ThemeChangeListener listener : mChangeListeners) { + try { + listener.onProgress(progress); + } catch (Throwable e) { + Log.w(TAG, "Unable to update theme change progress", e); + listenersToRemove.add(listener); + } + } + if (listenersToRemove.size() > 0) { + for (ThemeChangeListener listener : listenersToRemove) { + mChangeListeners.remove(listener); + } } } } @@ -68,11 +83,21 @@ public void onFinish(final boolean isSuccess) throws RemoteException { mHandler.post(new Runnable() { @Override public void run() { - for (ThemeChangeListener listener : mListeners) { - try { - listener.onFinish(isSuccess); - } catch (Throwable e) { - Log.w(TAG, "Unable to update theme change listener", e); + synchronized (mChangeListeners) { + List listenersToRemove = new ArrayList + (); + for (ThemeChangeListener listener : mChangeListeners) { + try { + listener.onFinish(isSuccess); + } catch (Throwable e) { + Log.w(TAG, "Unable to update theme change listener", e); + listenersToRemove.add(listener); + } + } + if (listenersToRemove.size() > 0) { + for (ThemeChangeListener listener : listenersToRemove) { + mChangeListeners.remove(listener); + } } } } @@ -80,27 +105,61 @@ public void run() { } }; - public void addClient(ThemeChangeListener listener) { - if (mListeners.contains(listener)) { - throw new IllegalArgumentException("Client was already added "); + private final IThemeProcessingListener mThemeProcessingListener = + new IThemeProcessingListener.Stub() { + @Override + public void onFinishedProcessing(final String pkgName) throws RemoteException { + mHandler.post(new Runnable() { + @Override + public void run() { + synchronized (mProcessingListeners) { + List listenersToRemove = new ArrayList + (); + for (ThemeProcessingListener listener : mProcessingListeners) { + try { + listener.onFinishedProcessing(pkgName); + } catch (Throwable e) { + Log.w(TAG, "Unable to update theme change progress", e); + listenersToRemove.add(listener); + } + } + if (listenersToRemove.size() > 0) { + for (ThemeProcessingListener listener : listenersToRemove) { + mProcessingListeners.remove(listener); + } + } + } + } + }); } - if (mListeners.size() == 0) { - try { - mService.requestThemeChangeUpdates(mThemeChangeListener); - } catch (RemoteException e) { - Log.w(TAG, "Unable to register listener", e); + }; + + + public void addClient(ThemeChangeListener listener) { + synchronized (mChangeListeners) { + if (mChangeListeners.contains(listener)) { + throw new IllegalArgumentException("Client was already added "); } + if (mChangeListeners.size() == 0) { + try { + mService.requestThemeChangeUpdates(mThemeChangeListener); + } catch (RemoteException e) { + Log.w(TAG, "Unable to register listener", e); + } + } + mChangeListeners.add(listener); } - mListeners.add(listener); } public void removeClient(ThemeChangeListener listener) { - mListeners.remove(listener); - if (mListeners.size() == 0) { - try { - mService.removeUpdates(mThemeChangeListener); - } catch (RemoteException e) { - Log.w(TAG, "Unable to remove listener", e); + synchronized (mChangeListeners) { + mChangeListeners.remove(listener); + if (mChangeListeners.size() == 0) { + try { + mService.removeUpdates(mThemeChangeListener); + } catch (RemoteException e) { + Log.w(TAG, "Unable to remove listener", e); + } } } } @@ -117,6 +176,43 @@ public void onClientDestroyed(ThemeChangeListener listener) { removeClient(listener); } + /** + * Register a ThemeProcessingListener to be notified when a theme is done being processed. + * @param listener ThemeChangeListener to register + */ + public void registerProcessingListener(ThemeProcessingListener listener) { + synchronized (mProcessingListeners) { + if (mProcessingListeners.contains(listener)) { + throw new IllegalArgumentException("Listener was already added "); + } + if (mProcessingListeners.size() == 0) { + try { + mService.registerThemeProcessingListener(mThemeProcessingListener); + } catch (RemoteException e) { + Log.w(TAG, "Unable to register listener", e); + } + } + mProcessingListeners.add(listener); + } + } + + /** + * Unregister a ThemeChangeListener. + * @param listener ThemeChangeListener to unregister + */ + public void unregisterProcessingListener(ThemeChangeListener listener) { + synchronized (mProcessingListeners) { + mProcessingListeners.remove(listener); + if (mProcessingListeners.size() == 0) { + try { + mService.unregisterThemeProcessingListener(mThemeProcessingListener); + } catch (RemoteException e) { + Log.w(TAG, "Unable to remove listener", e); + } + } + } + } + /** * Convenience method. Applies the entire theme. */ @@ -137,7 +233,7 @@ public void requestThemeChange(Map componentMap) { try { mService.requestThemeChange(componentMap); } catch (RemoteException e) { - Log.w(TAG, "Unable to access ThemeService", e); + logThemeServiceException(e); } } @@ -145,7 +241,7 @@ public void applyDefaultTheme() { try { mService.applyDefaultTheme(); } catch (RemoteException e) { - Log.w(TAG, "Unable to access ThemeService", e); + logThemeServiceException(e); } } @@ -153,23 +249,50 @@ public boolean isThemeApplying() { try { return mService.isThemeApplying(); } catch (RemoteException e) { - Log.w(TAG, "Unable to access ThemeService", e); + logThemeServiceException(e); } return false; } + public boolean isThemeBeingProcessed(String themePkgName) { + try { + return mService.isThemeBeingProcessed(themePkgName); + } catch (RemoteException e) { + logThemeServiceException(e); + } + return false; + } + public int getProgress() { try { return mService.getProgress(); } catch (RemoteException e) { - Log.w(TAG, "Unable to access ThemeService", e); + logThemeServiceException(e); } return -1; } + public boolean processThemeResources(String themePkgName) { + try { + return mService.processThemeResources(themePkgName); + } catch (RemoteException e) { + logThemeServiceException(e); + } + return false; + } + + private void logThemeServiceException(Exception e) { + Log.w(TAG, "Unable to access ThemeService", e); + } + public interface ThemeChangeListener { void onProgress(int progress); void onFinish(boolean isSuccess); } + + public interface ThemeProcessingListener { + void onFinishedProcessing(String pkgName); + } } + diff --git a/core/java/android/gesture/Gesture.aidl b/core/java/android/gesture/Gesture.aidl new file mode 100644 index 000000000000..4383ac051d8d --- /dev/null +++ b/core/java/android/gesture/Gesture.aidl @@ -0,0 +1,3 @@ +package android.gesture; + +parcelable Gesture; diff --git a/core/java/android/gesture/GestureOverlayView.java b/core/java/android/gesture/GestureOverlayView.java index 2d47f289b38e..edba8e3586fe 100644 --- a/core/java/android/gesture/GestureOverlayView.java +++ b/core/java/android/gesture/GestureOverlayView.java @@ -87,6 +87,8 @@ public class GestureOverlayView extends FrameLayout { private final Rect mInvalidRect = new Rect(); private final Path mPath = new Path(); private boolean mGestureVisible = true; + protected boolean mClearPerformedGesture = true; + protected boolean mInputEnabled = true; private float mX; private float mY; @@ -201,6 +203,7 @@ public void setOrientation(int orientation) { public void setGestureColor(int color) { mCertainGestureColor = color; + setCurrentColor(color); } public void setUncertainGestureColor(int color) { @@ -492,7 +495,7 @@ protected void onDetachedFromWindow() { @Override public boolean dispatchTouchEvent(MotionEvent event) { - if (isEnabled()) { + if (isEnabled() && mInputEnabled) { final boolean cancelDispatch = (mIsGesturing || (mCurrentGesture != null && mCurrentGesture.getStrokesCount() > 0 && mPreviousWasGesturing)) && mInterceptEvents; @@ -568,7 +571,7 @@ private void touchDown(MotionEvent event) { // if there is fading out going on, stop it. if (mFadingHasStarted) { cancelClearAnimation(); - } else if (mIsFadingOut) { + } else if (mIsFadingOut || !mClearPerformedGesture) { setPaintAlpha(255); mIsFadingOut = false; mFadingHasStarted = false; @@ -690,8 +693,13 @@ private void touchUp(MotionEvent event, boolean cancel) { listeners.get(i).onGestureEnded(this, event); } - clear(mHandleGestureActions && mFadeEnabled, mHandleGestureActions && mIsGesturing, - false); + if (mClearPerformedGesture) + clear(mHandleGestureActions && mFadeEnabled, mHandleGestureActions && mIsGesturing, + false); + else if (mHandleGestureActions && mIsGesturing) { + mIsFadingOut = false; + postDelayed(mFadingOut, mFadeOffset); + } } else { cancelGesture(event); @@ -764,9 +772,12 @@ public void run() { fireOnGesturePerformed(); mFadingHasStarted = false; - mPath.rewind(); - mCurrentGesture = null; - mPreviousWasGesturing = false; + if (mClearPerformedGesture) { + mPath.rewind(); + mCurrentGesture = null; + mPreviousWasGesturing = false; + } else + mResetGesture = true; setPaintAlpha(255); } diff --git a/core/java/android/net/LocalSocket.java b/core/java/android/net/LocalSocket.java index 31bc20b03501..87a92ca39585 100644 --- a/core/java/android/net/LocalSocket.java +++ b/core/java/android/net/LocalSocket.java @@ -185,6 +185,19 @@ public OutputStream getOutputStream() throws IOException { return impl.getOutputStream(); } + /** + * Set the flag to close the fd whcih was opened + * externally + * + * @return none + * @throws IOException if socket has been closed + * @hide + */ + public void closeExternalFd() throws IOException { + implCreateIfNeeded(); + impl.closeExternalFd(); + } + /** * Closes the socket. * diff --git a/core/java/android/net/LocalSocketImpl.java b/core/java/android/net/LocalSocketImpl.java index 2e5d8dbdb477..4be83cf8e9b8 100644 --- a/core/java/android/net/LocalSocketImpl.java +++ b/core/java/android/net/LocalSocketImpl.java @@ -42,6 +42,8 @@ class LocalSocketImpl /** whether fd is created internally */ private boolean mFdCreatedInternally; + private boolean mFdCreatedExternally = false; + // These fields are accessed by native code; /** file descriptor array received during a previous read */ FileDescriptor[] inboundFileDescriptors; @@ -266,7 +268,8 @@ public void create (int sockType) throws IOException { */ public void close() throws IOException { synchronized (LocalSocketImpl.this) { - if ((fd == null) || (mFdCreatedInternally == false)) { + if ((fd == null) || ((mFdCreatedInternally == false) && + (mFdCreatedExternally == false))) { fd = null; return; } @@ -374,6 +377,21 @@ protected OutputStream getOutputStream() throws IOException } } + /** + * Set the flag to close the fd which was opened + * externally. + * + * @return none + * @throws IOException if socket has been closed + */ + protected void closeExternalFd() throws IOException + { + if (fd == null) { + throw new IOException("socket not created"); + } + mFdCreatedExternally = true; + } + /** * Returns the number of bytes available for reading without blocking. * diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java index fb8f64a01cba..0197d73f3f87 100644 --- a/core/java/android/net/MobileDataStateTracker.java +++ b/core/java/android/net/MobileDataStateTracker.java @@ -400,6 +400,7 @@ public String getTcpBufferSizesPropName() { networkTypeStr = "edge"; break; case TelephonyManager.NETWORK_TYPE_UMTS: + case TelephonyManager.NETWORK_TYPE_TD_SCDMA: networkTypeStr = "umts"; break; case TelephonyManager.NETWORK_TYPE_HSDPA: diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index 4e9e0f583c6c..5123355ed44c 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -57,4 +57,5 @@ interface IPowerManager void cpuBoost(int duration); void setKeyboardVisibility(boolean visible); + void wakeUpWithProximityCheck(long time); } diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 94b961793e26..25e6aa56db3f 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -599,7 +599,7 @@ public final void writeMap(Map val) { * Flatten an ArrayMap into the parcel at the current dataPosition(), * growing dataCapacity() if needed. The Map keys must be String objects. */ - /* package */ void writeArrayMapInternal(ArrayMap val) { + /* package */ void writeArrayMapInternal(ArrayMap val) { if (val == null) { writeInt(-1); return; @@ -614,7 +614,7 @@ public final void writeMap(Map val) { int startPos; for (int i=0; i val) { + writeArrayMapInternal(val); + } + /** * Flatten a Bundle into the parcel at the current dataPosition(), * growing dataCapacity() if needed. @@ -2310,7 +2317,7 @@ protected void finalize() throws Throwable { int startPos; while (N > 0) { if (DEBUG_ARRAY_MAP) startPos = dataPosition(); - Object key = readValue(loader); + String key = readString(); Object value = readValue(loader); if (DEBUG_ARRAY_MAP) Log.d(TAG, " Read #" + (N-1) + " " + (dataPosition()-startPos) + " bytes: key=0x" @@ -2318,6 +2325,7 @@ protected void finalize() throws Throwable { outVal.append(key, value); N--; } + outVal.validate(); } /* package */ void readArrayMapSafelyInternal(ArrayMap outVal, int N, @@ -2328,7 +2336,7 @@ protected void finalize() throws Throwable { Log.d(TAG, "Reading safely " + N + " ArrayMap entries", here); } while (N > 0) { - Object key = readValue(loader); + String key = readString(); if (DEBUG_ARRAY_MAP) Log.d(TAG, " Read safe #" + (N-1) + ": key=0x" + (key != null ? key.hashCode() : 0) + " " + key); Object value = readValue(loader); @@ -2337,6 +2345,17 @@ protected void finalize() throws Throwable { } } + /** + * @hide For testing only. + */ + public void readArrayMap(ArrayMap outVal, ClassLoader loader) { + final int N = readInt(); + if (N < 0) { + return; + } + readArrayMapInternal(outVal, N, loader); + } + private void readListInternal(List outVal, int N, ClassLoader loader) { while (N > 0) { diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 29960e258d30..10c3955152cf 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -349,20 +349,20 @@ public int getMinimumAbsoluteScreenBrightness() { /** * Returns true if the screen auto-brightness adjustment setting should - * be available in the UI. This setting is experimental and disabled by default. + * be available in the UI. * @hide */ public static boolean useScreenAutoBrightnessAdjustmentFeature() { - return SystemProperties.getBoolean("persist.power.useautobrightadj", false); + return true; } /** - * Returns true if the twilight service should be used to adjust screen brightness - * policy. This setting is experimental and disabled by default. + * Returns true if the twilight service should be used to adjust + * screen brightness policy. * @hide */ public static boolean useTwilightAdjustmentFeature() { - return SystemProperties.getBoolean("persist.power.usetwilightadj", false); + return true; } /** @@ -520,6 +520,19 @@ public void wakeUp(long time) { } } + /** + * Forces the device to wake up from sleep only if + * nothing is blocking the proximity sensor + * @see #wakeUp + * @hide + */ + public void wakeUpWithProximityCheck(long time) { + try { + mService.wakeUpWithProximityCheck(time); + } catch (RemoteException e) { + } + } + /** * Forces the device to start napping. *

@@ -870,4 +883,22 @@ public void setKeyboardVisibility(boolean visible) } catch (RemoteException e) { } } + + /** + * Gets the default button brightness value. + * @hide + */ + public int getDefaultButtonBrightness() { + return mContext.getResources().getInteger( + com.android.internal.R.integer.config_buttonBrightnessSettingDefault); + } + + /** + * Gets the default keyboard brightness value. + * @hide + */ + public int getDefaultKeyboardBrightness() { + return mContext.getResources().getInteger( + com.android.internal.R.integer.config_keyboardBrightnessSettingDefault); + } } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 57abe4f8ccd9..cade2f3bbb6c 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -722,7 +722,7 @@ public final class Settings { */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_NOTIFICATION_LISTENER_SETTINGS - = "android.settings.NOTIFICATION_LISTENER_SETTINGS"; + = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"; /** * Activity Action: Show settings for video captioning. @@ -1035,7 +1035,7 @@ public static final class System extends NameValueTable { MOVED_TO_SECURE.add(Secure.LOCK_PATTERN_ENABLED); MOVED_TO_SECURE.add(Secure.LOCK_PATTERN_VISIBLE); MOVED_TO_SECURE.add(Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED); - MOVED_TO_SECURE.add(Secure.LOCK_NUMPAD_RANDOM); + MOVED_TO_SECURE.add(Secure.LOCK_PATTERN_SIZE); MOVED_TO_SECURE.add(Secure.LOGGING_ID); MOVED_TO_SECURE.add(Secure.PARENTAL_CONTROL_ENABLED); MOVED_TO_SECURE.add(Secure.PARENTAL_CONTROL_LAST_UPDATE); @@ -2771,6 +2771,8 @@ public static final class Secure extends NameValueTable { MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_PATTERN_ENABLED); MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_PATTERN_VISIBLE); MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED); + MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_GESTURE_ENABLED); + MOVED_TO_LOCK_SETTINGS.add(Secure.LOCK_GESTURE_VISIBLE); MOVED_TO_GLOBAL = new HashSet(); MOVED_TO_GLOBAL.add(Settings.Global.ADB_ENABLED); @@ -3426,6 +3428,12 @@ public static boolean putFloatForUser(ContentResolver cr, String name, float val */ public static final String LOCK_PATTERN_VISIBLE = "lock_pattern_visible_pattern"; + /** + * Determines the width and height of the LockPatternView widget + * @hide + */ + public static final String LOCK_PATTERN_SIZE = "lock_pattern_size"; + /** * Whether the NumKeyPad will change the orders of numbers * in a PIN locked lockscreen @@ -3447,6 +3455,18 @@ public static boolean putFloatForUser(ContentResolver cr, String name, float val public static final String LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED = "lock_pattern_tactile_feedback_enabled"; + /** + * Whether autolock is enabled (0 = false, 1 = true) + * @hide + */ + public static final String LOCK_GESTURE_ENABLED = "lock_gesture_autolock"; + + /** + * Whether lock gesture is visible as user enters (0 = false, 1 = true) + * @hide + */ + public static final String LOCK_GESTURE_VISIBLE = "lock_gesture_visible_pattern"; + /** * This preference allows the device to be locked given time after screen goes off, * subject to current DeviceAdmin policy limits. @@ -6802,6 +6822,12 @@ public static ArrayList getArrayListForUser(ContentResolver cr, String n */ public static final String QUIET_HOURS_DIM = "quiet_hours_dim"; + /** + * Whether to remove the system sounds during quiet hours. + * @hide + */ + public static final String QUIET_HOURS_SYSTEM = "quiet_hours_system"; + /** * Immersive Mode * @hide @@ -7150,6 +7176,13 @@ public static ArrayList getArrayListForUser(ContentResolver cr, String n */ public static final String SMART_PHONE_CALLER = "smart_phone_caller"; + /** + * Detailed incall info + * + * @hide + */ + public static final String DETAILED_INCALL_INFO = "detailed_incall_info"; + /** * The hostname for this device * @hide @@ -7222,6 +7255,14 @@ public static ArrayList getArrayListForUser(ContentResolver cr, String n */ public static final String LOCATION_MODES_TOGGLE = "location_modes_toggle"; + /** + * Network modes toggle + * States of network devided by | + * Like 9|0|1 + * @hide + */ + public static final String NETWORK_MODES_TOGGLE = "network_modes_toggle"; + /** * Whether to enable the navbar for hw key devices * @hide @@ -7490,6 +7531,14 @@ public static ArrayList getArrayListForUser(ContentResolver cr, String n */ public static final String BATTERY_CHARGING_LED_ENABLED = "battery_charging_led_enabled"; + /** + * Boolean value. Whether to show the 4G icon when on LTE. + * True = show 4G + * False = show LTE + * @hide + */ + public static final String STATUSBAR_SIGNAL_SHOW_4G_FOR_LTE = "statusbar_signal_show_4g_for_lte"; + /** * Enable / disable navring * @hide @@ -7588,6 +7637,64 @@ public static ArrayList getArrayListForUser(ContentResolver cr, String n */ public static final String VOLUME_KEY_CURSOR_CONTROL = "volume_key_cursor_control"; + /** + * Custom automatic brightness light sensor levels. + * The value is a comma separated int array with length N. + * Example: "100,300,3000". + * + * @hide + */ + public static final String AUTO_BRIGHTNESS_LUX = "auto_brightness_lux"; + + /** + * Custom automatic brightness display backlight brightness values. + * The value is a comma separated int array with length N+1. + * Example: "10,50,100,255". + * + * @hide + */ + public static final String AUTO_BRIGHTNESS_BACKLIGHT = "auto_brightness_backlight"; + + /** + * Correction factor for auto-brightness adjustment light sensor + * debounce times. + * Smaller factors will make the adjustment more responsive, but might + * cause flicker and/or cause higher CPU usage. + * Valid range is 0.2 ... 3 + * + * @hide + */ + public static final String AUTO_BRIGHTNESS_RESPONSIVENESS = "auto_brightness_responsiveness"; + + /** + * Whether to enable adjustment of automatic brightness adjustment + * to sunrise and sunset. + * @hide + */ + public static final String AUTO_BRIGHTNESS_TWILIGHT_ADJUSTMENT = "auto_brightness_twilight_adjustment"; + + /** + * The keyboard brightness to be used while the screen is on. + * Valid value range is between 0 and {@link PowerManager#getMaximumKeyboardBrightness()} + * @hide + */ + public static final String KEYBOARD_BRIGHTNESS = "keyboard_brightness"; + + /** + * The button brightness to be used while the screen is on or after a button press, + * depending on the value of {@link BUTTON_BACKLIGHT_TIMEOUT}. + * Valid value range is between 0 and {@link PowerManager#getMaximumButtonBrightness()} + * @hide + */ + public static final String BUTTON_BRIGHTNESS = "button_brightness"; + + /** + * The time in ms to keep the button backlight on after pressing a button. + * A value of 0 will keep the buttons on for as long as the screen is on. + * @hide + */ + public static final String BUTTON_BACKLIGHT_TIMEOUT = "button_backlight_timeout"; + /** * * Smooth Progress Bar Mirror @@ -7809,6 +7916,25 @@ public static ArrayList getArrayListForUser(ContentResolver cr, String n */ public static final String PHONE_BLACKLIST_REGEX_ENABLED = "phone_blacklist_regex_enabled"; + /** + * @hide + */ + public static final String PROXIMITY_ON_WAKE = "proximity_on_wake"; + + /** + * Boolean value whether to link ringtone and notification volumes + * + * @hide + */ + public static final String VOLUME_LINK_NOTIFICATION = "volume_link_notification"; + + /** + * Volume key controls ringtone or media sound stream + * + * @hide + */ + public static final String VOLUME_KEYS_CONTROL_RING_STREAM = "volume_keys_control_ring_stream"; + } /** diff --git a/core/java/android/provider/ThemesContract.java b/core/java/android/provider/ThemesContract.java index d01d230a3812..2c26ff465fab 100644 --- a/core/java/android/provider/ThemesContract.java +++ b/core/java/android/provider/ThemesContract.java @@ -227,6 +227,14 @@ public static class ThemesColumns { *

Default: 0

*/ public static final String LAST_UPDATE_TIME = "updateTime"; + + /** + * install time in millisecs. When the row is inserted this column + * is populated by the PackageInfo. + *

Type: INTEGER

+ *

Default: 0

+ */ + public static final String INSTALL_TIME = "install_time"; } /** @@ -454,6 +462,12 @@ public static class PreviewColumns { */ public static final String STATUSBAR_CLOCK_TEXT_COLOR = "statusbar_clock_text_color"; + /** + * Cached image of the themed navigation bar background. + *

Type: BLOB (bitmap)

+ */ + public static final String NAVBAR_BACKGROUND = "navbar_background"; + /** * Cached image of the themed back button. *

Type: BLOB (bitmap)

@@ -502,6 +516,12 @@ public static class PreviewColumns { */ public static final String STYLE_PREVIEW = "style_preview"; + /** + * Cached thumbnail preview of UI controls representing the theme's style + *

Type: BLOB (bitmap)

+ */ + public static final String STYLE_THUMBNAIL = "style_thumbnail"; + /** * Cached thumbnail of the theme's boot animation *

Type: BLOB (bitmap)

diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java index df1d4cd7047a..8ea23ba12c5c 100644 --- a/core/java/android/util/ArrayMap.java +++ b/core/java/android/util/ArrayMap.java @@ -493,6 +493,44 @@ public void append(K key, V value) { mArray[index+1] = value; } + /** + * The use of the {@link #append} function can result in invalid array maps, in particular + * an array map where the same key appears multiple times. This function verifies that + * the array map is valid, throwing IllegalArgumentException if a problem is found. The + * main use for this method is validating an array map after unpacking from an IPC, to + * protect against malicious callers. + * @hide + */ + public void validate() { + final int N = mSize; + if (N <= 1) { + // There can't be dups. + return; + } + int basehash = mHashes[0]; + int basei = 0; + for (int i=1; i=basei; j--) { + final Object prev = mArray[j<<1]; + if (cur == prev) { + throw new IllegalArgumentException("Duplicate key in ArrayMap: " + cur); + } + if (cur != null && prev != null && cur.equals(prev)) { + throw new IllegalArgumentException("Duplicate key in ArrayMap: " + cur); + } + } + } + } + /** * Perform a {@link #put(Object, Object)} of all key/value pairs in array * @param array The array whose contents are to be retrieved. diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java old mode 100644 new mode 100755 index d779628571bf..ca3d89e93d7c --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1565,6 +1565,9 @@ private void performTraversals() { // Our surface is gone if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) { + // Destroy hardware layers before hardware renderer is destroyed + mAttachInfo.mHardwareRenderer.destroyHardwareResources(mView); + mAttachInfo.mHardwareRenderer.destroy(true); } } else if (surfaceGenerationId != mSurface.getGenerationId() && diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java index de164071e323..09888af0b266 100644 --- a/core/java/android/view/VolumePanel.java +++ b/core/java/android/view/VolumePanel.java @@ -44,6 +44,8 @@ import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; +import com.android.internal.util.aokp.QuietHoursHelper; + import java.util.HashMap; /** @@ -765,11 +767,13 @@ protected void onPlaySound(int streamType, int flags) { onStopSounds(); } - synchronized (this) { - ToneGenerator toneGen = getOrCreateToneGenerator(streamType); - if (toneGen != null) { - toneGen.startTone(ToneGenerator.TONE_PROP_BEEP); - sendMessageDelayed(obtainMessage(MSG_STOP_SOUNDS), BEEP_DURATION); + if (!QuietHoursHelper.inQuietHours(mContext, Settings.AOKP.QUIET_HOURS_SYSTEM)) { + synchronized (this) { + ToneGenerator toneGen = getOrCreateToneGenerator(streamType); + if (toneGen != null) { + toneGen.startTone(ToneGenerator.TONE_PROP_BEEP); + sendMessageDelayed(obtainMessage(MSG_STOP_SOUNDS), BEEP_DURATION); + } } } } diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index b4b208cb16fb..8df650d3212b 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -2319,7 +2319,9 @@ View obtainView(int position, boolean[] isScrap) { lp = (LayoutParams) vlp; } lp.itemId = mAdapter.getItemId(position); - child.setLayoutParams(lp); + if (lp != vlp) { + child.setLayoutParams(lp); + } } if (AccessibilityManager.getInstance(mContext).isEnabled()) { diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java index fe2fc965848b..1185b67b43e4 100644 --- a/core/java/android/widget/AbsSeekBar.java +++ b/core/java/android/widget/AbsSeekBar.java @@ -448,7 +448,14 @@ private void trackTouchEvent(MotionEvent event) { final int max = getMax(); progress += scale * max; - setProgress((int) progress, true); + setProgress(updateTouchProgress(getProgress(), (int) progress), true); + } + + /** + * @hide + */ + protected int updateTouchProgress(int lastProgress, int newProgress) { + return newProgress; } /** diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index ad60a953dafa..55dc589d4152 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -801,9 +801,32 @@ void measureVertical(int widthMeasureSpec, int heightMeasureSpec) { int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0); heightSize = heightSizeAndState & MEASURED_SIZE_MASK; + int delta = heightSize - mTotalLength; + + // When children measure exactly the same height than the parent, any child with + // height == 0 and with weight > 0 must be reset. Otherwise, the view will retains + // the same height between measurements calls (that is not valid if the view changed + // its orientation) + if (delta == 0 && totalWeight > 0.0f) { + for (int i = 0; i < count; ++i) { + final View child = getVirtualChildAt(i); + + if (child == null || child.getVisibility() == View.GONE) { + continue; + } + + final LinearLayout.LayoutParams lp = + (LinearLayout.LayoutParams) child.getLayoutParams(); + + if (heightMode == MeasureSpec.EXACTLY && lp.height == 0 && lp.weight > 0) { + final int freeSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + child.measure(freeSpec, freeSpec); + } + } + } + // Either expand children with weight to take up available space or // shrink them if they extend beyond our current bounds - int delta = heightSize - mTotalLength; if (delta != 0 && totalWeight > 0.0f) { float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight; @@ -1178,10 +1201,33 @@ void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) { // Reconcile our calculated size with the widthMeasureSpec int widthSizeAndState = resolveSizeAndState(widthSize, widthMeasureSpec, 0); widthSize = widthSizeAndState & MEASURED_SIZE_MASK; - + + int delta = widthSize - mTotalLength; + + // When children measure exactly the same width than the parent, any child with + // width == 0 and with weight > 0 must be reset. Otherwise, the view will retains + // the same width between measurements calls (that is not valid if the view changed + // its orientation). If the view is baselineAligned, a precalculation was made previously + if (!baselineAligned && delta == 0 && totalWeight > 0.0f) { + for (int i = 0; i < count; ++i) { + final View child = getVirtualChildAt(i); + + if (child == null || child.getVisibility() == View.GONE) { + continue; + } + + final LinearLayout.LayoutParams lp = + (LinearLayout.LayoutParams) child.getLayoutParams(); + + if (widthMode == MeasureSpec.EXACTLY && lp.width == 0 && lp.weight > 0) { + final int freeSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + child.measure(freeSpec, freeSpec); + } + } + } + // Either expand children with weight to take up available space or // shrink them if they extend beyond our current bounds - int delta = widthSize - mTotalLength; if (delta != 0 && totalWeight > 0.0f) { float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight; diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java index 66fe46f29fce..b0c46a0bc04a 100644 --- a/core/java/android/widget/ListPopupWindow.java +++ b/core/java/android/widget/ListPopupWindow.java @@ -1,5 +1,9 @@ /* * Copyright (C) 2010 The Android Open Source Project + * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * + * Not a Contribution. Apache license notifications and license are retained + * for attribution purposes only. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -747,6 +751,14 @@ public boolean performItemClick(int position) { if (isShowing()) { if (mItemClickListener != null) { final DropDownListView list = mDropDownList; + + // Sometimes the perform item is not visible in ListView then + // 'list.getChildAt' method will return null. So we should call + // 'list.setSelectionInt' method to make sure the item is + // visible. + if (list.getLastVisiblePosition() < position) { + list.setSelectionInt(position); + } final View child = list.getChildAt(position - list.getFirstVisiblePosition()); final ListAdapter adapter = list.getAdapter(); mItemClickListener.onItemClick(list, child, position, adapter.getItemId(position)); diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java index c0fde2e51fb7..35efc3afdc5a 100644 --- a/core/java/android/widget/NumberPicker.java +++ b/core/java/android/widget/NumberPicker.java @@ -1,5 +1,9 @@ /* * Copyright (C) 2008 The Android Open Source Project + * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * + * Not a Contribution. Apache license notifications and license are retained + * for attribution purposes only. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -680,6 +684,11 @@ public void onFocusChange(View v, boolean hasFocus) { } else { mInputText.setSelection(0, 0); validateInputTextView(v); + // When mInputText isn't on focus ,hide the soft input. + InputMethodManager inputMethodManager = InputMethodManager.peekInstance(); + if (inputMethodManager != null && inputMethodManager.isActive(mInputText)) { + inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0); + } } } }); diff --git a/core/java/android/widget/QuickContactBadge.java b/core/java/android/widget/QuickContactBadge.java index fd2f7549e70a..b5d8fa63a89a 100644 --- a/core/java/android/widget/QuickContactBadge.java +++ b/core/java/android/widget/QuickContactBadge.java @@ -316,8 +316,11 @@ protected void onQueryComplete(int token, Object cookie, Cursor cursor) { try { switch(token) { case TOKEN_PHONE_LOOKUP_AND_TRIGGER: - trigger = true; - createUri = Uri.fromParts("tel", extras.getString(EXTRA_URI_CONTENT), null); + String contactExtra = extras.getString(EXTRA_URI_CONTENT); + if (contactExtra != null) { + trigger = true; + createUri = Uri.fromParts("tel", contactExtra, null); + } //$FALL-THROUGH$ case TOKEN_PHONE_LOOKUP: { diff --git a/core/java/android/widget/TextSwitcher.java b/core/java/android/widget/TextSwitcher.java index 1aefd2bd1d18..9963247f6e0d 100644 --- a/core/java/android/widget/TextSwitcher.java +++ b/core/java/android/widget/TextSwitcher.java @@ -77,8 +77,10 @@ public void addView(View child, int index, ViewGroup.LayoutParams params) { */ public void setText(CharSequence text) { final TextView t = (TextView) getNextView(); - t.setText(text); - showNext(); + if (t != null) { + t.setText(text); + showNext(); + } } /** diff --git a/core/java/com/android/internal/app/AOKPLogoActivity.java b/core/java/com/android/internal/app/AOKPLogoActivity.java deleted file mode 100644 index a4eeb97b26e0..000000000000 --- a/core/java/com/android/internal/app/AOKPLogoActivity.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.app; - -import android.app.Activity; -import android.content.ActivityNotFoundException; -import android.content.Intent; -import android.graphics.Typeface; -import android.os.Bundle; -import android.os.Handler; -import android.util.DisplayMetrics; -import android.view.Gravity; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; -import android.widget.Toast; - -public class AOKPLogoActivity extends Activity { - Toast mToast; - ImageView mContent; - int mCount; - final Handler mHandler = new Handler(); - - private View makeView() { - DisplayMetrics metrics = new DisplayMetrics(); - getWindowManager().getDefaultDisplay().getMetrics(metrics); - - LinearLayout view = new LinearLayout(this); - view.setOrientation(LinearLayout.VERTICAL); - view.setLayoutParams( - new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT - )); - final int p = (int)(8 * metrics.density); - view.setPadding(p, p, p, p); - - Typeface light = Typeface.create("sans-serif-light", Typeface.NORMAL); - Typeface normal = Typeface.create("sans-serif", Typeface.BOLD); - - final float size = 14 * metrics.density; - final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.WRAP_CONTENT, - LinearLayout.LayoutParams.WRAP_CONTENT); - lp.gravity = Gravity.CENTER_HORIZONTAL; - lp.bottomMargin = (int) (-4*metrics.density); - - TextView tv = new TextView(this); - if (light != null) tv.setTypeface(light); - tv.setTextSize(1.25f*size); - tv.setTextColor(0xFFFFFFFF); - tv.setShadowLayer(4*metrics.density, 0, 2*metrics.density, 0x66000000); - tv.setText("Android Open Kang Project"); - view.addView(tv, lp); - - tv = new TextView(this); - if (normal != null) tv.setTypeface(normal); - tv.setTextSize(size); - tv.setTextColor(0xFFFFFFFF); - tv.setShadowLayer(4*metrics.density, 0, 2*metrics.density, 0x66000000); - tv.setText(""); - view.addView(tv, lp); - - return view; - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mToast = Toast.makeText(this, "", Toast.LENGTH_LONG); - mToast.setView(makeView()); - - DisplayMetrics metrics = new DisplayMetrics(); - getWindowManager().getDefaultDisplay().getMetrics(metrics); - - mContent = new ImageView(this); - mContent.setImageResource(com.android.internal.R.drawable.aokplogo_alt); - mContent.setScaleType(ImageView.ScaleType.CENTER_INSIDE); - - final int p = (int)(32 * metrics.density); - mContent.setPadding(p, p, p, p); - - mContent.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - mToast.show(); - mContent.setImageResource(com.android.internal.R.drawable.aokplogo); - } - }); - - mContent.setOnLongClickListener(new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - try { - startActivity(new Intent(Intent.ACTION_MAIN) - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_CLEAR_TASK - | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) - .addCategory("com.android.internal.category.AOKPLOGO")); - //.setClassName("com.android.systemui","com.android.systemui.UnicornSack")); - } catch (ActivityNotFoundException ex) { - android.util.Log.e("AOKPLogoActivity", "Couldn't find a sack of unicorns."); - } - finish(); - return true; - } - }); - - setContentView(mContent); - } -} diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java index be4aab9a4906..4eceefb9ed67 100644 --- a/core/java/com/android/internal/app/PlatLogoActivity.java +++ b/core/java/com/android/internal/app/PlatLogoActivity.java @@ -1,5 +1,7 @@ /* * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2013-2014 The CyanogenMod Project + * Copyright (C) 2014 The Android Open Kang Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +27,7 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.os.SystemProperties; import android.text.method.AllCapsTransformationMethod; import android.text.method.TransformationMethod; import android.util.DisplayMetrics; @@ -44,12 +47,14 @@ public class PlatLogoActivity extends Activity { FrameLayout mContent; int mCount; final Handler mHandler = new Handler(); + private boolean mIsAOKP; static final int BGCOLOR = 0xffed1d24; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + mIsAOKP = getIntent().hasExtra("is_aokp"); DisplayMetrics metrics = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(metrics); @@ -58,14 +63,21 @@ protected void onCreate(Bundle savedInstanceState) { mContent = new FrameLayout(this); mContent.setBackgroundColor(0xC0000000); - + final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams( FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT); lp.gravity = Gravity.CENTER; + // Add some padding to the platlogo for devices where the + // width of the logo is bigger than the device width + int p = (int) (20 * metrics.density); + final ImageView logo = new ImageView(this); - logo.setImageResource(com.android.internal.R.drawable.platlogo); + logo.setImageResource(mIsAOKP + ? com.android.internal.R.drawable.aokp_platlogo + : com.android.internal.R.drawable.platlogo); + logo.setPadding(p, 0, p, 0); logo.setScaleType(ImageView.ScaleType.CENTER_INSIDE); logo.setVisibility(View.INVISIBLE); @@ -76,21 +88,20 @@ protected void onCreate(Bundle savedInstanceState) { final TextView letter = new TextView(this); letter.setTypeface(bold); - letter.setTextSize(300); + letter.setTextSize(mIsAOKP ? 75 : 300); letter.setTextColor(0xFFFFFFFF); letter.setGravity(Gravity.CENTER); - letter.setText("K"); + letter.setText(mIsAOKP ? "AOKP" : "K"); - final int p = (int)(4 * metrics.density); + p = (int) (4 * metrics.density); final TextView tv = new TextView(this); - if (light != null) tv.setTypeface(light); + tv.setTypeface(light); tv.setTextSize(30); tv.setPadding(p, p, p, p); tv.setTextColor(0xFFFFFFFF); tv.setGravity(Gravity.CENTER); - tv.setTransformationMethod(new AllCapsTransformationMethod(this)); - tv.setText("Android " + Build.VERSION.RELEASE); + tv.setText(mIsAOKP ? "AOKP " + Build.VERSION.RELEASE : "ANDROID " + Build.VERSION.RELEASE); tv.setVisibility(View.INVISIBLE); mContent.addView(bg); @@ -164,6 +175,7 @@ public boolean onLongClick(View v) { .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) + .putExtra("is_aokp", mIsAOKP) .addCategory("com.android.internal.category.PLATLOGO")); } catch (ActivityNotFoundException ex) { android.util.Log.e("PlatLogoActivity", "Couldn't catch a break."); diff --git a/core/java/com/android/internal/app/ProcessStats.java b/core/java/com/android/internal/app/ProcessStats.java index b46a20161a70..1411d5cab861 100644 --- a/core/java/com/android/internal/app/ProcessStats.java +++ b/core/java/com/android/internal/app/ProcessStats.java @@ -2351,6 +2351,9 @@ void addDuration(int state, long dur) { mDurationsTable = mStats.mAddLongTable; mDurationsTableSize = mStats.mAddLongTableSize; } + if (((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK) >= mStats.mLongs.size()) { + return; + } long[] longs = mStats.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK); if (DEBUG) Slog.d(TAG, "Duration of " + mName + " state " + state + " inc by " + dur + " from " + longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK]); diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 25916b22fa6f..31e810f24926 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -516,7 +516,8 @@ private static boolean startSystemServer() OsConstants.CAP_SYS_NICE, OsConstants.CAP_SYS_RESOURCE, OsConstants.CAP_SYS_TIME, - OsConstants.CAP_SYS_TTY_CONFIG + OsConstants.CAP_SYS_TTY_CONFIG, + OsConstants.CAP_BLOCK_SUSPEND ); /* Hardcoded command line to start the system server */ String args[] = { diff --git a/core/java/com/android/internal/util/aokp/AwesomeAction.java b/core/java/com/android/internal/util/aokp/AwesomeAction.java index 10404d3d348c..3a6b519e5ff8 100755 --- a/core/java/com/android/internal/util/aokp/AwesomeAction.java +++ b/core/java/com/android/internal/util/aokp/AwesomeAction.java @@ -19,6 +19,7 @@ import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; +import android.app.ActivityManagerNative; import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.ContentUris; @@ -367,10 +368,20 @@ private static void toggleLastApp(Context mContext) { looper++; } if (lastAppId != 0) { + sendCloseSystemWindows(mContext, null); am.moveTaskToFront(lastAppId, am.MOVE_TASK_NO_USER_ACTION); } } + private static void sendCloseSystemWindows(Context context, String reason) { + if (ActivityManagerNative.isSystemReady()) { + try { + ActivityManagerNative.getDefault().closeSystemDialogs(reason); + } catch (RemoteException e) { + } + } + } + private static Handler mHandler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { diff --git a/core/java/com/android/internal/util/cm/ImageUtils.java b/core/java/com/android/internal/util/cm/ImageUtils.java new file mode 100644 index 000000000000..f1f3a49b3b0c --- /dev/null +++ b/core/java/com/android/internal/util/cm/ImageUtils.java @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2013-2014 The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util.cm; + +import android.app.WallpaperManager; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.ThemeUtils; +import android.content.res.AssetManager; +import android.content.res.Resources; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Point; +import android.net.Uri; +import android.provider.ThemesContract; +import android.provider.ThemesContract.ThemesColumns; +import android.text.TextUtils; +import android.util.Log; +import android.webkit.URLUtil; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; + +import libcore.io.IoUtils; + +public class ImageUtils { + private static final String TAG = ImageUtils.class.getSimpleName(); + + private static final String ASSET_URI_PREFIX = "file:///android_asset/"; + private static final int DEFAULT_IMG_QUALITY = 100; + + /** + * Gets the Width and Height of the image + * + * @param inputStream The input stream of the image + * + * @return A point structure that holds the Width and Height (x and y)/*" + */ + public static Point getImageDimension(InputStream inputStream) { + if (inputStream == null) { + throw new IllegalArgumentException("'inputStream' cannot be null!"); + } + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeStream(inputStream, null, options); + Point point = new Point(options.outWidth,options.outHeight); + return point; + } + + /** + * Crops the input image and returns a new InputStream of the cropped area + * + * @param inputStream The input stream of the image + * @param imageWidth Width of the input image + * @param imageHeight Height of the input image + * @param inputStream Desired Width + * @param inputStream Desired Width + * + * @return a new InputStream of the cropped area/*" + */ + public static InputStream cropImage(InputStream inputStream, int imageWidth, int imageHeight, + int outWidth, int outHeight) throws IllegalArgumentException { + if (inputStream == null){ + throw new IllegalArgumentException("inputStream cannot be null"); + } + + if (imageWidth <= 0 || imageHeight <= 0) { + throw new IllegalArgumentException( + String.format("imageWidth and imageHeight must be > 0: imageWidth=%d" + + " imageHeight=%d", imageWidth, imageHeight)); + } + + if (outWidth <= 0 || outHeight <= 0) { + throw new IllegalArgumentException( + String.format("outWidth and outHeight must be > 0: outWidth=%d" + + " outHeight=%d", imageWidth, outHeight)); + } + + int scaleDownSampleSize = Math.min(imageWidth / outWidth, imageHeight / outHeight); + if (scaleDownSampleSize > 0) { + imageWidth /= scaleDownSampleSize; + imageHeight /= scaleDownSampleSize; + } else { + float ratio = (float) outWidth / outHeight; + if (imageWidth < imageHeight * ratio) { + outWidth = imageWidth; + outHeight = (int) (outWidth / ratio); + } else { + outHeight = imageHeight; + outWidth = (int) (outHeight * ratio); + } + } + int left = (imageWidth - outWidth) / 2; + int top = (imageHeight - outHeight) / 2; + InputStream compressed = null; + try { + BitmapFactory.Options options = new BitmapFactory.Options(); + if (scaleDownSampleSize > 1) { + options.inSampleSize = scaleDownSampleSize; + } + Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, options); + if (bitmap == null) { + return null; + } + Bitmap cropped = Bitmap.createBitmap(bitmap, left, top, outWidth, outHeight); + ByteArrayOutputStream tmpOut = new ByteArrayOutputStream(2048); + if (cropped.compress(Bitmap.CompressFormat.PNG, DEFAULT_IMG_QUALITY, tmpOut)) { + byte[] outByteArray = tmpOut.toByteArray(); + compressed = new ByteArrayInputStream(outByteArray); + } + } catch (Exception e) { + Log.e(TAG, "Exception " + e); + } + return compressed; + } + + /** + * Crops the lock screen image and returns a new InputStream of the cropped area + * + * @param pkgName Name of the theme package + * @param context The context + * + * @return a new InputStream of the cropped image/*" + */ + public static InputStream getCroppedKeyguardStream(String pkgName, Context context) + throws IllegalArgumentException { + if (TextUtils.isEmpty(pkgName)) { + throw new IllegalArgumentException("'pkgName' cannot be null or empty!"); + } + if (context == null) { + throw new IllegalArgumentException("'context' cannot be null!"); + } + + InputStream cropped = null; + InputStream stream = null; + try { + stream = getOriginalKeyguardStream(pkgName, context); + if (stream == null) { + return null; + } + Point point = getImageDimension(stream); + IoUtils.closeQuietly(stream); + if (point == null || point.x == 0 || point.y == 0) { + return null; + } + WallpaperManager wm = WallpaperManager.getInstance(context); + int outWidth = wm.getDesiredMinimumWidth(); + int outHeight = wm.getDesiredMinimumHeight(); + stream = getOriginalKeyguardStream(pkgName, context); + if (stream == null) { + return null; + } + cropped = cropImage(stream, point.x, point.y, outWidth, outHeight); + } catch (Exception e) { + Log.e(TAG, "Exception " + e); + } finally { + IoUtils.closeQuietly(stream); + } + return cropped; + } + + /** + * Crops the wallpaper image and returns a new InputStream of the cropped area + * + * @param pkgName Name of the theme package + * @param context The context + * + * @return a new InputStream of the cropped image/*" + */ + public static InputStream getCroppedWallpaperStream(String pkgName, Context context) { + if (TextUtils.isEmpty(pkgName)) { + throw new IllegalArgumentException("'pkgName' cannot be null or empty!"); + } + if (context == null) { + throw new IllegalArgumentException("'context' cannot be null!"); + } + + InputStream cropped = null; + InputStream stream = null; + try { + stream = getOriginalWallpaperStream(pkgName, context); + if (stream == null) { + return null; + } + Point point = getImageDimension(stream); + IoUtils.closeQuietly(stream); + if (point == null || point.x == 0 || point.y == 0) { + return null; + } + WallpaperManager wm = WallpaperManager.getInstance(context); + int outWidth = wm.getDesiredMinimumWidth(); + int outHeight = wm.getDesiredMinimumHeight(); + stream = getOriginalWallpaperStream(pkgName, context); + if (stream == null) { + return null; + } + cropped = cropImage(stream, point.x, point.y, outWidth, outHeight); + } catch (Exception e) { + Log.e(TAG, "Exception " + e); + } finally { + IoUtils.closeQuietly(stream); + } + return cropped; + } + + private static InputStream getOriginalKeyguardStream(String pkgName, Context context) { + if (TextUtils.isEmpty(pkgName) || context == null) { + return null; + } + + InputStream inputStream = null; + try { + //Get input WP stream from the theme + Context themeCtx = context.createPackageContext(pkgName, + Context.CONTEXT_IGNORE_SECURITY); + AssetManager assetManager = themeCtx.getAssets(); + String wpPath = ThemeUtils.getLockscreenWallpaperPath(assetManager); + if (wpPath == null) { + Log.w(TAG, "Not setting lockscreen wp because wallpaper file was not found."); + } else { + inputStream = ThemeUtils.getInputStreamFromAsset(themeCtx, + ASSET_URI_PREFIX + wpPath); + } + } catch (Exception e) { + Log.e(TAG, "There was an error setting lockscreen wp for pkg " + pkgName, e); + } + return inputStream; + } + + private static InputStream getOriginalWallpaperStream(String pkgName, Context context) { + if (TextUtils.isEmpty(pkgName) || context == null) { + return null; + } + + InputStream inputStream = null; + String selection = ThemesContract.ThemesColumns.PKG_NAME + "= ?"; + String[] selectionArgs = {pkgName}; + Cursor c = context.getContentResolver().query(ThemesColumns.CONTENT_URI, + null, selection, + selectionArgs, null); + if (c == null || c.getCount() < 1) { + if (c != null) c.close(); + return null; + } else { + c.moveToFirst(); + } + + try { + Context themeContext = context.createPackageContext(pkgName, + Context.CONTEXT_IGNORE_SECURITY); + boolean isLegacyTheme = c.getInt( + c.getColumnIndex(ThemesColumns.IS_LEGACY_THEME)) == 1; + if (!isLegacyTheme) { + String wallpaper = c.getString( + c.getColumnIndex(ThemesColumns.WALLPAPER_URI)); + if (wallpaper != null) { + if (URLUtil.isAssetUrl(wallpaper)) { + inputStream = ThemeUtils.getInputStreamFromAsset(themeContext, wallpaper); + } else { + inputStream = context.getContentResolver().openInputStream( + Uri.parse(wallpaper)); + } + } else { + // try and get the wallpaper directly from the apk if the URI was null + Context themeCtx = context.createPackageContext(pkgName, + Context.CONTEXT_IGNORE_SECURITY); + AssetManager assetManager = themeCtx.getAssets(); + String wpPath = ThemeUtils.getWallpaperPath(assetManager); + if (wpPath == null) { + Log.e(TAG, "Not setting wp because wallpaper file was not found."); + } else { + inputStream = ThemeUtils.getInputStreamFromAsset(themeCtx, + ASSET_URI_PREFIX + wpPath); + } + } + } else { + Resources resources = context.getResources(); + PackageInfo pi = context.getPackageManager().getPackageInfo(pkgName, 0); + + if (pi.legacyThemeInfos != null && pi.legacyThemeInfos.length > 0) { + inputStream = + resources.openRawResource(pi.legacyThemeInfos[0].wallpaperResourceId); + } + } + } catch (Exception e) { + Log.e(TAG, "getWallpaperStream: " + e); + } finally { + c.close(); + } + + return inputStream; + } +} + diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java index 9ddf75204321..8569c76510f9 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java +++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java @@ -351,7 +351,10 @@ public boolean hideOverflowMenu() { */ public boolean dismissPopupMenus() { boolean result = hideOverflowMenu(); - result |= hideSubMenus(); + + if (result) { + result |= hideSubMenus(); + } return result; } diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl index 91056f160910..27af8a2b0fdb 100644 --- a/core/java/com/android/internal/widget/ILockSettings.aidl +++ b/core/java/com/android/internal/widget/ILockSettings.aidl @@ -16,6 +16,8 @@ package com.android.internal.widget; +import android.gesture.Gesture; + /** {@hide} */ interface ILockSettings { void setBoolean(in String key, in boolean value, in int userId); @@ -24,11 +26,15 @@ interface ILockSettings { boolean getBoolean(in String key, in boolean defaultValue, in int userId); long getLong(in String key, in long defaultValue, in int userId); String getString(in String key, in String defaultValue, in int userId); + byte getLockPatternSize(int userId); void setLockPattern(in String pattern, int userId); boolean checkPattern(in String pattern, int userId); + void setLockGesture(in Gesture gesture, int userId); + boolean checkGesture(in Gesture gesture, int userId); void setLockPassword(in String password, int userId); boolean checkPassword(in String password, int userId); boolean havePattern(int userId); + boolean haveGesture(int userId); boolean havePassword(int userId); void removeUser(int userId); } diff --git a/core/java/com/android/internal/widget/LockGestureView.java b/core/java/com/android/internal/widget/LockGestureView.java new file mode 100644 index 000000000000..8e0895e3c589 --- /dev/null +++ b/core/java/com/android/internal/widget/LockGestureView.java @@ -0,0 +1,176 @@ +package com.android.internal.widget; + +import android.content.Context; +import android.gesture.Gesture; +import android.gesture.GestureOverlayView; +import android.graphics.Color; +import android.util.AttributeSet; + +public class LockGestureView extends GestureOverlayView implements GestureOverlayView.OnGesturingListener, + GestureOverlayView.OnGesturePerformedListener { + private static final int CORRECT_COLOR = Color.GREEN; + private static final int WRONG_COLOR = Color.RED; + + private DisplayMode mGestureDisplayMode = DisplayMode.Correct; + private boolean mInStealthMode = false; + + private OnLockGestureListener mOnGestureListener; + + @Override + public void onGesturePerformed(GestureOverlayView gestureOverlayView, Gesture gesture) { + notifyGestureDetected(gesture); + } + + @Override + public void onGesturingStarted(GestureOverlayView gestureOverlayView) { + notifyGestureStart(); + } + + @Override + public void onGesturingEnded(GestureOverlayView gestureOverlayView) { + } + + /** + * The call back interface for detecting gestures entered by the user. + */ + public static interface OnLockGestureListener { + + /** + * A new gesture has begun. + */ + void onGestureStart(); + + /** + * The gesture was cleared. + */ + void onGestureCleared(); + + /** + * A gesture was detected from the user. + * @param gesture The gesture. + */ + void onGestureDetected(Gesture gesture); + } + + /** + * How to display the current pattern. + */ + public enum DisplayMode { + + /** + * The pattern drawn is correct (i.e draw it in a friendly color) + */ + Correct, + + /** + * The pattern is wrong (i.e draw a foreboding color) + */ + Wrong + } + + public LockGestureView(Context context) { + this(context, null); + } + + public LockGestureView(Context context, AttributeSet attrs) { + super(context, attrs); + setGestureVisible(true); + addOnGesturingListener(this); + addOnGesturePerformedListener(this); + setGestureColor(CORRECT_COLOR); + mClearPerformedGesture = false; + } + + /** + * @return Whether the view is in stealth mode. + */ + public boolean isInStealthMode() { + return mInStealthMode; + } + + /** + * Set whether the view is in stealth mode. If true, there will be no + * visible feedback as the user enters the gesture. + * + * @param inStealthMode Whether in stealth mode. + */ + public void setInStealthMode(boolean inStealthMode) { + mInStealthMode = inStealthMode; + setGestureVisible(!inStealthMode); + } + + /** + * Set the display mode of the current pattern. This can be useful, for + * instance, after detecting a pattern to tell this view whether change the + * in progress result to correct or wrong. + * @param displayMode The display mode. + */ + public void setDisplayMode(DisplayMode displayMode) { + mGestureDisplayMode = displayMode; + switch (displayMode) { + case Correct: + setGestureColor(CORRECT_COLOR); + break; + case Wrong: + setGestureColor(WRONG_COLOR); + break; + } + + invalidate(); + } + + /** + * Disable input (for instance when displaying a message that will + * timeout so user doesn't get view into messy state). + */ + public void disableInput() { + mInputEnabled = false; + } + + /** + * Enable input. + */ + public void enableInput() { + mInputEnabled = true; + } + + /** + * Clear the gesture. + */ + public void clearGesture() { + resetGesture(); + } + + /** + * Reset all pattern state. + */ + private void resetGesture() { + mGestureDisplayMode = DisplayMode.Correct; + clear(false); + invalidate(); + } + + /** + * Set the call back for gesture detection. + * @param onGestureListener The call back. + */ + public void setOnGestureListener( + OnLockGestureListener onGestureListener) { + mOnGestureListener = onGestureListener; + } + + private void notifyGestureStart() { + if (mOnGestureListener != null) + mOnGestureListener.onGestureStart(); + } + + private void notifyGestureCleared() { + if (mOnGestureListener != null) + mOnGestureListener.onGestureCleared(); + } + + private void notifyGestureDetected(Gesture gesture) { + if (mOnGestureListener != null) + mOnGestureListener.onGestureDetected(gesture); + } +} diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 90b974d34b55..5e4ce700dd27 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -25,6 +25,7 @@ import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.gesture.Gesture; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; @@ -91,6 +92,11 @@ public class LockPatternUtils { */ public static final int MIN_LOCK_PATTERN_SIZE = 4; + /** + * The default size of the pattern lockscreen. Ex: 3x3 + */ + public static final byte PATTERN_SIZE_DEFAULT = 3; + /** * The minimum number of dots the user must include in a wrong pattern * attempt for it to be counted against the counts that affect @@ -127,6 +133,7 @@ public class LockPatternUtils { public final static String LOCKOUT_PERMANENT_KEY = "lockscreen.lockedoutpermanently"; public final static String LOCKOUT_ATTEMPT_DEADLINE = "lockscreen.lockoutattemptdeadline"; public final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen"; + public final static String GESTURE_EVER_CHOSEN_KEY = "lockscreen.gestureeverchosen"; public final static String PASSWORD_TYPE_KEY = "lockscreen.password_type"; public static final String PASSWORD_TYPE_ALTERNATE_KEY = "lockscreen.password_type_alternate"; public final static String LOCK_PASSWORD_SALT_KEY = "lockscreen.password_salt"; @@ -298,6 +305,24 @@ public boolean checkPattern(List pattern) { } } + /** + * Check to see if a gesture matches the saved gesture. If no gesture exists, + * always returns true. + * @param gesture The gesture to check. + * @return Whether the gesture matches the stored one. + */ + public boolean checkGesture(Gesture gesture) { + final int userId = getCurrentOrCallingUserId(); + try { + final boolean matched = getLockSettings().checkGesture(gesture, userId); + if (matched && (userId == UserHandle.USER_OWNER)) { + } + return matched; + } catch (RemoteException re) { + return true; + } + } + /** * Check to see if a password matches the saved password. If no password exists, * always returns true. @@ -340,6 +365,18 @@ public boolean checkPasswordHistory(String password) { return passwordHistory.contains(passwordHashString); } + /** + * Check to see if the user has stored a lock gesture. + * @return Whether a saved gesture exists. + */ + public boolean savedGestureExists() { + try { + return getLockSettings().haveGesture(getCurrentOrCallingUserId()); + } catch (RemoteException re) { + return false; + } + } + /** * Check to see if the user has stored a lock pattern. * @return Whether a saved pattern exists. @@ -374,6 +411,16 @@ public boolean isPatternEverChosen() { return getBoolean(PATTERN_EVER_CHOSEN_KEY, false); } + /** + * Return true if the user has ever chosen a gesture. This is true even if the gesture is + * currently cleared. + * + * @return True if the user has ever chosen a pattern. + */ + public boolean isGestureEverChosen() { + return getBoolean(GESTURE_EVER_CHOSEN_KEY, false); + } + /** * Return true if the user has ever chosen biometric weak. This is true even if biometric * weak is not current set. @@ -425,6 +472,11 @@ public int getActivePasswordQuality() { activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; } break; + case DevicePolicyManager.PASSWORD_QUALITY_GESTURE_WEAK: + if (isLockGestureEnabled()) { + activePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_GESTURE_WEAK; + } + break; } return activePasswordQuality; @@ -438,6 +490,8 @@ public void clearLock(boolean isFallback) { saveLockPassword(null, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); setLockPatternEnabled(false); saveLockPattern(null); + saveLockGesture(null); + setLockGestureEnabled(false); setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED); setLong(PASSWORD_TYPE_ALTERNATE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED); } @@ -539,6 +593,47 @@ public boolean isOwnerInfoEnabled() { return getBoolean(LOCK_SCREEN_OWNER_INFO_ENABLED, false); } + /** + * Save a lock pattern. + * @param pattern The new pattern to save. + */ + public void saveLockGesture(Gesture gesture) { + this.saveLockGesture(gesture, false); + } + + /** + * Save a lock pattern. + * @param pattern The new pattern to save. + * @param isFallback Specifies if this is a fallback to biometric weak + */ + public void saveLockGesture(Gesture gesture, boolean isFallback) { + try { + getLockSettings().setLockGesture(gesture, getCurrentOrCallingUserId()); + DevicePolicyManager dpm = getDevicePolicyManager(); + if (gesture != null) { + setBoolean(GESTURE_EVER_CHOSEN_KEY, true); + if (!isFallback) { + deleteGallery(); + setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_GESTURE_WEAK); + dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_GESTURE_WEAK, + 0, 0, 0, 0, 0, 0, 0, getCurrentOrCallingUserId()); + } else { + setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK); + setLong(PASSWORD_TYPE_ALTERNATE_KEY, + DevicePolicyManager.PASSWORD_QUALITY_GESTURE_WEAK); + finishBiometricWeak(); + dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK, + 0, 0, 0, 0, 0, 0, 0, getCurrentOrCallingUserId()); + } + } else { + dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, + 0, 0, 0, 0, 0, getCurrentOrCallingUserId()); + } + } catch (RemoteException re) { + Log.e(TAG, "Couldn't save lock gesture " + re); + } + } + /** * Compute the password quality from the given password string. */ @@ -740,13 +835,16 @@ public boolean usingBiometricWeak() { * @param string The pattern serialized with {@link #patternToString} * @return The pattern. */ - public static List stringToPattern(String string) { + public List stringToPattern(String string) { List result = Lists.newArrayList(); + final byte size = getLockPatternSize(); + LockPatternView.Cell.updateSize(size); + final byte[] bytes = string.getBytes(); for (int i = 0; i < bytes.length; i++) { byte b = bytes[i]; - result.add(LockPatternView.Cell.of(b / 3, b % 3)); + result.add(LockPatternView.Cell.of(b / size, b % size, size)); } return result; } @@ -756,7 +854,7 @@ public static List stringToPattern(String string) { * @param pattern The pattern. * @return The pattern in string form. */ - public static String patternToString(List pattern) { + public String patternToString(List pattern) { if (pattern == null) { return ""; } @@ -765,7 +863,7 @@ public static String patternToString(List pattern) { byte[] res = new byte[patternSize]; for (int i = 0; i < patternSize; i++) { LockPatternView.Cell cell = pattern.get(i); - res[i] = (byte) (cell.getRow() * 3 + cell.getColumn()); + res[i] = (byte) (cell.getRow() * getLockPatternSize() + cell.getColumn()); } return new String(res); } @@ -777,7 +875,7 @@ public static String patternToString(List pattern) { * @param pattern the gesture pattern. * @return the hash of the pattern in a byte array. */ - public static byte[] patternToHash(List pattern) { + public byte[] patternToHash(List pattern) { if (pattern == null) { return null; } @@ -786,7 +884,7 @@ public static byte[] patternToHash(List pattern) { byte[] res = new byte[patternSize]; for (int i = 0; i < patternSize; i++) { LockPatternView.Cell cell = pattern.get(i); - res[i] = (byte) (cell.getRow() * 3 + cell.getColumn()); + res[i] = (byte) (cell.getRow() * getLockPatternSize() + cell.getColumn()); } try { MessageDigest md = MessageDigest.getInstance("SHA-1"); @@ -879,6 +977,20 @@ public boolean isLockPatternEnabled() { (usingBiometricWeak() && backupEnabled)); } + /** + * @return Whether the lock gesture is enabled, or if it is set as a backup for biometric weak + */ + public boolean isLockGestureEnabled() { + final boolean backupEnabled = + getLong(PASSWORD_TYPE_ALTERNATE_KEY, DevicePolicyManager.PASSWORD_QUALITY_GESTURE_WEAK) + == DevicePolicyManager.PASSWORD_QUALITY_GESTURE_WEAK; + + return getBoolean(Settings.Secure.LOCK_GESTURE_ENABLED, false) + && (getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_GESTURE_WEAK) + == DevicePolicyManager.PASSWORD_QUALITY_GESTURE_WEAK || + (usingBiometricWeak() && backupEnabled)); + } + /** * @return Whether biometric weak lock is installed and that the front facing camera exists */ @@ -958,6 +1070,45 @@ public boolean isTactileFeedbackEnabled() { Settings.System.HAPTIC_FEEDBACK_ENABLED, 1, UserHandle.USER_CURRENT) != 0; } + /** + * Set whether the lock gesture is enabled. + */ + public void setLockGestureEnabled(boolean enabled) { + setBoolean(Settings.Secure.LOCK_GESTURE_ENABLED, enabled); + } + + /** + * @return Whether the visible gesture is enabled. + */ + public boolean isVisibleGestureEnabled() { + return getBoolean(Settings.Secure.LOCK_GESTURE_VISIBLE, true); + } + + /** + * Set whether the visible gesture is enabled. + */ + public void setVisibleGestureEnabled(boolean enabled) { + setBoolean(Settings.Secure.LOCK_GESTURE_VISIBLE, enabled); + } + + /** + * @return the pattern lockscreen size + */ + public byte getLockPatternSize() { + try { + return getLockSettings().getLockPatternSize(getCurrentOrCallingUserId()); + } catch (RemoteException re) { + return PATTERN_SIZE_DEFAULT; + } + } + + /** + * Set the pattern lockscreen size + */ + public void setLockPatternSize(long size) { + setLong(Settings.Secure.LOCK_PATTERN_SIZE, size); + } + /** * Set and store the lockout deadline, meaning the user can't attempt his/her unlock * pattern until the deadline has passed. @@ -1237,12 +1388,14 @@ private void setString(String secureSettingKey, String value, int userHandle) { public boolean isSecure() { long mode = getKeyguardStoredPasswordQuality(); final boolean isPattern = mode == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; + final boolean isGesture = mode == DevicePolicyManager.PASSWORD_QUALITY_GESTURE_WEAK; final boolean isPassword = mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; final boolean secure = isPattern && isLockPatternEnabled() && savedPatternExists() - || isPassword && savedPasswordExists(); + || isPassword && savedPasswordExists() + || isGesture && isLockGestureEnabled() && savedGestureExists(); return secure; } diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java index b066d703360a..424df6a5eaef 100644 --- a/core/java/com/android/internal/widget/LockPatternView.java +++ b/core/java/com/android/internal/widget/LockPatternView.java @@ -38,13 +38,14 @@ import android.view.accessibility.AccessibilityManager; import com.android.internal.R; +import com.android.internal.widget.LockPatternUtils; import java.util.ArrayList; import java.util.List; /** * Displays and detects the user's unlock attempt, which is a drag of a finger - * across 9 regions of the screen. + * across regions of the screen. * * Is also capable of displaying a static pattern in "in progress", "wrong" or * "correct" states. @@ -68,6 +69,8 @@ public class LockPatternView extends View { */ private static final int MILLIS_PER_CIRCLE_ANIMATING = 700; + private byte mPatternSize = LockPatternUtils.PATTERN_SIZE_DEFAULT; + /** * This can be used to avoid updating the display for very small motions or noisy panels. * It didn't seem to have much impact on the devices tested, so currently set to 0. @@ -75,7 +78,7 @@ public class LockPatternView extends View { private static final float DRAG_THRESHHOLD = 0.0f; private OnPatternListener mOnPatternListener; - private ArrayList mPattern = new ArrayList(9); + private ArrayList mPattern = new ArrayList(mPatternSize * mPatternSize); /** * Lookup table for the circles of the pattern we are currently drawing. @@ -83,7 +86,7 @@ public class LockPatternView extends View { * in which case we use this to hold the cells we are drawing for the in * progress animation. */ - private boolean[][] mPatternDrawLookup = new boolean[3][3]; + private boolean[][] mPatternDrawLookup = new boolean[mPatternSize][mPatternSize]; /** * the in progress point: @@ -128,29 +131,27 @@ public class LockPatternView extends View { private final Matrix mArrowMatrix = new Matrix(); private final Matrix mCircleMatrix = new Matrix(); + private LockPatternUtils mLockPatternUtils; + /** - * Represents a cell in the 3 X 3 matrix of the unlock pattern view. + * Represents a cell in the matrix of the unlock pattern view. */ public static class Cell { int row; int column; - // keep # objects limited to 9 - static Cell[][] sCells = new Cell[3][3]; + // keep # objects limited + static Cell[][] sCells; static { - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { - sCells[i][j] = new Cell(i, j); - } - } + updateSize(LockPatternUtils.PATTERN_SIZE_DEFAULT); } /** * @param row The row of the cell. * @param column The column of the cell. */ - private Cell(int row, int column) { - checkRange(row, column); + private Cell(int row, int column, byte size) { + checkRange(row, column, size); this.row = row; this.column = column; } @@ -167,17 +168,27 @@ public int getColumn() { * @param row The row of the cell. * @param column The column of the cell. */ - public static synchronized Cell of(int row, int column) { - checkRange(row, column); + public static synchronized Cell of(int row, int column, byte size) { + checkRange(row, column, size); return sCells[row][column]; } - private static void checkRange(int row, int column) { - if (row < 0 || row > 2) { - throw new IllegalArgumentException("row must be in range 0-2"); + + public static void updateSize(byte size) { + sCells = new Cell[size][size]; + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + sCells[i][j] = new Cell(i, j, size); + } } - if (column < 0 || column > 2) { - throw new IllegalArgumentException("column must be in range 0-2"); + } + + private static void checkRange(int row, int column, byte size) { + if (row < 0 || row > size - 1) { + throw new IllegalArgumentException("row must be in range 0-" + (size - 1)); + } + if (column < 0 || column > size - 1) { + throw new IllegalArgumentException("column must be in range 0-" + (size - 1)); } } @@ -305,6 +316,13 @@ public boolean isTactileFeedbackEnabled() { return mEnableHapticFeedback; } + /** + * @return the current pattern lockscreen size. + */ + public int getLockPatternSize() { + return mPatternSize; + } + /** * Set whether the view is in stealth mode. If true, there will be no * visible feedback as the user enters the pattern. @@ -325,6 +343,26 @@ public void setTactileFeedbackEnabled(boolean tactileFeedbackEnabled) { mEnableHapticFeedback = tactileFeedbackEnabled; } + /** + * Set the pattern size of the lockscreen + * + * @param size The pattern size. + */ + public void setLockPatternSize(byte size) { + mPatternSize = size; + Cell.updateSize(size); + mPattern = new ArrayList(size * size); + mPatternDrawLookup = new boolean[size][size]; + } + + /** + * Set the LockPatternUtil instance used to encode a pattern to a string + * @param utils The instance. + */ + public void setLockPatternUtils(LockPatternUtils utils) { + mLockPatternUtils = utils; + } + /** * Set the call back for pattern detection. * @param onPatternListener The call back. @@ -422,8 +460,8 @@ private void resetPattern() { * Clear the pattern lookup table. */ private void clearPatternDrawLookup() { - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { + for (int i = 0; i < mPatternSize; i++) { + for (int j = 0; j < mPatternSize; j++) { mPatternDrawLookup[i][j] = false; } } @@ -447,10 +485,10 @@ public void enableInput() { @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { final int width = w - mPaddingLeft - mPaddingRight; - mSquareWidth = width / 3.0f; + mSquareWidth = width / (float) mPatternSize; final int height = h - mPaddingTop - mPaddingBottom; - mSquareHeight = height / 3.0f; + mSquareHeight = height / (float) mPatternSize; } private int resolveMeasured(int measureSpec, int desired) @@ -473,14 +511,14 @@ private int resolveMeasured(int measureSpec, int desired) @Override protected int getSuggestedMinimumWidth() { - // View should be large enough to contain 3 side-by-side target bitmaps - return 3 * mBitmapWidth; + // View should be large enough to contain side-by-side target bitmaps + return mPatternSize * mBitmapWidth; } @Override protected int getSuggestedMinimumHeight() { - // View should be large enough to contain 3 side-by-side target bitmaps - return 3 * mBitmapWidth; + // View should be large enough to contain side-by-side target bitmaps + return mPatternSize * mBitmapWidth; } @Override @@ -517,7 +555,6 @@ private Cell detectAndAddHit(float x, float y) { if (cell != null) { // check for gaps in existing pattern - Cell fillInGapCell = null; final ArrayList pattern = mPattern; if (!pattern.isEmpty()) { final Cell lastCell = pattern.get(pattern.size() - 1); @@ -527,21 +564,19 @@ private Cell detectAndAddHit(float x, float y) { int fillInRow = lastCell.row; int fillInColumn = lastCell.column; - if (Math.abs(dRow) == 2 && Math.abs(dColumn) != 1) { - fillInRow = lastCell.row + ((dRow > 0) ? 1 : -1); - } - - if (Math.abs(dColumn) == 2 && Math.abs(dRow) != 1) { - fillInColumn = lastCell.column + ((dColumn > 0) ? 1 : -1); + if (dRow == 0 || dColumn == 0 || Math.abs(dRow) == Math.abs(dColumn)) { + while (true) { + fillInRow += Integer.signum(dRow); + fillInColumn += Integer.signum(dColumn); + if (fillInRow == cell.row && fillInColumn == cell.column) break; + Cell fillInGapCell = Cell.of(fillInRow, fillInColumn, mPatternSize); + if (!mPatternDrawLookup[fillInGapCell.row][fillInGapCell.column]) { + addCellToPattern(fillInGapCell); + } + } } - - fillInGapCell = Cell.of(fillInRow, fillInColumn); } - if (fillInGapCell != null && - !mPatternDrawLookup[fillInGapCell.row][fillInGapCell.column]) { - addCellToPattern(fillInGapCell); - } addCellToPattern(cell); if (mEnableHapticFeedback) { performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, @@ -574,7 +609,7 @@ private Cell checkForNewHit(float x, float y) { if (mPatternDrawLookup[rowHit][columnHit]) { return null; } - return Cell.of(rowHit, columnHit); + return Cell.of(rowHit, columnHit, mPatternSize); } /** @@ -588,7 +623,7 @@ private int getRowHit(float y) { float hitSize = squareHeight * mHitFactor; float offset = mPaddingTop + (squareHeight - hitSize) / 2f; - for (int i = 0; i < 3; i++) { + for (int i = 0; i < mPatternSize; i++) { final float hitTop = offset + squareHeight * i; if (y >= hitTop && y <= hitTop + hitSize) { @@ -608,7 +643,7 @@ private int getColumnHit(float x) { float hitSize = squareWidth * mHitFactor; float offset = mPaddingLeft + (squareWidth - hitSize) / 2f; - for (int i = 0; i < 3; i++) { + for (int i = 0; i < mPatternSize; i++) { final float hitLeft = offset + squareWidth * i; if (x >= hitLeft && x <= hitLeft + hitSize) { @@ -857,10 +892,10 @@ protected void onDraw(Canvas canvas) { final int paddingTop = mPaddingTop; final int paddingLeft = mPaddingLeft; - for (int i = 0; i < 3; i++) { + for (int i = 0; i < mPatternSize; i++) { float topY = paddingTop + i * squareHeight; //float centerY = mPaddingTop + i * mSquareHeight + (mSquareHeight / 2); - for (int j = 0; j < 3; j++) { + for (int j = 0; j < mPatternSize; j++) { float leftX = paddingLeft + j * squareWidth; drawCircle(canvas, (int) leftX, (int) topY, drawLookup[i][j]); } @@ -1021,8 +1056,8 @@ private void drawCircle(Canvas canvas, int leftX, int topY, boolean partOfPatter protected Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); return new SavedState(superState, - LockPatternUtils.patternToString(mPattern), - mPatternDisplayMode.ordinal(), + mLockPatternUtils.patternToString(mPattern), + mPatternDisplayMode.ordinal(), mPatternSize, mInputEnabled, mInStealthMode, mEnableHapticFeedback); } @@ -1032,8 +1067,9 @@ protected void onRestoreInstanceState(Parcelable state) { super.onRestoreInstanceState(ss.getSuperState()); setPattern( DisplayMode.Correct, - LockPatternUtils.stringToPattern(ss.getSerializedPattern())); + mLockPatternUtils.stringToPattern(ss.getSerializedPattern())); mPatternDisplayMode = DisplayMode.values()[ss.getDisplayMode()]; + mPatternSize = ss.getPatternSize(); mInputEnabled = ss.isInputEnabled(); mInStealthMode = ss.isInStealthMode(); mEnableHapticFeedback = ss.isTactileFeedbackEnabled(); @@ -1046,6 +1082,7 @@ private static class SavedState extends BaseSavedState { private final String mSerializedPattern; private final int mDisplayMode; + private final byte mPatternSize; private final boolean mInputEnabled; private final boolean mInStealthMode; private final boolean mTactileFeedbackEnabled; @@ -1054,10 +1091,12 @@ private static class SavedState extends BaseSavedState { * Constructor called from {@link LockPatternView#onSaveInstanceState()} */ private SavedState(Parcelable superState, String serializedPattern, int displayMode, - boolean inputEnabled, boolean inStealthMode, boolean tactileFeedbackEnabled) { + byte patternSize, boolean inputEnabled, boolean inStealthMode, + boolean tactileFeedbackEnabled) { super(superState); mSerializedPattern = serializedPattern; mDisplayMode = displayMode; + mPatternSize = patternSize; mInputEnabled = inputEnabled; mInStealthMode = inStealthMode; mTactileFeedbackEnabled = tactileFeedbackEnabled; @@ -1070,6 +1109,7 @@ private SavedState(Parcel in) { super(in); mSerializedPattern = in.readString(); mDisplayMode = in.readInt(); + mPatternSize = (byte) in.readByte(); mInputEnabled = (Boolean) in.readValue(null); mInStealthMode = (Boolean) in.readValue(null); mTactileFeedbackEnabled = (Boolean) in.readValue(null); @@ -1083,6 +1123,10 @@ public int getDisplayMode() { return mDisplayMode; } + public byte getPatternSize() { + return mPatternSize; + } + public boolean isInputEnabled() { return mInputEnabled; } @@ -1100,6 +1144,7 @@ public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeString(mSerializedPattern); dest.writeInt(mDisplayMode); + dest.writeByte(mPatternSize); dest.writeValue(mInputEnabled); dest.writeValue(mInStealthMode); dest.writeValue(mTactileFeedbackEnabled); diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 6fcd22f9f8c9..e8bc5b425ee0 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -73,6 +73,7 @@ LOCAL_SRC_FILES:= \ android_net_NetUtils.cpp \ android_net_TrafficStats.cpp \ android_net_wifi_WifiNative.cpp \ + android_net_wifi_Gbk2Utf.cpp \ android_nio_utils.cpp \ android_text_format_Time.cpp \ android_util_AssetManager.cpp \ @@ -150,10 +151,6 @@ LOCAL_SRC_FILES:= \ android_animation_PropertyValuesHolder.cpp \ com_android_internal_net_NetworkStatsFactory.cpp -ifeq ($(call is-vendor-board-platform,QCOM),true) -LOCAL_SRC_FILES += com_android_internal_app_ActivityTrigger.cpp -endif - LOCAL_C_INCLUDES += \ $(JNI_H_INCLUDE) \ $(LOCAL_PATH)/android/graphics \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index eebd2f415677..49d873c75481 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -179,7 +179,6 @@ extern int register_android_content_res_Configuration(JNIEnv* env); extern int register_android_animation_PropertyValuesHolder(JNIEnv *env); extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env); extern int register_com_android_internal_net_NetworkStatsFactory(JNIEnv *env); -extern int register_com_android_internal_app_ActivityTrigger(JNIEnv *env); extern int register_android_content_res_PackageRedirectionMap(JNIEnv* env); static AndroidRuntime* gCurRuntime = NULL; @@ -1222,9 +1221,6 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_animation_PropertyValuesHolder), REG_JNI(register_com_android_internal_content_NativeLibraryHelper), REG_JNI(register_com_android_internal_net_NetworkStatsFactory), -#ifdef QCOM_HARDWARE - REG_JNI(register_com_android_internal_app_ActivityTrigger), -#endif }; /* diff --git a/core/jni/NOTICE b/core/jni/NOTICE index c5b1efa7aac7..33a334b7b5b2 100644 --- a/core/jni/NOTICE +++ b/core/jni/NOTICE @@ -188,3 +188,65 @@ END OF TERMS AND CONDITIONS + + Copyright (c) 2013, The Linux Foundation. All rights reserved. + + wpa_supplicant/hostapd / common helper functions, etc. + Copyright (c) 2002-2007, Jouni Malinen + + This software may be distributed under the terms of the BSD license. + See README for more details. + +wpa_supplicant and hostapd +-------------------------- + +Copyright (c) 2002-2012, Jouni Malinen and contributors +All Rights Reserved. + +These programs are licensed under the BSD license (the one with +advertisement clause removed). + + +This package may include either wpa_supplicant, hostapd, or both. See +README file respective subdirectories (wpa_supplicant/README or +hostapd/README) for more details. + +Source code files were moved around in v0.6.x releases and compared to +earlier releases, the programs are now built by first going to a +subdirectory (wpa_supplicant or hostapd) and creating build +configuration (.config) and running 'make' there (for Linux/BSD/cygwin +builds). + + +License +------- + +This software may be distributed, used, and modified under the terms of +BSD license: + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name(s) of the above-listed copyright holder(s) nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/core/jni/README b/core/jni/README new file mode 100644 index 000000000000..805c6cfe15d4 --- /dev/null +++ b/core/jni/README @@ -0,0 +1,53 @@ +wpa_supplicant and hostapd +-------------------------- + +Copyright (c) 2002-2012, Jouni Malinen and contributors +All Rights Reserved. + +These programs are licensed under the BSD license (the one with +advertisement clause removed). + + +This package may include either wpa_supplicant, hostapd, or both. See +README file respective subdirectories (wpa_supplicant/README or +hostapd/README) for more details. + +Source code files were moved around in v0.6.x releases and compared to +earlier releases, the programs are now built by first going to a +subdirectory (wpa_supplicant or hostapd) and creating build +configuration (.config) and running 'make' there (for Linux/BSD/cygwin +builds). + + +License +------- + +This software may be distributed, used, and modified under the terms of +BSD license: + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name(s) of the above-listed copyright holder(s) nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 709de5c7c070..062873b7906a 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -525,13 +525,14 @@ static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fi // Restore the descriptor's offset on exiting this function. AutoFDSeek autoRestore(descriptor); + descriptor = dup(descriptor); FILE* file = fdopen(descriptor, "r"); if (file == NULL) { return nullObjectReturn("Could not open file"); } SkAutoTUnref fileStream(new SkFILEStream(file, - SkFILEStream::kCallerRetains_Ownership)); + SkFILEStream::kCallerPasses_Ownership)); SkAutoTUnref stream; diff --git a/core/jni/android_net_wifi_Gbk2Utf.cpp b/core/jni/android_net_wifi_Gbk2Utf.cpp new file mode 100644 index 000000000000..31b53f124896 --- /dev/null +++ b/core/jni/android_net_wifi_Gbk2Utf.cpp @@ -0,0 +1,568 @@ +/* + * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * + * wpa_supplicant/hostapd / common helper functions, etc. + * Copyright (c) 2002-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#define LOG_TAG "wifi_gbk2utf" + +#include "jni.h" +#include "android_net_wifi_Gbk2Utf.h" + +#define BUF_SIZE 256 +#define CONVERT_LINE_LEN 2048 +#define CHARSET_CN ("gbk") + +namespace android { + +static jint DBG = false; + +struct accessPointObjectItem *g_pItemList = NULL; +struct accessPointObjectItem *g_pLastNode = NULL; +pthread_mutex_t *g_pItemListMutex = NULL; + +static void addAPObjectItem(const char *ssid, const char *ssid_utf8) +{ + if (NULL == ssid || NULL == ssid_utf8) { + ALOGE("ssid or ssid_utf8 is NULL"); + return; + } + + struct accessPointObjectItem *pTmpItemNode = NULL; + struct accessPointObjectItem *pItemNode = NULL; + bool foundItem = false; + + pthread_mutex_lock(g_pItemListMutex); + pTmpItemNode = g_pItemList; + while (pTmpItemNode) { + if (pTmpItemNode->ssid && (*(pTmpItemNode->ssid) == ssid)) { + foundItem = true; + break; + } + pTmpItemNode = pTmpItemNode->pNext; + } + if (foundItem) { + if (DBG) + ALOGD("Found AP %s", pTmpItemNode->ssid->string()); + } else { + pItemNode = new struct accessPointObjectItem(); + if (NULL == pItemNode) { + ALOGE("Failed to allocate memory for new item!"); + goto EXIT; + } + memset(pItemNode, 0, sizeof(accessPointObjectItem)); + pItemNode->ssid_utf8 = new String8(ssid_utf8); + if (NULL == pItemNode->ssid_utf8) { + ALOGE("Failed to allocate memory for new ssid_utf8!"); + delete pItemNode; + goto EXIT; + } + pItemNode->ssid = new String8(ssid); + if (NULL == pItemNode->ssid) { + ALOGE("Failed to allocate memory for new ssid!"); + delete pItemNode; + goto EXIT; + } + + pItemNode->pNext = NULL; + if (DBG) + ALOGD("AP doesn't exist, new one for %s", ssid); + if (NULL == g_pItemList) { + g_pItemList = pItemNode; + g_pLastNode = g_pItemList; + } else { + g_pLastNode->pNext = pItemNode; + g_pLastNode = pItemNode; + } + } + +EXIT: + pthread_mutex_unlock(g_pItemListMutex); +} + +static int hex2num(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + return -1; +} + + +static int hex2byte(const char *hex) +{ + int a, b; + a = hex2num(*hex++); + if (a < 0) + return -1; + b = hex2num(*hex++); + if (b < 0) + return -1; + return (a << 4) | b; +} + +/* parse SSID string encoded from wpa_supplicant to normal string */ +static size_t ssid_decode(char *buf, size_t maxlen, const char *str) +{ + const char *pos = str; + size_t len = 0; + int val; + + while (*pos) { + if (len == maxlen) + break; + switch (*pos) { + case '\\': + pos++; + switch (*pos) { + case '\\': + buf[len++] = '\\'; + pos++; + break; + case '"': + buf[len++] = '"'; + pos++; + break; + case 'n': + buf[len++] = '\n'; + pos++; + break; + case 'r': + buf[len++] = '\r'; + pos++; + break; + case 't': + buf[len++] = '\t'; + pos++; + break; + case 'e': + buf[len++] = '\e'; + pos++; + break; + case 'x': + pos++; + val = hex2byte(pos); + if (val < 0) { + val = hex2num(*pos); + if (val < 0) + break; + buf[len++] = val; + pos++; + } else { + buf[len++] = val; + pos += 2; + } + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + val = *pos++ - '0'; + if (*pos >= '0' && *pos <= '7') + val = val * 8 + (*pos++ - '0'); + if (*pos >= '0' && *pos <= '7') + val = val * 8 + (*pos++ - '0'); + buf[len++] = val; + break; + default: + break; + } + break; + default: + buf[len++] = *pos++; + break; + } + } + + return len; +} + +/* This function can be used to convert SSIDs into printable form. Since wifi + * framework layer needs to parse printable form string. +*/ +static void ssid_encode(char *txt, size_t maxlen, const char *data, unsigned int len) +{ + char *end = txt + maxlen; + size_t i; + + for (i = 0; i < len; i++) { + if (txt + 4 > end) + break; + + switch (data[i]) { + case '\"': + *txt++ = '\\'; + *txt++ = '\"'; + break; + case '\\': + *txt++ = '\\'; + *txt++ = '\\'; + break; + case '\e': + *txt++ = '\\'; + *txt++ = 'e'; + break; + case '\n': + *txt++ = '\\'; + *txt++ = 'n'; + break; + case '\r': + *txt++ = '\\'; + *txt++ = 'r'; + break; + case '\t': + *txt++ = '\\'; + *txt++ = 't'; + break; + default: + if (data[i] >= 32 && data[i] <= 127) { + *txt++ = data[i]; + } else { + txt += snprintf(txt, end - txt, "\\x%02x", + data[i]); + } + break; + } + } + *txt = '\0'; +} + +/* check if the SSID string is UTF coded */ +static bool isUTF8String(const char* str, long length) +{ + unsigned int nBytes = 0; + unsigned char chr; + bool bAllAscii = true; + for (int i = 0; i < length; i++) { + chr = *(str+i); + if ((chr & 0x80) != 0) { + bAllAscii = false; + } + if (0 == nBytes) { + if (chr >= 0x80) { + if (chr >= 0xFC && chr <= 0xFD) { + nBytes = 6; + } else if (chr >= 0xF8) { + nBytes = 5; + } else if (chr >= 0xF0) { + nBytes = 4; + } else if (chr >= 0xE0) { + nBytes = 3; + } else if (chr >= 0xC0) { + nBytes = 2; + } else { + return false; + } + nBytes--; + } + } else { + if ((chr & 0xC0) != 0x80) { + return false; + } + nBytes--; + } + } + + if (nBytes > 0 || bAllAscii) { + return false; + } + return true; +} + +static void createFromHex(char *buf, int maxlen, const char *str) +{ + const char *pos = str; + int len = 0; + int val; + + while(*pos){ + if (len == maxlen) + break; + val = hex2byte(pos); + if (val < 0) { + val = hex2num(*pos); + if (val < 0) + break; + buf[len++] = val; + } else { + buf[len++] = val; + pos += 2; + } + } +} + +static size_t createToHex(char *buf, size_t buf_size, const char *str, unsigned int len) +{ + size_t i; + char *pos = buf, *end = buf + buf_size; + int ret; + if (buf_size == 0) + return 0; + for (i = 0; i < len; i++) { + ret = snprintf(pos, end - pos, "%02x", str[i]); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return pos - buf; + } + pos += ret; + } + end[-1] = '\0'; + return pos - buf; +} + +void parseScanResults(String16& str, const char *reply) +{ + unsigned int lineBeg = 0, lineEnd = 0; + size_t replyLen = strlen(reply); + char *pos = NULL; + char ssid[BUF_SIZE] = {0}; + char ssid_utf8[BUF_SIZE] = {0}; + char ssid_txt[BUF_SIZE] ={0}; + bool isUTF8 = false, isCh = false; + char buf[BUF_SIZE] = {0}; + String8 line; + + UConverterType conType = UCNV_UTF8; + char dest[CONVERT_LINE_LEN] = {0}; + UErrorCode err = U_ZERO_ERROR; + UConverter* pConverter = ucnv_open(CHARSET_CN, &err); + if (U_FAILURE(err)) { + ALOGE("ucnv_open error"); + return; + } + /* Parse every line of the reply to construct accessPointObjectItem list */ + for (lineBeg = 0, lineEnd = 0; lineEnd <= replyLen; ++lineEnd) { + if (lineEnd == replyLen || '\n' == reply[lineEnd]) { + line.setTo(reply + lineBeg, lineEnd - lineBeg + 1); + if (DBG) + ALOGD("%s, line=%s ", __FUNCTION__, line.string()); + if (strncmp(line.string(), "ssid=", 5) == 0) { + sscanf(line.string() + 5, "%[^\n]", ssid); + ssid_decode(buf,BUF_SIZE,ssid); + isUTF8 = isUTF8String(buf,sizeof(buf)); + isCh = false; + for (pos = buf; '\0' != *pos; pos++) { + if (0x80 == (*pos & 0x80)) { + isCh = true; + break; + } + } + if (DBG) + ALOGD("%s, ssid = %s, buf = %s,isUTF8= %d, isCh = %d", + __FUNCTION__, ssid, buf ,isUTF8, isCh); + if (!isUTF8 && isCh) { + ucnv_toAlgorithmic(conType, pConverter, dest, CONVERT_LINE_LEN, + buf, strlen(buf), &err); + if (U_FAILURE(err)) { + ALOGE("ucnv_toUChars error"); + goto EXIT; + } + ssid_encode(ssid_txt, BUF_SIZE, dest, strlen(dest)); + if (DBG) + ALOGD("%s, ssid_txt = %s", __FUNCTION__,ssid_txt); + str += String16("ssid="); + str += String16(ssid_txt); + str += String16("\n"); + strncpy(ssid_utf8, dest, strlen(dest)); + memset(dest, 0, CONVERT_LINE_LEN); + memset(ssid_txt, 0, BUF_SIZE); + } else { + memset(buf, 0, BUF_SIZE); + str += String16(line.string()); + } + } else if (strncmp(line.string(), "====", 4) == 0) { + if (DBG) + ALOGD("After sscanf,ssid:%s, isCh:%d", + ssid, isCh); + if( !isUTF8 && isCh){ + if (DBG) + ALOGD("add AP Object Item, ssid:%s l=%d, UTF8:%s, l=%d", + ssid, strlen(ssid), ssid_utf8, strlen(ssid_utf8)); + addAPObjectItem(buf, ssid_utf8); + memset(buf, 0, BUF_SIZE); + } + } + if (strncmp(line.string(), "ssid=", 5) != 0) + str += String16(line.string()); + lineBeg = lineEnd + 1; + } + } + +EXIT: + ucnv_close(pConverter); +} + +void constructSsid(String16& str, const char *reply) +{ + size_t replyLen = strlen(reply); + char ssid[BUF_SIZE] = {0}; + char buf[BUF_SIZE] = {0}; + char ssid_txt[BUF_SIZE] ={0}; + char *pos = NULL; + bool isUTF8 = false, isCh = false; + + char dest[CONVERT_LINE_LEN] = {0}; + UConverterType conType = UCNV_UTF8; + UErrorCode err = U_ZERO_ERROR; + UConverter* pConverter = ucnv_open(CHARSET_CN, &err); + if (U_FAILURE(err)) { + ALOGE("ucnv_open error"); + return; + } + sscanf(reply, "%[^\n]", ssid); + if (DBG) + ALOGD("%s, ssid = %s", __FUNCTION__, ssid); + createFromHex(buf, BUF_SIZE, ssid); + isUTF8 = isUTF8String(buf, sizeof(buf)); + isCh = false; + for (pos = buf; '\0' != *pos; pos++) { + if (0x80 == (*pos & 0x80)) { + isCh = true; + break; + } + } + if (!isUTF8 && isCh) { + ucnv_toAlgorithmic(conType, pConverter, dest, CONVERT_LINE_LEN, + buf, strlen(buf), &err); + if (U_FAILURE(err)) { + ALOGE("ucnv_toUChars error"); + goto EXIT; + } + createToHex(ssid_txt, strlen(dest)*2 + 1, dest, strlen(dest)); + if (DBG) + ALOGD("%s, ssid_txt = %s, dest = %s \n" , + __FUNCTION__, ssid_txt, dest); + str += String16(ssid_txt); + str += String16("\n"); + memset(dest, 0, CONVERT_LINE_LEN); + memset(buf, 0, BUF_SIZE); + memset(ssid_txt, 0, BUF_SIZE); + } else { + memset(buf, 0, BUF_SIZE); + str += String16(reply); + } + +EXIT: + ucnv_close(pConverter); +} + +jboolean setNetworkVariable(char *buf) +{ + struct accessPointObjectItem *pTmpItemNode = NULL; + char pos[BUF_SIZE] = {0}; + bool isCh = false; + bool gbk_found = false; + int i; + + unsigned int netId; + char name[BUF_SIZE] = {0}; + char value[BUF_SIZE] = {0}; + char interface[BUF_SIZE] = {0}; + char dummy[BUF_SIZE] = {0}; + if (strlen(buf) > BUF_SIZE) { + ALOGE("setNetworkVariable failed due to invalid length"); + return JNI_FALSE; + } + /* parse SET_NETWORK command*/ + sscanf(buf, "%s %s %d %s \"%s\"", interface, dummy, &netId, name, value); + + if (DBG) + ALOGD("parse SET_NETWORK command success, netId = %d, name = %s, value =%s, length=%d", + netId, name, value, strlen(value)); + + pthread_mutex_lock(g_pItemListMutex); + pTmpItemNode = g_pItemList; + if (NULL == pTmpItemNode) { + ALOGE("g_pItemList is NULL"); + } + while (pTmpItemNode) { + ALOGD("ssid_utf8 = %s, length=%d, value =%s, length=%d", + pTmpItemNode->ssid_utf8->string(),strlen(pTmpItemNode->ssid_utf8->string()), value, strlen(value)); + if (pTmpItemNode->ssid_utf8 && (0 == memcmp(pTmpItemNode->ssid_utf8->string(), value, + pTmpItemNode->ssid_utf8->length()))) { + gbk_found = true; + break; + } + pTmpItemNode = pTmpItemNode->pNext; + } + + if (0 == strncmp(name, "ssid", 4) && gbk_found) { + snprintf(buf, BUF_SIZE, "%s SET_NETWORK %d ssid \"%s\"", interface, netId, pTmpItemNode->ssid->string()); + if (DBG) + ALOGD("new SET_NETWORK command is: %s", buf); + } + + pthread_mutex_unlock(g_pItemListMutex); + + + return JNI_TRUE; +} + +void constructEventSsid(char *eventstr) +{ + char *pos = NULL; + char *tmp = NULL; + char ssid[BUF_SIZE] = {0}; + char ssid_txt[BUF_SIZE] = {0}; + char buf[BUF_SIZE] = {0}; + bool isUTF8 = false, isCh = false; + + UConverterType conType = UCNV_UTF8; + char dest[CONVERT_LINE_LEN] = {0}; + UErrorCode err = U_ZERO_ERROR; + UConverter* pConverter = ucnv_open(CHARSET_CN, &err); + if (U_FAILURE(err)) { + ALOGE("ucnv_open error"); + return; + } + + tmp = strstr(eventstr, " SSID"); + if (strlen(tmp) > 6 ) { + if(!strstr(tmp,"=")) + sscanf(tmp + 7, "%[^\']", ssid); + else + sscanf(tmp + 6, "%s", ssid); + if (DBG) + ALOGD("%s, SSID = %s", __FUNCTION__, ssid); + ssid_decode(buf,BUF_SIZE,ssid); + isUTF8 = isUTF8String(buf,sizeof(buf)); + isCh = false; + for (pos = buf; '\0' != *pos; pos++) { + if (0x80 == (*pos & 0x80)) { + isCh = true; + break; + } + } + if (!isUTF8 && isCh) { + ucnv_toAlgorithmic(conType, pConverter, dest, CONVERT_LINE_LEN, + buf, strlen(buf), &err); + if (U_FAILURE(err)) { + ALOGE("ucnv_toUChars error"); + goto EXIT; + } + ssid_encode(ssid_txt, BUF_SIZE, dest, strlen(dest)); + if (!strstr(tmp,"=")) + snprintf(eventstr + (strlen(eventstr) - strlen(tmp)), strlen(eventstr), " SSID \'%s\'", ssid_txt); + else + snprintf(eventstr + (strlen(eventstr) - strlen(tmp)), strlen(eventstr), " SSID=%s", ssid_txt); + if (DBG) + ALOGD("%s, ssid_txt = %s, eventsrt = %s", __FUNCTION__, ssid_txt, eventstr); + } + } + +EXIT: + ucnv_close(pConverter); +} + +} //namespace android diff --git a/core/java/com/android/internal/app/ActivityTrigger.java b/core/jni/android_net_wifi_Gbk2Utf.h similarity index 54% rename from core/java/com/android/internal/app/ActivityTrigger.java rename to core/jni/android_net_wifi_Gbk2Utf.h index 71aeff3c770a..37e7a2b75272 100644 --- a/core/java/com/android/internal/app/ActivityTrigger.java +++ b/core/jni/android_net_wifi_Gbk2Utf.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Code Aurora Forum. All rights reserved. + * Copyright (c) 2013, The Linux Foundation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -8,7 +8,7 @@ * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * * Neither the name of Code Aurora nor + * * Neither the name of The Linux Foundation nor * the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written * permission. @@ -26,47 +26,31 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package com.android.internal.app; +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -import android.content.ComponentName; -import android.content.Intent; -import android.util.Log; +namespace android { -public class ActivityTrigger -{ - private static final String TAG = "ActivityTrigger"; +struct accessPointObjectItem { + String8 *ssid_utf8; + String8 *ssid; + struct accessPointObjectItem *pNext; +}; - /** &hide */ - public ActivityTrigger() { - //Log.d(TAG, "ActivityTrigger initialized"); - } +extern void parseScanResults(String16& str, const char *reply); - /** &hide */ - protected void finalize() { - native_at_deinit(); - } +extern void constructSsid(String16& str, const char *reply); - /** &hide */ - public void activityStartTrigger(Intent intent) { - ComponentName cn = intent.getComponent(); - String activity = null; +extern void constructEventSsid(char *eventstr); - if (cn != null) - activity = cn.flattenToString(); - native_at_startActivity(activity); - } +extern jboolean setNetworkVariable(char *buf); - /** &hide */ - public void activityResumeTrigger(Intent intent) { - ComponentName cn = intent.getComponent(); - String activity = null; - - if (cn != null) - activity = cn.flattenToString(); - native_at_resumeActivity(activity); - } - - private native void native_at_startActivity(String activity); - private native void native_at_resumeActivity(String activity); - private native void native_at_deinit(); -} +} //namespace android diff --git a/core/jni/android_net_wifi_WifiNative.cpp b/core/jni/android_net_wifi_WifiNative.cpp index b869cc054307..b3c4e288e30d 100644 --- a/core/jni/android_net_wifi_WifiNative.cpp +++ b/core/jni/android_net_wifi_WifiNative.cpp @@ -18,27 +18,37 @@ #include "jni.h" #include -#include #include -#include -#include #include "wifi.h" #define REPLY_BUF_SIZE 4096 // wpa_supplicant's maximum size. #define EVENT_BUF_SIZE 2048 +#define CONVERT_LINE_LEN 2048 +#define CHARSET_CN ("gbk") + +#include "android_net_wifi_Gbk2Utf.h" + namespace android { static jint DBG = false; +extern struct accessPointObjectItem *g_pItemList; +extern struct accessPointObjectItem *g_pLastNode; +extern pthread_mutex_t *g_pItemListMutex; +extern String8 *g_pCurrentSSID; static bool doCommand(JNIEnv* env, jstring javaCommand, char* reply, size_t reply_len) { ScopedUtfChars command(env, javaCommand); if (command.c_str() == NULL) { return false; // ScopedUtfChars already threw on error. } - + if(strstr(command.c_str(), "SET_NETWORK")) { + if(!setNetworkVariable((char *)command.c_str())) { + return false; + } + } if (DBG) { ALOGD("doCommand: %s", command.c_str()); } @@ -76,10 +86,22 @@ static jboolean doBooleanCommand(JNIEnv* env, jstring javaCommand) { // Send a command to the supplicant, and return the reply as a String. static jstring doStringCommand(JNIEnv* env, jstring javaCommand) { char reply[REPLY_BUF_SIZE]; + ScopedUtfChars command(env, javaCommand); if (!doCommand(env, javaCommand, reply, sizeof(reply))) { return NULL; } - return env->NewStringUTF(reply); + if (DBG) ALOGD("cmd = %s, reply: %s", command.c_str(), reply); + String16 str; + if (strstr(command.c_str(),"BSS RANGE=")) { + parseScanResults(str,reply); + } else if (strstr(command.c_str(),"GET_NETWORK") && + strstr(command.c_str(),"ssid") && !strstr(command.c_str(),"bssid") + && !strstr(command.c_str(),"scan_ssid")){ + constructSsid(str, reply); + } else { + str += String16((char *)reply); + } + return env->NewString((const jchar *)str.string(), str.size()); } static jboolean android_net_wifi_setMode(JNIEnv* env, jobject, jint type) @@ -94,11 +116,41 @@ static jboolean android_net_wifi_isDriverLoaded(JNIEnv* env, jobject) static jboolean android_net_wifi_loadDriver(JNIEnv* env, jobject) { + g_pItemListMutex = new pthread_mutex_t; + if (NULL == g_pItemListMutex) { + ALOGE("Failed to allocate memory for g_pItemListMutex!"); + return JNI_FALSE; + } + pthread_mutex_init(g_pItemListMutex, NULL); return (::wifi_load_driver() == 0); } static jboolean android_net_wifi_unloadDriver(JNIEnv* env, jobject) { + if (g_pItemListMutex != NULL) { + pthread_mutex_lock(g_pItemListMutex); + struct accessPointObjectItem *pCurrentNode = g_pItemList; + struct accessPointObjectItem *pNextNode = NULL; + while (pCurrentNode) { + pNextNode = pCurrentNode->pNext; + if (NULL != pCurrentNode->ssid) { + delete pCurrentNode->ssid; + pCurrentNode->ssid = NULL; + } + if (NULL != pCurrentNode->ssid_utf8) { + delete pCurrentNode->ssid_utf8; + pCurrentNode->ssid_utf8 = NULL; + } + delete pCurrentNode; + pCurrentNode = pNextNode; + } + g_pItemList = NULL; + g_pLastNode = NULL; + pthread_mutex_unlock(g_pItemListMutex); + pthread_mutex_destroy(g_pItemListMutex); + delete g_pItemListMutex; + g_pItemListMutex = NULL; + } return (::wifi_unload_driver() == 0); } @@ -127,6 +179,9 @@ static jstring android_net_wifi_waitForEvent(JNIEnv* env, jobject) char buf[EVENT_BUF_SIZE]; int nread = ::wifi_wait_for_event(buf, sizeof buf); if (nread > 0) { + if (strstr(buf, " SSID=") || strstr(buf, " SSID ")){ + constructEventSsid(buf); + } return env->NewStringUTF(buf); } else { return NULL; diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index b2c42e51a34d..709f2bffd84d 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -1274,7 +1274,7 @@ static jboolean android_content_AssetManager_applyStyle(JNIEnv* env, jobject cla if (newBlock >= 0) { DEBUG_STYLES(LOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data)); - newBlock = res.resolveReference(&value, block, &resid, + newBlock = res.resolveReference(&value, newBlock, &resid, &typeSetFlags, &config); #if THROW_ON_BAD_ID if (newBlock == BAD_INDEX) { diff --git a/core/jni/com_android_internal_app_ActivityTrigger.cpp b/core/jni/com_android_internal_app_ActivityTrigger.cpp deleted file mode 100644 index 950e4ac718a0..000000000000 --- a/core/jni/com_android_internal_app_ActivityTrigger.cpp +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (c) 2011, Code Aurora Forum. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Code Aurora nor - * the names of its contributors may be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#define LOG_TAG "ActTriggerJNI" - -#include "jni.h" -#include "JNIHelp.h" -#include - -#include -#include -#include - -#include -#include - -#define LIBRARY_PATH_PREFIX "/vendor/lib/" - -namespace android -{ - -// ---------------------------------------------------------------------------- - -static void (*startActivity)(const char *) = NULL; -static void (*resumeActivity)(const char *) = NULL; -static void *dlhandle = NULL; - -// ---------------------------------------------------------------------------- - -static void -com_android_internal_app_ActivityTrigger_native_at_init() -{ - const char *rc; - void (*init)(void); - char buf[PROPERTY_VALUE_MAX]; - int len; - - /* Retrieve name of vendor extension library */ - if (property_get("ro.vendor.extension_library", buf, NULL) <= 0) { - return; - } - - /* Sanity check - ensure */ - buf[PROPERTY_VALUE_MAX-1] = '\0'; - if ((strncmp(buf, LIBRARY_PATH_PREFIX, sizeof(LIBRARY_PATH_PREFIX) - 1) != 0) - || - (strstr(buf, "..") != NULL)) { - return; - } - - dlhandle = dlopen(buf, RTLD_NOW | RTLD_LOCAL); - if (dlhandle == NULL) { - return; - } - - dlerror(); - - *(void **) (&startActivity) = dlsym(dlhandle, "activity_trigger_start"); - if ((rc = dlerror()) != NULL) { - goto cleanup; - } - *(void **) (&resumeActivity) = dlsym(dlhandle, "activity_trigger_resume"); - if ((rc = dlerror()) != NULL) { - goto cleanup; - } - *(void **) (&init) = dlsym(dlhandle, "activity_trigger_init"); - if ((rc = dlerror()) != NULL) { - goto cleanup; - } - (*init)(); - return; - -cleanup: - startActivity = NULL; - resumeActivity = NULL; - if (dlhandle) { - dlclose(dlhandle); - dlhandle = NULL; - } -} - -static void -com_android_internal_app_ActivityTrigger_native_at_deinit(JNIEnv *env, jobject clazz) -{ - void (*deinit)(void); - - if (dlhandle) { - startActivity = NULL; - resumeActivity = NULL; - - *(void **) (&deinit) = dlsym(dlhandle, "activity_trigger_deinit"); - if (deinit) { - (*deinit)(); - } - - dlclose(dlhandle); - dlhandle = NULL; - } -} - -static void -com_android_internal_app_ActivityTrigger_native_at_startActivity(JNIEnv *env, jobject clazz, jstring activity) -{ - if (startActivity && activity) { - const char *actStr = env->GetStringUTFChars(activity, NULL); - if (actStr) { - (*startActivity)(actStr); - } - } -} - -static void -com_android_internal_app_ActivityTrigger_native_at_resumeActivity(JNIEnv *env, jobject clazz, jstring activity) -{ - if (resumeActivity && activity) { - const char *actStr = env->GetStringUTFChars(activity, NULL); - if (actStr) { - (*resumeActivity)(actStr); - } - } -} - -// ---------------------------------------------------------------------------- - -static JNINativeMethod gMethods[] = { - {"native_at_startActivity", "(Ljava/lang/String;)V", (void *)com_android_internal_app_ActivityTrigger_native_at_startActivity}, - {"native_at_resumeActivity", "(Ljava/lang/String;)V", (void *)com_android_internal_app_ActivityTrigger_native_at_resumeActivity}, - {"native_at_deinit", "()V", (void *)com_android_internal_app_ActivityTrigger_native_at_deinit}, -}; - - -int register_com_android_internal_app_ActivityTrigger(JNIEnv *env) -{ - com_android_internal_app_ActivityTrigger_native_at_init(); - - return AndroidRuntime::registerNativeMethods(env, - "com/android/internal/app/ActivityTrigger", gMethods, NELEM(gMethods)); -} - -} // namespace android diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index efecaa6cd55c..7ee6c78a6285 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -255,6 +255,7 @@ android:name="com.android.server.connectivityservice.CONNECTED_TO_PROVISIONING_NETWORK_ACTION" /> + @@ -1922,7 +1923,7 @@ + android:protectionLevel="signature|system" /> - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + diff --git a/core/res/res/drawable/stat_sys_upload.xml b/core/res/res/drawable/stat_sys_upload.xml index 12d707093b5d..a9d960950761 100644 --- a/core/res/res/drawable/stat_sys_upload.xml +++ b/core/res/res/drawable/stat_sys_upload.xml @@ -1,5 +1,7 @@ - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + diff --git a/core/res/res/layout/notification_action.xml b/core/res/res/layout/notification_action.xml index 4e7c74c879bc..e93f96724ff9 100644 --- a/core/res/res/layout/notification_action.xml +++ b/core/res/res/layout/notification_action.xml @@ -23,7 +23,7 @@ android:gravity="start|center_vertical" android:drawablePadding="8dp" android:paddingStart="8dp" - android:textColor="#ccc" + android:textColor="@color/notification_action_text_color" android:textSize="14dp" android:singleLine="true" android:ellipsize="end" diff --git a/core/res/res/layout/notification_action_list.xml b/core/res/res/layout/notification_action_list.xml index 400decc49945..544e9ef6649b 100644 --- a/core/res/res/layout/notification_action_list.xml +++ b/core/res/res/layout/notification_action_list.xml @@ -25,6 +25,7 @@ android:showDividers="middle" android:divider="?android:attr/listDivider" android:dividerPadding="12dp" + android:background="@drawable/notification_action_list_bg" > diff --git a/core/res/res/values-af/strings_custom.xml b/core/res/res/values-af/strings_custom.xml index ca1ea94980ef..f89585f90d90 100644 --- a/core/res/res/values-af/strings_custom.xml +++ b/core/res/res/values-af/strings_custom.xml @@ -1,5 +1,5 @@ - + + + + Restartovat + Rychle restartovat Recovery Download Bootloader @@ -87,4 +88,11 @@ Umožnit aplikacím vytvářet nová témata a nastavit používané téma. nastavit pozadí na zamčené obrazovce Umožnit aplikaci změnit pozadí na zamčené obrazovce. + Zabezpečení + Oprávnění vztahující se k bezpečnostním informacím zařízení. + číst černou listinu telefonu + Umožnit aplikacím číst informace o blokovaných telefonních číslech pro příchozí volání a zprávy. + měnit černou listinu telefonu + Umožnit aplikacím měnit telefonní čísla blokována pro příchozí volání a zprávy. + Dotykem spravovat diff --git a/core/res/res/values-da/strings_custom.xml b/core/res/res/values-da/strings_custom.xml index ca1ea94980ef..f89585f90d90 100644 --- a/core/res/res/values-da/strings_custom.xml +++ b/core/res/res/values-da/strings_custom.xml @@ -1,5 +1,5 @@ - + + Neustart + Schneller Neustart Recovery Download Bootloader @@ -87,4 +88,11 @@ Gestattet der App neue Themes einzufügen und das von Ihnen verwendete Theme zu bestimmen. Sperrbildschirm-Hintergrund setzen Ermöglicht einer App, den Sperrbildschirm-Hintergrund zu ändern. + Sicherheit + Genehmigungen bezüglich der Informationen zur Geräte-Sicherheit beziehen. + Sperrliste lesen + Erlaubt es der App, Informationen über Nummern, von denen eingehende Anrufe und Nachrichten blockiert werden, zu lesen. + Sperrliste ändern + Erlaubt es der App, die Nummern, von denen eingehende Anrufe und Nachrichten blockiert werden, zu ändern. + Tippen zum Verwalten diff --git a/core/res/res/values-el/strings_custom.xml b/core/res/res/values-el/strings_custom.xml index cb9f14addac5..f235a34d92fb 100644 --- a/core/res/res/values-el/strings_custom.xml +++ b/core/res/res/values-el/strings_custom.xml @@ -1,5 +1,5 @@ - + + Reiniciar + Reinicio rápido Recovery Modo Descarga Bootloader @@ -89,4 +90,11 @@ modificar el tema que este aplicado.
Establecer el fondo de pantalla de bloqueo Permite que otras aplicaciones cambien el fondo de pantalla de bloqueo. + Seguridad + Permisos relacionados con información de seguridad del dispositivo. + Leer lista negra del teléfono + Permite que la aplicación lea la información sobre los números de teléfono bloqueados para llamadas o mensajes entrantes. + cambiar la lista negra del teléfono + Permite que la aplicación cambie los números de teléfono bloqueados para llamadas o mensajes entrantes. + Toca para gestionar diff --git a/core/res/res/values-fa/strings_custom.xml b/core/res/res/values-fa/strings_custom.xml new file mode 100644 index 000000000000..f89585f90d90 --- /dev/null +++ b/core/res/res/values-fa/strings_custom.xml @@ -0,0 +1,20 @@ + + + + diff --git a/core/res/res/values-fi/strings_custom.xml b/core/res/res/values-fi/strings_custom.xml index 63226371591b..91e90162dae3 100644 --- a/core/res/res/values-fi/strings_custom.xml +++ b/core/res/res/values-fi/strings_custom.xml @@ -1,5 +1,5 @@ - + + Redémarrer + Redémarrage logiciel Recovery Téléchargement Bootloader - L\'appareil redémarre + Votre appareil redémarre Blocage de la rotation activé/désactivé Mode immersif Le mode immersif est activé @@ -52,10 +53,12 @@ Déverrouiller Appareil photo Vide - Gadgets de navigation + Nav Widgets Fenêtre Application Ruban AOKP Application/raccourci personnalisé + Flèche gauche + Flèche droite Application tuée Capture d\'écran Capture vidéo @@ -75,14 +78,21 @@ Agrandir à (droite) Désactiver le Wi-Fi Désactiver le Partage de Connexion - outrepasser le bouton d\'alimentation - Permet à une application de remplacer le bouton Power + outrepasser le bouton marche/arrêt + Permet à une application de remplacer le bouton marche/arrêt accéder au service de thème Autorise une application à accéder au service de thème. Ne devrait jamais être nécessaire pour des applications standard. lire les information de votre thème Autorise une application à lire vos thèmes et à déterminer quel thème vous avez appliqué. modifier vos thèmes Autorise une application à insérer de nouveaux thèmes et à modifier le thème que vous avez appliqué. - Définir le fond d\'écran du clavier de verrouillage + Définir le fond d\'écran de verrouillage Autorise une application à changer le fond de l\'écran de verrouillage. + Sécurité + Autorisations liées à des informations de sécurité de l\'appareil. + lire la liste noire du téléphone + Autorise une application à lire les informations sur les numéros de téléphone qui sont bloqués pour les appels ou les messages entrants. + modifier la liste noire du téléphone + Autorise une application à modifier les numéros de téléphone qui sont bloqués pour les appels ou les messages entrants. + Appuyez pour configurer diff --git a/core/res/res/values-hu/strings_custom.xml b/core/res/res/values-hu/strings_custom.xml index e80d345b4eb5..650d35fcdd47 100644 --- a/core/res/res/values-hu/strings_custom.xml +++ b/core/res/res/values-hu/strings_custom.xml @@ -1,5 +1,5 @@ - + Újraindítás + Lágy újraindítás Recovery Letöltés Bootloader A készülék újraindul - Forgó zár kapcsolva + Forgatás zárolása átváltva Teljes képernyős mód Teljes képernyős mód bekapcsolva Teljes képernyős mód kikapcsolva Kezdőoldal Vissza - Legutóbbi + Legutóbbiak Legutóbbi (GB stílusú) Keresés - Képernyőkép + Képernyőmentés Menü - Billentyűzet váltó nyitása + Billentyűzet váltó megnyítása Feladat befejezése Utolsó alk. Kikapcsolás Értesítések Keresés (Google Now) Óra beállítások - Hang keresés + Hangalapú keresés Zseblámpa - sneg/Néma + Cseng/Néma Cseng/Rezeg Cseng/Rezeg/Néma Esemény @@ -56,16 +57,18 @@ Alk. Ablak AOKP Szalagsáv Egyéni Alk./Parancsikon + Balra nyíl + Jobbra nyíl Alkalmazás kilőve Képernyőmentés - Képernyőfelvétel + Képernyő felvétel Áttűnés - Jobbról csúszik - Balról csúszik - Jobbról csúszik (nincs áttűnés) - Balról csúszik (nincs áttűnés) - Alulról csúszik - Felülről csúszik + Beúszás jobbról + Beúszás balról + Beúszás jobbról (nincs halványítás) + Beúszás balról (nincs halványítás) + Beúszás alulról + Beúszás felűlről Alaphelyzet Áttetsző Nyúlás (Felülről) @@ -73,5 +76,23 @@ Nyúlás (Alulról) Nyúlás (Balról) Nyúlás (Jobbról) - Wi-fi kikapcsolása + Wi-Fi kikapcsolása + Megosztás kikapcsolása + kikapcsológomb felülbírálata + Lehetővé teszi az alkalmazás számára, hogy felülírja a kikapcsológombot + hozzáférés a témaszolgáltatáshoz + Lehetővé teszi az alkalmazás számára, hogy hozzáférjen a témaszolgáltatáshoz. Általános alkalmazásoknak soha nem kell ilyen engedély. + téma információjának megtekintése + Lehetővé teszi az alkalmazásnak, hogy megtekintse a témákat és megállapítsa, melyik téma aktív. + témák módosítása + Lehetővé teszi az alkalmazásnak, hogy új témákat hozzon létre és módosítsa a jelenleg aktív témát. + zárképernyő háttérképének beállítása + Lehetővé teszi az alkalmazás számára a képernyőzár hátterének módosítását. + Biztonság + Engedélyek az eszköz biztonsági információira vonatkozóan. + tiltólista olvasása + Engedélyezi az alkalmazásnak, hogy olvassa azokat a számokat melyek blokkolva vannak bejövő hívásnál vagy üzenetnél. + tiltólista módosítása + Lehetővé teszi az alkalmazás számára, hogy szerkessze azokat a telefonszámokat melyek blokkolva vannak bejövő hívásnál vagy üzenetnél. + Kezeléshez érintse meg diff --git a/core/res/res/values-it/strings_custom.xml b/core/res/res/values-it/strings_custom.xml index 247319a28c8d..6dd9816c254e 100644 --- a/core/res/res/values-it/strings_custom.xml +++ b/core/res/res/values-it/strings_custom.xml @@ -1,5 +1,5 @@ - + Riavvia + Riavvio veloce Recovery Scarica Bootloader @@ -41,7 +42,7 @@ Notifiche Ricerca (Google Now) Opzioni Orologio - Voice Search + Ricerca Vocale Torcia Suoneria/Silenzioso Suoneria/Vibrazione @@ -93,4 +94,5 @@ Permette all\'app di leggere le informazioni sui numeri di telefono che sono bloccati per le chiamate o i messaggi in entrata. Cambia la blacklist del telefono Permette all\'app di cambiare i numeri di telefono che sono bloccati per le chiamate o i messaggi in entrata. + Tocca per gestire diff --git a/core/res/res/values-iw/strings_custom.xml b/core/res/res/values-iw/strings_custom.xml index abf2e69c016e..c936f8cd74dc 100644 --- a/core/res/res/values-iw/strings_custom.xml +++ b/core/res/res/values-iw/strings_custom.xml @@ -1,5 +1,5 @@ - + + 再起動 + ソフトリブート リカバリー ダウンロード ブートローダー @@ -93,4 +94,5 @@ 着信やメッセージをブロックしている電話番号に関する情報の読み取りをアプリに許可する 携帯電話のブラックリストの変更 着信やメッセージをブロックしている電話番号の変更をアプリに許可する + タップして管理 diff --git a/core/res/res/values-ko/strings_custom.xml b/core/res/res/values-ko/strings_custom.xml index d9d7e7c87fb4..5c7122857262 100644 --- a/core/res/res/values-ko/strings_custom.xml +++ b/core/res/res/values-ko/strings_custom.xml @@ -1,5 +1,5 @@ - + 재부팅 + 빠른 다시 시작 복구 모드 다운로드 부트로더 @@ -89,4 +90,11 @@        당신이 적용한 테마가 수정합니다.
keyguard 배경설정 앱으로 잠금화면 배경을 변경할 수 있습니다. + 보안 + 장치의 보안 정보에 관련된 권한입니다. + 전화 블랙리스트 읽기 + 앱이 전화 또는 메시지 수신이 차단된 전화번호들의 정보를 읽을 수 있도록 허용합니다. + 전화 블랙리스트 수정 + 앱이 어떤 전화번호들의 전화 또는 메시지를 차단할 것인지를 변경할 수 있도록 허용합니다. + 관리 탭 diff --git a/core/res/res/values-mcc234-mnc01/config.xml b/core/res/res/values-mcc234-mnc01/config.xml new file mode 100644 index 000000000000..32335ae29a5c --- /dev/null +++ b/core/res/res/values-mcc234-mnc01/config.xml @@ -0,0 +1,25 @@ + + + + + + + 23430 + + diff --git a/core/res/res/values-mcc268-mnc04/config.xml b/core/res/res/values-mcc268-mnc04/config.xml new file mode 100644 index 000000000000..cc70a32649b8 --- /dev/null +++ b/core/res/res/values-mcc268-mnc04/config.xml @@ -0,0 +1,25 @@ + + + + + + + 26801 + + diff --git a/core/res/res/values-mcc268-mnc07/config.xml b/core/res/res/values-mcc268-mnc07/config.xml new file mode 100644 index 000000000000..8af71994c581 --- /dev/null +++ b/core/res/res/values-mcc268-mnc07/config.xml @@ -0,0 +1,25 @@ + + + + + + + 26803 + + diff --git a/core/res/res/values-mcc310-mnc410/config.xml b/core/res/res/values-mcc310-mnc410/config.xml index 73aa1cef1a07..41d89dade33e 100644 --- a/core/res/res/values-mcc310-mnc410/config.xml +++ b/core/res/res/values-mcc310-mnc410/config.xml @@ -25,4 +25,5 @@ --> 1410 + false diff --git a/core/res/res/values-mcc730-mnc07/config.xml b/core/res/res/values-mcc730-mnc07/config.xml new file mode 100644 index 000000000000..fe5075c644c5 --- /dev/null +++ b/core/res/res/values-mcc730-mnc07/config.xml @@ -0,0 +1,25 @@ + + + + + + + 73002 + + diff --git a/core/res/res/values-nl/strings_custom.xml b/core/res/res/values-nl/strings_custom.xml index 747c54220b24..a2e4aefdd9d0 100644 --- a/core/res/res/values-nl/strings_custom.xml +++ b/core/res/res/values-nl/strings_custom.xml @@ -1,5 +1,5 @@ - + + + Uruchom ponownie + Miękki restart Restart do recovery Restart do trybu Download Restart do bootloadera @@ -93,4 +94,5 @@ Umożliwia aplikacji odczytywanie informacji o numerach telefonów, od których przychodzące połączenia i wiadomości są blokowane. edytowanie czarnej listy Umożliwia aplikacji zmianę numerów telefonów, od których przychodzące połączenia i wiadomości są blokowane. + Dotknij, aby zarządzać diff --git a/core/res/res/values-pt-rBR/strings_custom.xml b/core/res/res/values-pt-rBR/strings_custom.xml index b71e95b1e222..544397dedd98 100644 --- a/core/res/res/values-pt-rBR/strings_custom.xml +++ b/core/res/res/values-pt-rBR/strings_custom.xml @@ -1,5 +1,5 @@ - + + + + Перезагрузка + Мягкая перезагрузка Режим восстановления Загрузка Загрузчик @@ -93,4 +94,5 @@ Приложение сможет считывать информацию о списке абонентов, от которых заблокированы входящие звонки или сообщения. Изменение чёрного списка Приложение сможет изменять список абонентов, от которых заблокированы входящие звонки или сообщения. + Нажать для управления diff --git a/core/res/res/values-si/strings_custom.xml b/core/res/res/values-si/strings_custom.xml index f72804d645a0..287ee88d81c6 100644 --- a/core/res/res/values-si/strings_custom.xml +++ b/core/res/res/values-si/strings_custom.xml @@ -1,5 +1,5 @@ - + නැවත පනගන්වන්න + මෘදුව නැවත පනගන්වන්න ප්‍රතිසාධන ප්‍රකාරයට බාගැනීමේ ප්‍රකාරයට ඇරඹුම් ප්‍රවේශකයට @@ -93,4 +94,5 @@ පැමිණෙන ඇමතුම් හෝ පණිවිඩ සඳහා අවහිර කර ඇති ඇමතුම් අංක පිලිබඳ තොරතුරු කියවීම සඳහා යෙදුමකට අවසර දෙන්න. දුරකථන අපලේඛණය වෙනස් කරන්න පැමිණෙන ඇමතුම් හෝ පණිවිඩ සඳහා අවහිර කර ඇති ඇමතුම් අංක වෙනස් කිරීම සඳහා යෙදුමකට අවසර දෙන්න. + පාලනයට තට්ටුකරන්න diff --git a/core/res/res/values-sr/strings_custom.xml b/core/res/res/values-sr/strings_custom.xml index ca1ea94980ef..f89585f90d90 100644 --- a/core/res/res/values-sr/strings_custom.xml +++ b/core/res/res/values-sr/strings_custom.xml @@ -1,5 +1,5 @@ - + + Starta om + Mjuk omstart Recovery Ladda ner Bootloader @@ -87,4 +88,11 @@ Tillåter att appen infogar nya teman och ändra vilket tema som du har använt. ställa in bakgrundsbild på låsskärm Tillåter en app att ändra låsskärmens bakgrundsbild. + Säkerhet + Behörighetsrelaterad säkerhetsinformation om enheten. + Läs telefonens svartklista + Tillåter en app att läsa information om telefonnummer som blockeras för inkommande samtal eller meddelanden. + ändra telefonens svartlista + Tillåter en app att ändra de telefonnummer som är blockerade för inkommande samtal eller meddelanden. + Tryck för att hantera diff --git a/core/res/res/values-tr/strings_custom.xml b/core/res/res/values-tr/strings_custom.xml index 2338d0a04afe..bfdfc5013c8d 100644 --- a/core/res/res/values-tr/strings_custom.xml +++ b/core/res/res/values-tr/strings_custom.xml @@ -1,5 +1,5 @@ - + Yeniden Başlat + Yazılımsal yeniden başlatma Kurtarma İndir Ön yükleyici @@ -93,4 +94,5 @@ Bir uygulamanın gelen çağrılar ya da mesajlar için engellenmiş telefon numaralarını değiştirmesine izin verir. telefon kara listesini değiştir Bir uygulamanın gelen çağrılar ya da mesajlar için engellenmiş telefon numaralarını değiştirmesine izin verir. + Yönetmek için dokunun diff --git a/core/res/res/values-uk/strings_custom.xml b/core/res/res/values-uk/strings_custom.xml index ca1ea94980ef..f89585f90d90 100644 --- a/core/res/res/values-uk/strings_custom.xml +++ b/core/res/res/values-uk/strings_custom.xml @@ -1,5 +1,5 @@ - + + + 重新启动 + 软重启 恢复模式 下载模式 引导模式 @@ -56,6 +57,8 @@ 应用窗口 AOKP操作区 自定义程序/快捷方式 + 左箭头 + 右箭头 应用程序已结束 屏幕截图 屏幕录像 @@ -85,4 +88,11 @@ 允许程序修改您已经使用的主题,并插入新的主题。 设置锁屏壁纸 允许应用更改锁屏壁纸。 + 安全 + 与设备安全信息相关的权限。 + 读取手机黑名单 + 允许应用读取有关被阻止来电或信息的电话号码的信息。 + 更改手机黑名单 + 允许应用更改被阻止来电或信息的电话号码。 + 点击以管理 diff --git a/core/res/res/values-zh-rHK/strings_custom.xml b/core/res/res/values-zh-rHK/strings_custom.xml index 5bea8d87ef92..4c37a6066956 100644 --- a/core/res/res/values-zh-rHK/strings_custom.xml +++ b/core/res/res/values-zh-rHK/strings_custom.xml @@ -1,5 +1,5 @@ - + 重新啟動 + 軟重新啟動 修復模式 下載 啟動載入器 @@ -56,6 +57,8 @@ 應用程式視窗 AOKP 功能區 自訂應用程式與捷徑 + 左箭頭 + 右箭頭 應用程式已強制結束 擷取螢幕畫面 螢幕錄製 @@ -84,6 +87,13 @@ 允許應用程式讀取您的主題,並確定您已套用的主題。 變更佈景主題 允許程式變更目前主题,並插入新的主題。 - 設定螢幕鎖定牆紙 - 允許應用程式變更螢幕鎖定牆紙。 + 設定鍵盤鎖的牆紙 + 允許應用程式變更鎖定畫面牆紙。 + 安全 + 與裝置安全資訊相關的許可權。 + 讀取電話黑名單 + 允許應用程式讀取關於被封鎖來電或短訊的電話號碼資訊。 + 變更電話黑名單 + 允許應用程式更改被封鎖來電或短訊的電話號碼。 + 輕按以管理 diff --git a/core/res/res/values-zh-rTW/strings_custom.xml b/core/res/res/values-zh-rTW/strings_custom.xml index e492dc1ab127..907fd52008be 100644 --- a/core/res/res/values-zh-rTW/strings_custom.xml +++ b/core/res/res/values-zh-rTW/strings_custom.xml @@ -1,5 +1,5 @@ - + - + @string/reboot + @string/reboot_soft @string/reboot_recovery @string/reboot_bootloader @@ -27,6 +28,7 @@ + soft_reboot recovery bootloader diff --git a/core/res/res/values/colors_custom.xml b/core/res/res/values/colors_custom.xml index f1d8f86b42c4..cf093072bb1a 100644 --- a/core/res/res/values/colors_custom.xml +++ b/core/res/res/values/colors_custom.xml @@ -21,4 +21,9 @@ @android:color/holo_blue_light @android:color/holo_blue_light @android:color/holo_blue_light + + + #00000000 + + #ccc diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index cf3b56140bbf..cbd295d585eb 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -705,6 +705,10 @@ + 255 + 0 + false + @@ -1159,8 +1163,10 @@ 10 - 1500 + may have a specific value set in an overlay config.xml file. + This really must be 1358 as per 3GPP standards and packet segmentation on the LTE + radio.--> + 1358 + + + false + 250 + + + + diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 0913142b3b19..49e60f128f9b 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -2791,14 +2791,14 @@ Allows the app to modify the Browser\'s history or bookmarks stored on your tablet. This may allow the app to erase or modify Browser data. Note: this - permission may note be enforced by third-party browsers or other + permission may not be enforced by third-party browsers or other applications with web browsing capabilities. Allows the app to modify the Browser\'s history or bookmarks stored on your phone. This may allow the app to erase or modify Browser data. Note: - this permission may note be enforced by third-party browsers or other + this permission may not be enforced by third-party browsers or other applications with web browsing capabilities. Access keyguard secure storage - Allows an application to access keguard secure storage. + Allows an application to access keyguard secure storage. Control displaying and hiding keyguard - Allows an application to control keguard. + Allows an application to control keyguard. Touch twice for zoom control diff --git a/core/res/res/values/strings_custom.xml b/core/res/res/values/strings_custom.xml index 6b0ea610c0aa..347266a0eb69 100644 --- a/core/res/res/values/strings_custom.xml +++ b/core/res/res/values/strings_custom.xml @@ -19,6 +19,7 @@ Reboot + Soft reboot Recovery @@ -144,4 +145,10 @@ Allows an app to change the phone numbers that are blocked for incoming calls or messages. + + Tap to manage + + + Failed to install theme + %s failed to install diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index db0cf0307704..8f47b3b39ca7 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1509,6 +1509,9 @@ + + + diff --git a/core/res/res/values/symbols_custom.xml b/core/res/res/values/symbols_custom.xml index 865d726c60d9..e4a10452ffde 100644 --- a/core/res/res/values/symbols_custom.xml +++ b/core/res/res/values/symbols_custom.xml @@ -34,8 +34,7 @@ - - + @@ -244,4 +243,20 @@ + + + + + + + + + + + + + + + + diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h index 80eec8c66a82..4bb5ac830034 100644 --- a/include/androidfw/ResourceTypes.h +++ b/include/androidfw/ResourceTypes.h @@ -1590,6 +1590,7 @@ class ResTable uint32_t pkgIdOverride); bool isResTypeAllowed(const char* type) const; + bool isProtectedAttr(uint32_t resID) const; void print_value(const Package* pkg, const Res_value& value) const; diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp index 6bca07fe7f6c..77791340fd4c 100644 --- a/libs/androidfw/AssetManager.cpp +++ b/libs/androidfw/AssetManager.cpp @@ -462,6 +462,7 @@ String8 AssetManager::getPkgName(const char *apkPath) { } + tree.uninit(); manifestAsset->close(); return pkgName; } diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 946203d79b22..b2860fbe7d68 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -70,6 +70,10 @@ namespace android { // size measured in sizeof(uint32_t) #define IDMAP_HEADER_SIZE (ResTable::IDMAP_HEADER_SIZE_BYTES / sizeof(uint32_t)) +// Define attributes to protect from theme changes +#define ATTR_WINDOW_NO_TITLE 0x01010056 // windowNoTitle +#define ATTR_WINDOW_ACTION_BAR 0x010102cd // windowActionBar + static void printToLogFunc(void* cookie, const char* txt) { ALOGV("%s", txt); @@ -3446,6 +3450,25 @@ void ResTable::unlock() const mLock.unlock(); } +// Protected attributes are not permitted to be themed. If a theme +// does try to change a protected attribute it will be overriden +// by the app's original value. +const static uint32_t PROTECTED_ATTRS[] = { + ATTR_WINDOW_NO_TITLE, + ATTR_WINDOW_ACTION_BAR +}; + +bool ResTable::isProtectedAttr(uint32_t resID) const +{ + int length = sizeof(PROTECTED_ATTRS) / sizeof(PROTECTED_ATTRS[0]); + for(int i=0; i < length; i++) { + if (PROTECTED_ATTRS[i] == resID) { + return true; + } + } + return false; +} + ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, uint32_t* outTypeSpecFlags, bool performMapping) const { @@ -3548,6 +3571,7 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, checkOverlay = false; ip++; } + uint32_t originalResID = 0; if (package->header->resourceIDMap) { if (performMapping) { uint32_t overlayResID = 0x0; @@ -3559,6 +3583,7 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, ALOGV("resource map 0x%08x -> 0x%08x\n", resID, overlayResID); T = Res_GETTYPE(overlayResID); E = Res_GETENTRY(overlayResID); + originalResID = resID; resID = overlayResID; } else { // resource not present in overlay package, continue with the next package @@ -3748,6 +3773,80 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, if (curEntry > set->numAttrs) { set->numAttrs = curEntry; } + + // If this style was overridden by a theme then we need to compare our bag with + // the bag from the original and add any missing attributes to our bag + if (originalResID && originalResID != resID) { + const bag_entry* originalBag; + uint32_t originalTypeSpecFlags = 0; + const ssize_t NO = getBagLocked(originalResID, &originalBag, + &originalTypeSpecFlags, false); + if (NO <= 0) { + ALOGW("Failed to retrieve original bag for 0x%08x", originalResID); + } + + // Now merge in the original attributes... + bag_entry* entries = (bag_entry*)(set+1); + size_t curEntry = 0; + uint32_t pos = 0; + for (int i = 0; i < NO; i++) { + TABLE_NOISY(printf("Now at %d\n", i)); + const uint32_t newName = originalBag[i].map.name.ident; + bool isInside; + uint32_t oldName = 0; + curEntry = 0; + + while ((isInside=(curEntry < set->numAttrs)) + && (oldName=entries[curEntry].map.name.ident) < newName) { + curEntry++; + } + + if ((!isInside) || oldName != newName) { + // This is a new attribute... figure out what to do with it. + // Need to alloc more memory... + size_t prevEntry = curEntry; + curEntry = set->availAttrs; + set->availAttrs++; + const size_t newAvail = set->availAttrs; + set = (bag_set*)realloc(set, + sizeof(bag_set) + + sizeof(bag_entry)*newAvail); + if (set == NULL) { + return NO_MEMORY; + } + entries = (bag_entry*)(set+1); + TABLE_NOISY(printf("Reallocated set %p, entries=%p, avail=%d\n", + set, entries, set->availAttrs)); + if (isInside) { + // Going in the middle, need to make space. + memmove(entries+prevEntry+1, entries+prevEntry, + sizeof(bag_entry)*(set->numAttrs-prevEntry)); + } + TABLE_NOISY(printf("#%d: Inserting new attribute: 0x%08x\n", + curEntry, newName)); + + bag_entry* cur = entries+curEntry; + + cur->stringBlock = originalBag[i].stringBlock; + cur->map.name.ident = originalBag[i].map.name.ident; + cur->map.value = originalBag[i].map.value; + set->typeSpecFlags |= originalTypeSpecFlags; + set->numAttrs = set->availAttrs; + TABLE_NOISY(printf("Setting entry #%d %p: block=%d, name=0x%08x, type=%d, \ + data=0x%08x\n", + curEntry, cur, cur->stringBlock, cur->map.name.ident, + cur->map.value.dataType, cur->map.value.data)); + } else if (isProtectedAttr(newName)) { + // The attribute exists in both the original and the new theme bags, + // furthermore it is an attribute we don't wish themers to theme, so + // give our current themed bag the same value as the original + bag_entry* cur = entries+curEntry; + cur->stringBlock = originalBag[i].stringBlock; + cur->map.name.ident = originalBag[i].map.name.ident; + cur->map.value = originalBag[i].map.value; + } + }; + } } // And this is it... @@ -5728,9 +5827,9 @@ void ResTable::removeAssetsByCookie(const String8 &packageName, void* cookie) bool ResTable::isResTypeAllowed(const char* type) const { if (type == NULL) return false; - const char* allowedResources[] = { "color", "dimen", "drawable", "mipmap", "style" }; + const char* allowedResources[] = { "color", "dimen", "drawable", "mipmap", "style", "anim" }; // ALLOWED_RESOURCE_COUNT should match the number of elements in allowedResources - const uint32_t ALLOWED_RESOURCE_COUNT = 5; + const uint32_t ALLOWED_RESOURCE_COUNT = 6; for (int i = 0; i < ALLOWED_RESOURCE_COUNT; i++) { if (strstr(type, allowedResources[i]) != NULL) return true; } diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp old mode 100644 new mode 100755 index b0f4c2c7189d..aa2d59562f4a --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -220,6 +220,7 @@ void Caches::terminate() { glDeleteBuffers(1, &mMeshIndices); delete[] mRegionMesh; mMeshIndices = 0; + mCurrentIndicesBuffer = 0; mRegionMesh = NULL; fboCache.clear(); diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp old mode 100644 new mode 100755 index 00e78708dd31..d136b58c8734 --- a/libs/hwui/FontRenderer.cpp +++ b/libs/hwui/FontRenderer.cpp @@ -487,6 +487,7 @@ void FontRenderer::issueDrawCommand(Vector& cacheTextures) { Caches& caches = Caches::getInstance(); bool first = true; bool force = false; + bool drawn = false; for (uint32_t i = 0; i < cacheTextures.size(); i++) { CacheTexture* texture = cacheTextures[i]; if (texture->canDraw()) { @@ -517,6 +518,7 @@ void FontRenderer::issueDrawCommand(Vector& cacheTextures) { caches.bindPositionVertexPointer(force, &mesh[0].position[0]); caches.bindTexCoordsVertexPointer(force, &mesh[0].texture[0]); force = false; + drawn = true; glDrawElements(GL_TRIANGLES, texture->meshElementCount(), GL_UNSIGNED_SHORT, texture->indices()); @@ -524,13 +526,18 @@ void FontRenderer::issueDrawCommand(Vector& cacheTextures) { texture->resetMesh(); } } + if (drawn) { + mDrawn = true; + } } void FontRenderer::issueDrawCommand() { issueDrawCommand(mACacheTextures); issueDrawCommand(mRGBACacheTextures); - mDrawn = true; + // We should not set mDrawn to true if we have no GL draw call. + // In some case, issueDrawCommand may executed without GL draw call. + // It may miss the unbindMeshBuffer in the next issueDrawCommand and the status of VBO of FontRenderer will incorrect. } void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1, diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp old mode 100644 new mode 100755 index cfdfa520c3b4..1cac07ff06ac --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -294,21 +294,34 @@ void OpenGLRenderer::syncState() { } } -void OpenGLRenderer::startTiling(const sp& s, bool opaque) { +void OpenGLRenderer::startTiling(const sp& s, bool opaque, bool expand) { if (!mSuppressTiling) { Rect* clip = &mTilingClip; if (s->flags & Snapshot::kFlagFboTarget) { clip = &(s->layer->clipRect); } - startTiling(*clip, s->height, opaque); + startTiling(*clip, s->height, opaque, expand); } } -void OpenGLRenderer::startTiling(const Rect& clip, int windowHeight, bool opaque) { +void OpenGLRenderer::startTiling(const Rect& clip, int windowHeight, bool opaque, bool expand) { if (!mSuppressTiling) { - mCaches.startTiling(clip.left, windowHeight - clip.bottom, + if(expand) { + // Expand the startTiling region by 1 + int leftNotZero = (clip.left > 0) ? 1 : 0; + int topNotZero = (windowHeight - clip.bottom > 0) ? 1 : 0; + + mCaches.startTiling( + clip.left - leftNotZero, + windowHeight - clip.bottom - topNotZero, + clip.right - clip.left + leftNotZero + 1, + clip.bottom - clip.top + topNotZero + 1, + opaque); + } else { + mCaches.startTiling(clip.left, windowHeight - clip.bottom, clip.right - clip.left, clip.bottom - clip.top, opaque); + } } } @@ -399,6 +412,8 @@ void OpenGLRenderer::resume() { glEnable(GL_BLEND); glBlendFunc(mCaches.lastSrcMode, mCaches.lastDstMode); glBlendEquation(GL_FUNC_ADD); + + glStencilMask(0xff); } void OpenGLRenderer::resumeAfterLayer() { @@ -1014,7 +1029,8 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip, GLui glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, layer->getTexture(), 0); - startTiling(mSnapshot, true); + // Expand the startTiling region by 1 + startTiling(mSnapshot, true, true); // Clear the FBO, expand the clear region by 1 to get nice bilinear filtering mCaches.enableScissor(); @@ -2083,7 +2099,9 @@ status_t OpenGLRenderer::drawDisplayList(DisplayList* displayList, Rect& dirty, return status | deferredList.flush(*this, dirty); } - return DrawGlInfo::kStatusDone; + // Even if there is no drawing command(Ex: invisible), + // it still needs startFrame to clear buffer and start tiling. + return startFrame(); } void OpenGLRenderer::outputDisplayList(DisplayList* displayList) { @@ -2245,14 +2263,16 @@ status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int mes float right = FLT_MIN; float bottom = FLT_MIN; - const uint32_t count = meshWidth * meshHeight * 6; + uint32_t vertexCountWidth = meshWidth + 1; + uint32_t vertexCountHeight = meshHeight + 1; + uint32_t vertexCount = vertexCountWidth * vertexCountHeight; - ColorTextureVertex mesh[count]; + ColorTextureVertex mesh[vertexCount]; ColorTextureVertex* vertex = mesh; bool cleanupColors = false; if (!colors) { - uint32_t colorsCount = (meshWidth + 1) * (meshHeight + 1); + uint32_t colorsCount = vertexCountWidth * vertexCountHeight; colors = new int[colorsCount]; memset(colors, 0xff, colorsCount * sizeof(int)); cleanupColors = true; @@ -2262,39 +2282,54 @@ status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int mes Texture* texture = mCaches.assetAtlas.getEntryTexture(bitmap); const UvMapper& mapper(getMapper(texture)); - for (int32_t y = 0; y < meshHeight; y++) { - for (int32_t x = 0; x < meshWidth; x++) { - uint32_t i = (y * (meshWidth + 1) + x) * 2; - - float u1 = float(x) / meshWidth; - float u2 = float(x + 1) / meshWidth; - float v1 = float(y) / meshHeight; - float v2 = float(y + 1) / meshHeight; - - mapper.map(u1, v1, u2, v2); - - int ax = i + (meshWidth + 1) * 2; - int ay = ax + 1; - int bx = i; - int by = bx + 1; - int cx = i + 2; - int cy = cx + 1; - int dx = i + (meshWidth + 1) * 2 + 2; - int dy = dx + 1; - - ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]); - ColorTextureVertex::set(vertex++, vertices[ax], vertices[ay], u1, v2, colors[ax / 2]); - ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]); - - ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]); - ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]); - ColorTextureVertex::set(vertex++, vertices[cx], vertices[cy], u2, v1, colors[cx / 2]); - - left = fminf(left, fminf(vertices[ax], fminf(vertices[bx], vertices[cx]))); - top = fminf(top, fminf(vertices[ay], fminf(vertices[by], vertices[cy]))); - right = fmaxf(right, fmaxf(vertices[ax], fmaxf(vertices[bx], vertices[cx]))); - bottom = fmaxf(bottom, fmaxf(vertices[ay], fmaxf(vertices[by], vertices[cy]))); + // Set vertices from Mesh's up to bottom (left to right in every row). + // Update V coordinate only once for every row. + for (uint32_t i = 0; i < vertexCountHeight; i++) { + float v = float(i) / meshHeight; + mapper.mapV(v); + for (uint32_t j = 0; j < vertexCountWidth; j++) { + uint32_t x = i * vertexCountWidth * 2 + j * 2; + uint32_t y = x + 1; + float u = float(j) / meshWidth; + mapper.mapU(u); + + left = fminf(left, vertices[x]); + top = fminf(top, vertices[y]); + right = fmaxf(right, vertices[x]); + bottom = fmaxf(bottom, vertices[y]); + + ColorTextureVertex::set(vertex++, vertices[x], vertices[y], u, v, colors[x/2]); + } + } + + // Set index order from mesh's bottom to up, So that + // construct the same triangle mesh with original implementation. + // Or, May result in diffrent interpolated color. + // + // For each row, add degenerate triangle for triangle strip + // Take below for example: + // v00 --- v01 --- v02 --- v03 + // | ... | ... | ... | + // v10 --- v11 --- v12 --- v13 + // | ... | ... | ... | + // v20 --- v21 --- v22 --- v23 + // Indices are: v20, v20, v10, v21, v11, v22, v12, v23, v13, v13, + // v10, v10, v00, v11, v01, v12, v02, v13, v03, v03 + uint32_t indexCount = meshHeight * (meshWidth * 2 + 4); + uint16_t indices[indexCount]; + uint32_t currVertexRow = meshHeight; + uint32_t index = 0; + while (currVertexRow > 0) { + uint32_t upVertexRow = currVertexRow - 1; + indices[index++] = currVertexRow * vertexCountWidth; + indices[index++] = currVertexRow * vertexCountWidth; + indices[index++] = upVertexRow * vertexCountWidth; + for (uint32_t i = 1; i <= meshWidth; i++) { + indices[index++] = currVertexRow * vertexCountWidth + i; + indices[index++] = upVertexRow * vertexCountWidth + i; } + indices[index++] = upVertexRow * vertexCountWidth + meshWidth; + currVertexRow--; } if (quickReject(left, top, right, bottom)) { @@ -2337,7 +2372,7 @@ status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int mes setupDrawColorFilterUniforms(); setupDrawMesh(&mesh[0].position[0], &mesh[0].texture[0], &mesh[0].color[0]); - glDrawArrays(GL_TRIANGLES, 0, count); + glDrawElements(GL_TRIANGLE_STRIP, indexCount, GL_UNSIGNED_SHORT, indices); int slot = mCaches.currentProgram->getAttrib("colors"); if (slot >= 0) { diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h old mode 100644 new mode 100755 index 9afb7ad4f2d2..2e03a1b8330e --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -587,14 +587,14 @@ class OpenGLRenderer { * This method needs to be invoked every time getTargetFbo() is * bound again. */ - void startTiling(const sp& snapshot, bool opaque = false); + void startTiling(const sp& snapshot, bool opaque = false, bool expand = false); /** * Tells the GPU what part of the screen is about to be redrawn. * This method needs to be invoked every time getTargetFbo() is * bound again. */ - void startTiling(const Rect& clip, int windowHeight, bool opaque = false); + void startTiling(const Rect& clip, int windowHeight, bool opaque = false, bool expand = false); /** * Tells the GPU that we are done drawing the frame or that we diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp index 9b023f989165..383c01ab6271 100644 --- a/libs/hwui/Patch.cpp +++ b/libs/hwui/Patch.cpp @@ -36,6 +36,7 @@ Patch::Patch(): vertices(NULL), verticesCount(0), indexCount(0), hasEmptyQuads(f } Patch::~Patch() { + delete[] vertices; } /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp index a8c1610cd312..30b01b49cc4f 100644 --- a/libs/hwui/PatchCache.cpp +++ b/libs/hwui/PatchCache.cpp @@ -119,7 +119,21 @@ void PatchCache::remove(Vector& patchesToRemove, Res_png_9patch* p void PatchCache::removeDeferred(Res_png_9patch* patch) { Mutex::Autolock _l(mLock); - mGarbage.push(patch); + + // Assert that patch is not already garbage + size_t count = mGarbage.size(); + for (size_t i = 0; i < count; i++) { + if (patch == mGarbage[i]) { + patch = NULL; + break; + } + } + + if (patch == NULL) { + ALOGE("9-patch is already garbage, skipping"); + } else { + mGarbage.push(patch); + } } void PatchCache::clearGarbage() { @@ -143,8 +157,8 @@ void PatchCache::clearGarbage() { for (size_t i = 0; i < patchesToRemove.size(); i++) { const patch_pair_t& pair = patchesToRemove[i]; - // Add a new free block to the list - const Patch* patch = pair.getSecond(); + // Release the patch and mark the space in the free list + Patch* patch = pair.getSecond(); BufferBlock* block = new BufferBlock(patch->offset, patch->getSize()); block->next = mFreeBlocks; mFreeBlocks = block; @@ -217,6 +231,7 @@ void PatchCache::setupMesh(Patch* newMesh, TextureVertex* vertices) { } else { mFreeBlocks = block->next; } + delete block; } else { // Resize the block now that it's occupied block->offset += size; diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp old mode 100644 new mode 100755 index 3970913814c6..9e8b2769cfc0 --- a/libs/hwui/PathTessellator.cpp +++ b/libs/hwui/PathTessellator.cpp @@ -54,6 +54,7 @@ namespace android { namespace uirenderer { #define THRESHOLD 0.5f +#define THRESHOLD_MIN 0.0005f #define ROUND_CAP_THRESH 0.25f #define PI 3.1415926535897932f @@ -1004,7 +1005,13 @@ void PathTessellator::recursiveQuadraticBezierVertices( float dy = by - ay; float d = (cx - bx) * dy - (cy - by) * dx; - if (d * d < THRESHOLD * THRESHOLD * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) { + if (d * d < THRESHOLD * THRESHOLD * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX) + // In some cases like the distance between the calculating point is very small, + // the recursion may not terminate. If the calculated dx and dy are 0, d is 0 too, + // in which case the termination condition will never be met, thus infinite recursion + // will cause stack-overflow. To avoid that, introduce an additional threshold to + // terminate the recursion when d is very small (possibly 0). + || fabs(d) < THRESHOLD_MIN) { // below thresh, draw line by adding endpoint pushToVector(outputVertices, bx, by); } else { diff --git a/media/java/android/media/.AudioService.java.swp b/media/java/android/media/.AudioService.java.swp deleted file mode 100644 index 69d2779faa10..000000000000 Binary files a/media/java/android/media/.AudioService.java.swp and /dev/null differ diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 40d033ecfa39..c8b37b55c7f9 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -44,6 +44,8 @@ import java.util.HashMap; +import com.android.internal.util.aokp.QuietHoursHelper; + /** * AudioManager provides access to volume and ringer mode control. *

@@ -1800,6 +1802,10 @@ public void playSoundEffect(int effectType) { return; } + if (QuietHoursHelper.inQuietHours(mContext, Settings.AOKP.QUIET_HOURS_SYSTEM)) { + return; + } + if (!querySoundEffectsEnabled()) { return; } diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 2fc0c71d129d..1898b83864ef 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -78,6 +78,7 @@ import android.view.Surface; import android.view.VolumePanel; import android.view.WindowManager; +import android.view.OrientationEventListener; import com.android.internal.telephony.ITelephony; import com.android.internal.util.XmlUtils; @@ -290,6 +291,8 @@ public class AudioService extends IAudioService.Stub { "STREAM_TTS" }; + private boolean mLinkNotificationWithVolume; + private final AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() { public void onError(int error) { switch (error) { @@ -464,6 +467,9 @@ public void onError(int error) { private final Object mA2dpAvrcpLock = new Object(); // If absolute volume is supported in AVRCP device private boolean mAvrcpAbsVolSupported = false; + private boolean mVolumeKeysControlRingStream; + + private AudioOrientationEventListener mOrientationListener; /////////////////////////////////////////////////////////////////////////// // Construction @@ -529,9 +535,9 @@ public AudioService(Context context) { // array initialized by updateStreamVolumeAlias() updateStreamVolumeAlias(false /*updateVolumes*/); readPersistedSettings(); + // must update link ring-notifications streams once the user preferences were read + updateLinkNotificationStreamVolumeAlias(); mSettingsObserver = new SettingsObserver(); - //Update volumes steps before creatingStreamStates! - initVolumeSteps(); createStreamStates(); readAndSetLowRamDevice(); @@ -567,6 +573,10 @@ public AudioService(Context context) { mDeviceRotation = ((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE)) .getDefaultDisplay().getRotation(); Log.v(TAG, "monitoring device rotation, initial=" + mDeviceRotation); + + mOrientationListener = new AudioOrientationEventListener(mContext); + mOrientationListener.enable(); + // initialize rotation in AudioSystem setRotationForAudioSystem(); } @@ -717,6 +727,7 @@ private void updateStreamVolumeAlias(boolean updateVolumes) { mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_DTMF); } mStreamVolumeAlias[AudioSystem.STREAM_DTMF] = dtmfStreamAlias; + updateLinkNotificationStreamVolumeAlias(); if (updateVolumes) { mStreamStates[AudioSystem.STREAM_DTMF].setAllIndexes(mStreamStates[dtmfStreamAlias]); // apply stream mute states according to new value of mRingerModeAffectedStreams @@ -730,6 +741,14 @@ private void updateStreamVolumeAlias(boolean updateVolumes) { } } + private void updateLinkNotificationStreamVolumeAlias() { + if (mLinkNotificationWithVolume) { + mStreamVolumeAlias[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_RING; + } else { + mStreamVolumeAlias[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_NOTIFICATION; + } + } + private void readDockAudioSettings(ContentResolver cr) { mDockAudioMediaEnabled = Settings.Global.getInt( @@ -790,8 +809,14 @@ private void readPersistedSettings() { updateRingerModeAffectedStreams(); readDockAudioSettings(cr); updateManualSafeMediaVolume(); + + mVolumeKeysControlRingStream = Settings.AOKP.getIntForUser(cr, + Settings.AOKP.VOLUME_KEYS_CONTROL_RING_STREAM, 1, UserHandle.USER_CURRENT) == 1; } + mLinkNotificationWithVolume = Settings.AOKP.getIntForUser(cr, + Settings.AOKP.VOLUME_LINK_NOTIFICATION, 1, UserHandle.USER_CURRENT) == 1; + mMuteAffectedStreams = System.getIntForUser(cr, System.MUTE_STREAMS_AFFECTED, ((1 << AudioSystem.STREAM_MUSIC)| @@ -816,15 +841,31 @@ private void readPersistedSettings() { // Broadcast vibrate settings broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER); broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION); - - // Restore the default media button receiver from the system settings - mMediaFocusControl.restoreMediaButtonReceiver(); } private int rescaleIndex(int index, int srcStream, int dstStream) { return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex(); } + private class AudioOrientationEventListener + extends OrientationEventListener { + public AudioOrientationEventListener(Context context) { + super(context); + } + + @Override + public void onOrientationChanged(int orientation) { + //Even though we're responding to phone orientation events, + //use display rotation so audio stays in sync with video/dialogs + int newRotation = ((WindowManager) mContext.getSystemService( + Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation(); + if (newRotation != mDeviceRotation) { + mDeviceRotation = newRotation; + setRotationForAudioSystem(); + } + } + } + /////////////////////////////////////////////////////////////////////////// // IPC methods /////////////////////////////////////////////////////////////////////////// @@ -2772,9 +2813,15 @@ private int getActiveStreamType(int suggestedStreamType) { Log.v(TAG, "getActiveStreamType: Forcing STREAM_REMOTE_MUSIC"); return STREAM_REMOTE_MUSIC; } else { - if (DEBUG_VOL) - Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING b/c default"); - return AudioSystem.STREAM_RING; + if (mVolumeKeysControlRingStream) { + if (DEBUG_VOL) + Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING b/c default"); + return AudioSystem.STREAM_RING; + } else { + if (DEBUG_VOL) + Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC b/c default setting"); + return AudioSystem.STREAM_MUSIC; + } } } else if (isAfMusicActiveRecently(0)) { if (DEBUG_VOL) @@ -3837,25 +3884,67 @@ private class SettingsObserver extends ContentObserver { Settings.Global.DOCK_AUDIO_MEDIA_ENABLED), false, this); mContentResolver.registerContentObserver(Settings.AOKP.getUriFor( Settings.AOKP.MANUAL_SAFE_MEDIA_VOLUME), false, this); + mContentResolver.registerContentObserver(Settings.AOKP.getUriFor( + Settings.AOKP.VOLUME_LINK_NOTIFICATION), false, this); + mContentResolver.registerContentObserver(Settings.AOKP.getUriFor( + Settings.AOKP.VOLUME_KEYS_CONTROL_RING_STREAM), false, this); } @Override - public void onChange(boolean selfChange) { + public void onChange(boolean selfChange, Uri uri) { super.onChange(selfChange); + if (uri == null) { + return; + } // FIXME This synchronized is not necessary if mSettingsLock only protects mRingerMode. // However there appear to be some missing locks around mRingerModeMutedStreams // and mRingerModeAffectedStreams, so will leave this synchronized for now. // mRingerModeMutedStreams and mMuteAffectedStreams are safe (only accessed once). synchronized (mSettingsLock) { - if (updateRingerModeAffectedStreams()) { - /* - * Ensure all stream types that should be affected by ringer mode - * are in the proper state. - */ - setRingerModeInt(getRingerMode(), false); + if (uri.equals(Settings.System.getUriFor(Settings.System.MODE_RINGER_STREAMS_AFFECTED))) { + int ringerModeAffectedStreams = Settings.System.getIntForUser(mContentResolver, + Settings.System.MODE_RINGER_STREAMS_AFFECTED, + ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)| + (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)), + UserHandle.USER_CURRENT); + if (mVoiceCapable) { + ringerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC); + } else { + ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC); + } + synchronized (mCameraSoundForced) { + if (mCameraSoundForced) { + ringerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_SYSTEM_ENFORCED); + } else { + ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_SYSTEM_ENFORCED); + } + } + if (ringerModeAffectedStreams != mRingerModeAffectedStreams) { + /* + * Ensure all stream types that should be affected by ringer mode + * are in the proper state. + */ + mRingerModeAffectedStreams = ringerModeAffectedStreams; + setRingerModeInt(getRingerMode(), false); + updateManualSafeMediaVolume(); + } + + } else if (uri.equals(Settings.Global.getUriFor(Settings.Global.DOCK_AUDIO_MEDIA_ENABLED))) { + readDockAudioSettings(mContentResolver); + + } else if (uri.equals(Settings.AOKP.getUriFor(Settings.AOKP.VOLUME_LINK_NOTIFICATION))) { + mLinkNotificationWithVolume = Settings.AOKP.getInt(mContentResolver, + Settings.AOKP.VOLUME_LINK_NOTIFICATION, 1) == 1; + if (mLinkNotificationWithVolume) { + mStreamVolumeAlias[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_RING; + } else { + mStreamVolumeAlias[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_NOTIFICATION; + } + + } else if (uri.equals(Settings.AOKP.getUriFor(Settings.AOKP.VOLUME_KEYS_CONTROL_RING_STREAM))) { + mVolumeKeysControlRingStream = Settings.AOKP.getIntForUser(mContentResolver, + Settings.AOKP.VOLUME_KEYS_CONTROL_RING_STREAM, 1, UserHandle.USER_CURRENT) == 1; } - readDockAudioSettings(mContentResolver); - updateManualSafeMediaVolume(); } } } @@ -3878,7 +3967,9 @@ private void makeA2dpDeviceAvailable(String address) { } private void onSendBecomingNoisyIntent() { - sendBroadcastToAll(new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY)); + Intent intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY); + intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + sendBroadcastToAll(intent); } // must be called synchronized on mConnectedDevices @@ -4315,8 +4406,16 @@ public void onReceive(Context context, Intent intent) { SAFE_VOLUME_CONFIGURE_TIMEOUT_MS); adjustCurrentStreamVolume(); } else if (action.equals(Intent.ACTION_SCREEN_ON)) { + if (mMonitorRotation) { + mOrientationListener.onOrientationChanged(0); //argument is ignored anyway + mOrientationListener.enable(); + } AudioSystem.setParameters("screen_state=on"); } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { + if (mMonitorRotation) { + //reduce wakeups (save current) by only listening when display is on + mOrientationListener.disable(); + } AudioSystem.setParameters("screen_state=off"); } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) { handleConfigurationChanged(context); @@ -4512,14 +4611,6 @@ private void handleConfigurationChanged(Context context) { setOrientationForAudioSystem(); } } - if (mMonitorRotation) { - int newRotation = ((WindowManager) context.getSystemService( - Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation(); - if (newRotation != mDeviceRotation) { - mDeviceRotation = newRotation; - setRotationForAudioSystem(); - } - } sendMsg(mAudioHandler, MSG_CONFIGURE_SAFE_MEDIA_VOLUME, SENDMSG_REPLACE, diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java index b8b98444f945..e653358c816a 100644 --- a/media/java/android/media/MediaFile.java +++ b/media/java/android/media/MediaFile.java @@ -113,9 +113,10 @@ public class MediaFile { public static final int FILE_TYPE_PLS = 42; public static final int FILE_TYPE_WPL = 43; public static final int FILE_TYPE_HTTPLIVE = 44; + public static final int FILE_TYPE_DASH = 45; private static final int FIRST_PLAYLIST_FILE_TYPE = FILE_TYPE_M3U; - private static final int LAST_PLAYLIST_FILE_TYPE = FILE_TYPE_HTTPLIVE; + private static final int LAST_PLAYLIST_FILE_TYPE = FILE_TYPE_DASH; // Drm file types public static final int FILE_TYPE_FL = 51; @@ -228,6 +229,7 @@ private static boolean isWMVEnabled() { addFileType("MPG", FILE_TYPE_MP4, "video/mpeg", MtpConstants.FORMAT_MPEG); addFileType("MP4", FILE_TYPE_MP4, "video/mp4", MtpConstants.FORMAT_MPEG); addFileType("M4V", FILE_TYPE_M4V, "video/mp4", MtpConstants.FORMAT_MPEG); + addFileType("MOV", FILE_TYPE_MP4, "video/quicktime", MtpConstants.FORMAT_MPEG); addFileType("3GP", FILE_TYPE_3GPP, "video/3gpp", MtpConstants.FORMAT_3GP_CONTAINER); addFileType("3GPP", FILE_TYPE_3GPP, "video/3gpp", MtpConstants.FORMAT_3GP_CONTAINER); addFileType("3G2", FILE_TYPE_3GPP2, "video/3gpp2", MtpConstants.FORMAT_3GP_CONTAINER); @@ -263,6 +265,7 @@ private static boolean isWMVEnabled() { addFileType("M3U8", FILE_TYPE_HTTPLIVE, "application/vnd.apple.mpegurl"); addFileType("M3U8", FILE_TYPE_HTTPLIVE, "audio/mpegurl"); addFileType("M3U8", FILE_TYPE_HTTPLIVE, "audio/x-mpegurl"); + addFileType("MPD", FILE_TYPE_DASH, "application/dash+xml"); addFileType("FL", FILE_TYPE_FL, "application/x-android-drm-fl"); diff --git a/media/java/android/media/MediaFocusControl.java b/media/java/android/media/MediaFocusControl.java index c9288d5cf12d..dece6c0745cb 100644 --- a/media/java/android/media/MediaFocusControl.java +++ b/media/java/android/media/MediaFocusControl.java @@ -318,7 +318,6 @@ private boolean isComponentInStringArray(ComponentName comp, String[] enabledArr //========================================================================================== // event handler messages - private static final int MSG_PERSIST_MEDIABUTTONRECEIVER = 0; private static final int MSG_RCDISPLAY_CLEAR = 1; private static final int MSG_RCDISPLAY_UPDATE = 2; private static final int MSG_REEVALUATE_REMOTE = 3; @@ -359,9 +358,6 @@ private class MediaEventHandler extends Handler { @Override public void handleMessage(Message msg) { switch(msg.what) { - case MSG_PERSIST_MEDIABUTTONRECEIVER: - onHandlePersistMediaButtonReceiver( (ComponentName) msg.obj ); - break; case MSG_RCDISPLAY_CLEAR: onRcDisplayClear(); @@ -1433,47 +1429,7 @@ private void cleanupMediaButtonReceiverForPackage(String packageName, boolean re } } } - if (mRCStack.empty()) { - // no saved media button receiver - mEventHandler.sendMessage( - mEventHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, - null)); - } else if (oldTop != mRCStack.peek()) { - // the top of the stack has changed, save it in the system settings - // by posting a message to persist it; only do this however if it has - // a concrete component name (is not a transient registration) - RemoteControlStackEntry rcse = mRCStack.peek(); - if (rcse.mReceiverComponent != null) { - mEventHandler.sendMessage( - mEventHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, - rcse.mReceiverComponent)); - } - } - } - } - } - - /** - * Helper function: - * Restore remote control receiver from the system settings. - */ - protected void restoreMediaButtonReceiver() { - String receiverName = Settings.System.getStringForUser(mContentResolver, - Settings.System.MEDIA_BUTTON_RECEIVER, UserHandle.USER_CURRENT); - if ((null != receiverName) && !receiverName.isEmpty()) { - ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName); - if (eventReceiver == null) { - // an invalid name was persisted - return; } - // construct a PendingIntent targeted to the restored component name - // for the media button and register it - Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); - // the associated intent will be handled by the component being registered - mediaButtonIntent.setComponent(eventReceiver); - PendingIntent pi = PendingIntent.getBroadcast(mContext, - 0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/); - registerMediaButtonIntent(pi, eventReceiver, null); } } @@ -1515,12 +1471,6 @@ private boolean pushMediaButtonReceiver_syncAfRcs(PendingIntent mediaIntent, } mRCStack.push(rcse); // rcse is never null - // post message to persist the default media button receiver - if (target != null) { - mEventHandler.sendMessage( mEventHandler.obtainMessage( - MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) ); - } - // RC stack was modified return true; } @@ -1559,12 +1509,6 @@ private boolean isCurrentRcController(PendingIntent pi) { return false; } - private void onHandlePersistMediaButtonReceiver(ComponentName receiver) { - Settings.System.putStringForUser(mContentResolver, - Settings.System.MEDIA_BUTTON_RECEIVER, - receiver == null ? "" : receiver.flattenToString(), - UserHandle.USER_CURRENT); - } //========================================================================================== // Remote control display / client diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index f81fa408e852..d20b24293310 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -383,7 +383,7 @@ public void setProfile(CamcorderProfile profile) { setVideoEncodingBitRate(profile.videoBitRate); setVideoEncoder(profile.videoCodec); if (profile.quality >= CamcorderProfile.QUALITY_TIME_LAPSE_LOW && - profile.quality <= CamcorderProfile.QUALITY_TIME_LAPSE_QVGA) { + profile.quality <= CamcorderProfile.QUALITY_TIME_LAPSE_WQVGA) { // Nothing needs to be done. Call to setCaptureRate() enables // time lapse video recording. } else if (profile.audioCodec >= 0) { diff --git a/media/jni/android_media_ExtMediaPlayer.cpp b/media/jni/android_media_ExtMediaPlayer.cpp index 5c35f4db3437..93587a408e53 100644 --- a/media/jni/android_media_ExtMediaPlayer.cpp +++ b/media/jni/android_media_ExtMediaPlayer.cpp @@ -104,7 +104,12 @@ JNIExtMediaPlayerListener::~JNIExtMediaPlayerListener() void JNIExtMediaPlayerListener::notify(int msg, int ext1, int ext2, const Parcel *obj) { JNIEnv *env = AndroidRuntime::getJNIEnv(); - if (env && obj && obj->dataSize() > 0) + + if (!env) { + return; + } + + if (obj && obj->dataSize() > 0) { if (mParcel != NULL) { diff --git a/obex/javax/obex/ClientSession.java b/obex/javax/obex/ClientSession.java index b3f14484363c..6006bf659f45 100644 --- a/obex/javax/obex/ClientSession.java +++ b/obex/javax/obex/ClientSession.java @@ -457,8 +457,9 @@ public boolean sendRequest(int opCode, byte[] head, HeaderSet header, maxPacketSize = (mInput.read() << 8) + mInput.read(); //check with local max size - if (setMTU) { - maxPacketSize = ObexHelper.A2DP_SCO_OBEX_MAX_CLIENT_PACKET_SIZE; + if (setMTU && maxPacketSize > ObexHelper.A2DP_OBEX_MAX_CLIENT_PACKET_SIZE) { + maxPacketSize = ObexHelper.A2DP_OBEX_MAX_CLIENT_PACKET_SIZE; + setMTU = false; } else if (maxPacketSize > ObexHelper.MAX_CLIENT_PACKET_SIZE) { maxPacketSize = ObexHelper.MAX_CLIENT_PACKET_SIZE; } diff --git a/obex/javax/obex/ObexHelper.java b/obex/javax/obex/ObexHelper.java index 8ebd802a3364..de5c307a82bb 100644 --- a/obex/javax/obex/ObexHelper.java +++ b/obex/javax/obex/ObexHelper.java @@ -76,8 +76,8 @@ private ObexHelper() { * Temporary workaround to be able to push files to Windows 7. * TODO: Should be removed as soon as Microsoft updates their driver. */ - public static final int MAX_CLIENT_PACKET_SIZE = 0xFC00; - public static final int A2DP_SCO_OBEX_MAX_CLIENT_PACKET_SIZE = 0x2000; + public static final int MAX_CLIENT_PACKET_SIZE = 0xEC88; + public static final int A2DP_OBEX_MAX_CLIENT_PACKET_SIZE = 0x2000; public static final int OBEX_OPCODE_CONNECT = 0x80; diff --git a/packages/DocumentsUI/.gitignore b/packages/DocumentsUI/.gitignore new file mode 100644 index 000000000000..b15b12b9c002 --- /dev/null +++ b/packages/DocumentsUI/.gitignore @@ -0,0 +1,4 @@ +*.iml +*.idea +gen/ + diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml index 44d76435e6fd..e9ccd11e048d 100644 --- a/packages/DocumentsUI/AndroidManifest.xml +++ b/packages/DocumentsUI/AndroidManifest.xml @@ -4,6 +4,8 @@ + + - + + + + + + + - + + Αρχεία + Αρχεία που έχουν διαγραφεί + Διαγραφή αρχείων; + Αντιγραφή + Αποκοπή + Επικόλληση + Αντιγραφή... + Διαγραφή... + ΟΚ + Ακύρωση + diff --git a/packages/DocumentsUI/res/values-es/strings_custom.xml b/packages/DocumentsUI/res/values-es/strings_custom.xml index b146b48c42e2..b54a4ed7fb39 100644 --- a/packages/DocumentsUI/res/values-es/strings_custom.xml +++ b/packages/DocumentsUI/res/values-es/strings_custom.xml @@ -1,5 +1,5 @@ - + + + diff --git a/packages/DocumentsUI/res/values-fi/strings_custom.xml b/packages/DocumentsUI/res/values-fi/strings_custom.xml index ff673f3af704..d40f73d10961 100644 --- a/packages/DocumentsUI/res/values-fi/strings_custom.xml +++ b/packages/DocumentsUI/res/values-fi/strings_custom.xml @@ -1,5 +1,5 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/Keyguard/res/values-af/strings_custom.xml b/packages/Keyguard/res/values-af/strings_custom.xml index 1e75e1988c5a..1cda7dc25fa2 100644 --- a/packages/Keyguard/res/values-af/strings_custom.xml +++ b/packages/Keyguard/res/values-af/strings_custom.xml @@ -1,5 +1,5 @@ - + - + diff --git a/packages/Keyguard/res/values-ar/strings_custom.xml b/packages/Keyguard/res/values-ar/strings_custom.xml index 42452c81955b..37db7d8b9dfd 100644 --- a/packages/Keyguard/res/values-ar/strings_custom.xml +++ b/packages/Keyguard/res/values-ar/strings_custom.xml @@ -1,5 +1,5 @@ - + - + شاشة القفل diff --git a/packages/Keyguard/res/values-ca/strings_custom.xml b/packages/Keyguard/res/values-ca/strings_custom.xml index ab9501aa0e58..22cca4a19926 100644 --- a/packages/Keyguard/res/values-ca/strings_custom.xml +++ b/packages/Keyguard/res/values-ca/strings_custom.xml @@ -1,5 +1,5 @@ - + - + Pantalla de bloqueig Recarrega els botons aleatoris diff --git a/packages/Keyguard/res/values-cs/strings_custom.xml b/packages/Keyguard/res/values-cs/strings_custom.xml index daad68b0c17e..97e388239038 100644 --- a/packages/Keyguard/res/values-cs/strings_custom.xml +++ b/packages/Keyguard/res/values-cs/strings_custom.xml @@ -1,5 +1,5 @@ - + - + Zamčená obrazovka Načíst tlačítka náhodně + Nakreslit gesto pro odemčení + Správně! + Zkusit znovu + Odemykací gesto. + Prostor gesta. + Zapomenout gesto + Špatné gesto + Nakreslit gesto + Složité gesto bylo %dx špatně nakresleno. +\n\nVyzkoušet znovu za %d sekund. diff --git a/packages/Keyguard/res/values-da/strings_custom.xml b/packages/Keyguard/res/values-da/strings_custom.xml index ff3939c08495..36539a8326bc 100644 --- a/packages/Keyguard/res/values-da/strings_custom.xml +++ b/packages/Keyguard/res/values-da/strings_custom.xml @@ -1,5 +1,5 @@ - + - + Låseskærm Genindlæs tilfældelige knapper diff --git a/packages/Keyguard/res/values-de/strings_custom.xml b/packages/Keyguard/res/values-de/strings_custom.xml index 5833ac32c806..8b554a26dcfd 100644 --- a/packages/Keyguard/res/values-de/strings_custom.xml +++ b/packages/Keyguard/res/values-de/strings_custom.xml @@ -1,5 +1,5 @@ - + - + Sperrbildschirm Aktualisiere zufällige Knöpfe + Zum Entsperren Geste zeichnen + Korrekt! + Nochmal versuchen + Entsperrungsgeste. + Gestenbereich. + Geste vergessen + Falsche Geste + Geste zeichnen + + Sie haben Ihr Entsperrungsmuster %d-mal falsch gezeichnet. "\n\n"Bitte versuchen Sie es in %d Sekunden noch einmal. + diff --git a/packages/Keyguard/res/values-el/strings_custom.xml b/packages/Keyguard/res/values-el/strings_custom.xml index 1e75e1988c5a..e1a2cd2c2851 100644 --- a/packages/Keyguard/res/values-el/strings_custom.xml +++ b/packages/Keyguard/res/values-el/strings_custom.xml @@ -1,5 +1,5 @@ - + - + + Οθόνη κλειδώματος + diff --git a/packages/Keyguard/res/values-es/strings_custom.xml b/packages/Keyguard/res/values-es/strings_custom.xml index 1a03bac8fa9a..e6cdb6da3eb4 100644 --- a/packages/Keyguard/res/values-es/strings_custom.xml +++ b/packages/Keyguard/res/values-es/strings_custom.xml @@ -1,5 +1,5 @@ - + - + Pantalla de bloqueo Recargar botones aleatoriamente + Dibujar gesto para desbloqueo + ¡Correcto! + Inténtelo de nuevo + Gesto de desbloqueo. + Área de gesto. + Olvidó el gesto + Gesto erróneo + Dibuje su gesto + + Ha dibujado incorrectamente su gesto de desbloqueo %d veces. +\n\nInténtelo de nuevo dentro de %d segundos. + diff --git a/packages/Keyguard/res/values-fa/strings_custom.xml b/packages/Keyguard/res/values-fa/strings_custom.xml new file mode 100644 index 000000000000..1cda7dc25fa2 --- /dev/null +++ b/packages/Keyguard/res/values-fa/strings_custom.xml @@ -0,0 +1,18 @@ + + + + diff --git a/packages/Keyguard/res/values-fi/strings_custom.xml b/packages/Keyguard/res/values-fi/strings_custom.xml index fb29f911f8f6..16b3c40f87ed 100644 --- a/packages/Keyguard/res/values-fi/strings_custom.xml +++ b/packages/Keyguard/res/values-fi/strings_custom.xml @@ -1,5 +1,5 @@ - + - + Lukitusnäyttö Lataa satunnaiset näppäimet uudelleen diff --git a/packages/Keyguard/res/values-fr/strings_custom.xml b/packages/Keyguard/res/values-fr/strings_custom.xml index b86845af51bc..f9a76686c382 100644 --- a/packages/Keyguard/res/values-fr/strings_custom.xml +++ b/packages/Keyguard/res/values-fr/strings_custom.xml @@ -1,5 +1,5 @@ - + - + Écran de verrouillage Recharger les boutons au hasard + Dessiner le geste pour déverrouiller + Correct ! + Réessayer + Geste de déverrouillage. + Zone de geste. + Geste oublié + Mauvais geste + Dessiner votre geste + + Vous avez dessiné un geste de déverrouillage incorrect à %d reprises. + \n\nVeuillez réessayer dans %d secondes. + diff --git a/packages/Keyguard/res/values-hu/strings_custom.xml b/packages/Keyguard/res/values-hu/strings_custom.xml index 1e75e1988c5a..e3905d222401 100644 --- a/packages/Keyguard/res/values-hu/strings_custom.xml +++ b/packages/Keyguard/res/values-hu/strings_custom.xml @@ -1,5 +1,5 @@ - + - + + Zárképernyő + Véletlen gombok frissítése + Feloldáshoz rajzoljon gesztuszt + Helyes! + Próbálja újra + Feloldás gesztussal. + Gesztus területe. + Elfelejtett gesztus + Hibás gesztus + Gesztus megrajzolása + + Feloldó gesztusát helytelenül rajzolta meg %d alkalommal. + \n\nPróbálkozzon újra %d másodperc múlva. + + diff --git a/packages/Keyguard/res/values-it/strings_custom.xml b/packages/Keyguard/res/values-it/strings_custom.xml index 0f6c45d6f64f..2d6ab633b787 100644 --- a/packages/Keyguard/res/values-it/strings_custom.xml +++ b/packages/Keyguard/res/values-it/strings_custom.xml @@ -1,5 +1,5 @@ - + - + Schermata di sblocco Ricarica pulsanti casuali + Disegnare gesture per sbloccare + Corretto! + Riprova + Sblocco con un gesto. + Zona della gesture. + Gesture dimenticata + Gesture errata + Disegna la tua gesture + + Hai disegnato la gesture di sblocco in modo errato %d volte. + \n\nProva ancora fra%d secondi. + diff --git a/packages/Keyguard/res/values-iw/strings_custom.xml b/packages/Keyguard/res/values-iw/strings_custom.xml index 1e75e1988c5a..1cda7dc25fa2 100644 --- a/packages/Keyguard/res/values-iw/strings_custom.xml +++ b/packages/Keyguard/res/values-iw/strings_custom.xml @@ -1,5 +1,5 @@ - + - + diff --git a/packages/Keyguard/res/values-ja/strings_custom.xml b/packages/Keyguard/res/values-ja/strings_custom.xml index ba11a04452be..52bea909ed3e 100644 --- a/packages/Keyguard/res/values-ja/strings_custom.xml +++ b/packages/Keyguard/res/values-ja/strings_custom.xml @@ -1,5 +1,5 @@ - + - + ロック画面 ランダムボタンをリロード diff --git a/packages/Keyguard/res/values-ko/strings_custom.xml b/packages/Keyguard/res/values-ko/strings_custom.xml index 0a33ddf87c40..6ab836fb0bb3 100644 --- a/packages/Keyguard/res/values-ko/strings_custom.xml +++ b/packages/Keyguard/res/values-ko/strings_custom.xml @@ -1,5 +1,5 @@ - + - + 잠금화면 임의의 버튼을 다시불러오기 + 잠금을 해제 하려면 제스처 그리기 + 수정! + 다시 시도해 보세요. + 제스처 잠금해제 + 제스쳐 영역 + 제스쳐 분실 + 잘못된 제스쳐 + 당신의 제스처 그리기 + 잠금해제 패턴을 %d회 잘못 그렸습니다. "\n\n"%d초 후에 다시 시도하세요. diff --git a/packages/Keyguard/res/values-land/dimens.xml b/packages/Keyguard/res/values-land/dimens.xml index bf30332f1ef5..02574f1c6b0a 100644 --- a/packages/Keyguard/res/values-land/dimens.xml +++ b/packages/Keyguard/res/values-land/dimens.xml @@ -19,6 +19,9 @@ --> + + -40dp + 30dp diff --git a/packages/Keyguard/res/values-nl/strings_custom.xml b/packages/Keyguard/res/values-nl/strings_custom.xml index 556b265b0d97..f68c01cba0f5 100644 --- a/packages/Keyguard/res/values-nl/strings_custom.xml +++ b/packages/Keyguard/res/values-nl/strings_custom.xml @@ -1,5 +1,5 @@ - + - + Vergrendelscherm diff --git a/packages/Keyguard/res/values-no/strings_custom.xml b/packages/Keyguard/res/values-no/strings_custom.xml index 1e75e1988c5a..1cda7dc25fa2 100644 --- a/packages/Keyguard/res/values-no/strings_custom.xml +++ b/packages/Keyguard/res/values-no/strings_custom.xml @@ -1,5 +1,5 @@ - + - + diff --git a/packages/Keyguard/res/values-pl/strings_custom.xml b/packages/Keyguard/res/values-pl/strings_custom.xml index 74fc52182d4f..5d997fbc3dec 100644 --- a/packages/Keyguard/res/values-pl/strings_custom.xml +++ b/packages/Keyguard/res/values-pl/strings_custom.xml @@ -1,5 +1,5 @@ - + - + Ekran blokady Przeładuj losowe przyciski + Narysuj gest, by odblokować + Prawidłowo! + Spróbuj ponownie + Odblokowanie gestem. + Pole do wprowadzenia gestu. + Zapomniałem gest + Nieprawidłowy gest + Narysuj swój gest + + Błędnie narysowano gest %d razy. + \n\nSpróbuj ponownie za %d sekund. + diff --git a/packages/Keyguard/res/values-pt-rBR/strings_custom.xml b/packages/Keyguard/res/values-pt-rBR/strings_custom.xml index ee7c9562a7da..f955c7345840 100644 --- a/packages/Keyguard/res/values-pt-rBR/strings_custom.xml +++ b/packages/Keyguard/res/values-pt-rBR/strings_custom.xml @@ -1,5 +1,5 @@ - + - + Tela de bloqueio diff --git a/packages/Keyguard/res/values-pt/strings_custom.xml b/packages/Keyguard/res/values-pt/strings_custom.xml index 1e75e1988c5a..1cda7dc25fa2 100644 --- a/packages/Keyguard/res/values-pt/strings_custom.xml +++ b/packages/Keyguard/res/values-pt/strings_custom.xml @@ -1,5 +1,5 @@ - + - + diff --git a/packages/Keyguard/res/values-ro/strings_custom.xml b/packages/Keyguard/res/values-ro/strings_custom.xml index 8f8e9085844e..1badd5028be3 100644 --- a/packages/Keyguard/res/values-ro/strings_custom.xml +++ b/packages/Keyguard/res/values-ro/strings_custom.xml @@ -1,5 +1,5 @@ - + - + Ecran de blocare diff --git a/packages/Keyguard/res/values-ru/strings_custom.xml b/packages/Keyguard/res/values-ru/strings_custom.xml index 1c6cb9532a35..1d1a33f11860 100644 --- a/packages/Keyguard/res/values-ru/strings_custom.xml +++ b/packages/Keyguard/res/values-ru/strings_custom.xml @@ -1,5 +1,5 @@ - + - + Экран блокировки Сброс случайных кнопок + Нарисуйте жест для разблокировки + Правильно! + Попробуйте снова + Разблокировка жестом. + Место для жеста. + Забыли жест + Неверный жест + Нарисуйте Ваш жест + + Вы нарисовали неверный жест разблокировки %d раз. + \n\nПопробуйте снова через %d с. + diff --git a/packages/Keyguard/res/values-si/strings_custom.xml b/packages/Keyguard/res/values-si/strings_custom.xml index d9d88bc8fbfd..963d03263866 100644 --- a/packages/Keyguard/res/values-si/strings_custom.xml +++ b/packages/Keyguard/res/values-si/strings_custom.xml @@ -1,5 +1,5 @@ - + - + අගුළු තිරය අහඹු බොත්තම් යලිපූරණය කරන්න + අගුළු ඇරීමට අභිනය අඳින්න + නිවැරදියි! + යලි උත්සාහ කරන්න + අභිනයෙන් අගුළු ඇරීම. + අභිනයේ පෙදෙස. + අභිනය අමතකය + වැරදි අභිනයකි + ඔබගේ අභිනය අඳින්න + + ඔබ ඔබගේ අගුළු ඇරීමේ අභිනය %d වරක් වැරදි ලෙස ඇඳ ඇත. + \n\nතත්පර %d කින් යළි උත්සාහ කරන්න. + diff --git a/packages/Keyguard/res/values-sr/strings_custom.xml b/packages/Keyguard/res/values-sr/strings_custom.xml index 1e75e1988c5a..1cda7dc25fa2 100644 --- a/packages/Keyguard/res/values-sr/strings_custom.xml +++ b/packages/Keyguard/res/values-sr/strings_custom.xml @@ -1,5 +1,5 @@ - + - + diff --git a/packages/Keyguard/res/values-sv/strings_custom.xml b/packages/Keyguard/res/values-sv/strings_custom.xml index 89fff371fa8a..f78a58c9a8cf 100644 --- a/packages/Keyguard/res/values-sv/strings_custom.xml +++ b/packages/Keyguard/res/values-sv/strings_custom.xml @@ -1,5 +1,5 @@ - + - + Låsskärm Ladda om blandade knappar + Rita mönster för att låsa upp + Korrekt! + Försök igen + Mönsterupplåsning + Mönsterområde. + Glöm gest + Fel gest + Rita din gest + + Du har ritat ditt grafiska lösenord fel %d gånger. + \n\nFörsök igen om %d sekunder. + diff --git a/packages/Keyguard/res/values-tr/strings_custom.xml b/packages/Keyguard/res/values-tr/strings_custom.xml index e2ae6072897c..e06bf5dab7c3 100644 --- a/packages/Keyguard/res/values-tr/strings_custom.xml +++ b/packages/Keyguard/res/values-tr/strings_custom.xml @@ -1,5 +1,5 @@ - + - + Kilit Ekranı Rastgele tuşları yükle + Kilidi açmak için desen çizin + Doğru! + Tekrar deneyin + Desenle kilit açma. + Desen alanı. + Deseni unuttum + Yanlış Desen + Deseninizi çizin + + Kilit açma deseninizi %d kez yanlış girdiniz. + \n\n%d saniye sonra tekrar deneyiniz. + diff --git a/packages/Keyguard/res/values-uk/strings_custom.xml b/packages/Keyguard/res/values-uk/strings_custom.xml index 1e75e1988c5a..1cda7dc25fa2 100644 --- a/packages/Keyguard/res/values-uk/strings_custom.xml +++ b/packages/Keyguard/res/values-uk/strings_custom.xml @@ -1,5 +1,5 @@ - + - + diff --git a/packages/Keyguard/res/values-vi/strings_custom.xml b/packages/Keyguard/res/values-vi/strings_custom.xml index 167c1162d688..bf074917107e 100644 --- a/packages/Keyguard/res/values-vi/strings_custom.xml +++ b/packages/Keyguard/res/values-vi/strings_custom.xml @@ -1,5 +1,5 @@ - + - + Màn hình khoá Tải lại nút ngẫu nhiên diff --git a/packages/Keyguard/res/values-zh-rCN/strings_custom.xml b/packages/Keyguard/res/values-zh-rCN/strings_custom.xml index d1349edeca88..842e29c91f07 100644 --- a/packages/Keyguard/res/values-zh-rCN/strings_custom.xml +++ b/packages/Keyguard/res/values-zh-rCN/strings_custom.xml @@ -1,5 +1,5 @@ - + - + 锁屏 重新加载随机按钮 + 绘制手势解锁 + 正确! + 请重试 + 手势解锁 + 手势区域 + 忘记手势 + 错误手势 + 绘制您的手势 + 您已经 %d 次错误地绘制了解锁图案。\n\n请在 %d 秒后重试。 diff --git a/packages/Keyguard/res/values-zh-rHK/strings_custom.xml b/packages/Keyguard/res/values-zh-rHK/strings_custom.xml index aab74b3909d4..915bdbb81521 100644 --- a/packages/Keyguard/res/values-zh-rHK/strings_custom.xml +++ b/packages/Keyguard/res/values-zh-rHK/strings_custom.xml @@ -1,5 +1,5 @@ - + - + 鎖定螢幕 重新載入隨機按鈕 diff --git a/packages/Keyguard/res/values-zh-rTW/strings_custom.xml b/packages/Keyguard/res/values-zh-rTW/strings_custom.xml index afd5548fe3e8..a0a7e1bde2d2 100644 --- a/packages/Keyguard/res/values-zh-rTW/strings_custom.xml +++ b/packages/Keyguard/res/values-zh-rTW/strings_custom.xml @@ -1,5 +1,5 @@ - + - + 鎖定畫面 diff --git a/packages/Keyguard/res/values/integers.xml b/packages/Keyguard/res/values/integers.xml index dc90bbff1aae..080e1b3d1067 100644 --- a/packages/Keyguard/res/values/integers.xml +++ b/packages/Keyguard/res/values/integers.xml @@ -19,4 +19,7 @@ 75 0 + + + 2 diff --git a/packages/Keyguard/res/values/strings_custom.xml b/packages/Keyguard/res/values/strings_custom.xml index 0571f95446fd..7659a0262e67 100644 --- a/packages/Keyguard/res/values/strings_custom.xml +++ b/packages/Keyguard/res/values/strings_custom.xml @@ -14,9 +14,30 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> - + Lockscreen Reload random buttons + + + Draw gesture to unlock + + Correct! + + Try again + + Gesture unlock. + + Gesture area. + + Forgot Gesture + + Wrong Gesture + + Draw your gesture + + You have incorrectly drawn your unlock gesture %d times. + \n\nTry again in %d seconds. + diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java index 461fd77d813a..b9cee0906aa7 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java @@ -20,11 +20,15 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.CountDownTimer; +import android.os.PowerManager; import android.os.SystemClock; +import android.provider.Settings; import android.text.Editable; import android.text.TextWatcher; import android.util.AttributeSet; +import android.view.GestureDetector; import android.view.HapticFeedbackConstants; +import android.view.MotionEvent; import android.view.KeyEvent; import android.view.View; import android.view.inputmethod.EditorInfo; @@ -47,6 +51,8 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout private Drawable mBouncerFrame; protected boolean mEnableHaptics; + private GestureDetector mDoubleTapGesture; + // To avoid accidental lockout due to events while the device in in the pocket, ignore // any passwords with length less than or equal to this length. protected static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3; @@ -101,10 +107,30 @@ protected boolean shouldLockout(long deadline) { protected void onFinishInflate() { mLockPatternUtils = new LockPatternUtils(mContext); + mDoubleTapGesture = new GestureDetector(mContext, + new GestureDetector.SimpleOnGestureListener() { + @Override + public boolean onDoubleTap(MotionEvent e) { + PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + if (pm != null) pm.goToSleep(e.getEventTime()); + return true; + } + }); + mPasswordEntry = (TextView) findViewById(getPasswordTextViewId()); mPasswordEntry.setOnEditorActionListener(this); mPasswordEntry.addTextChangedListener(this); + if (Settings.AOKP.getInt(mContext.getContentResolver(), + Settings.AOKP.DOUBLE_TAP_SLEEP_GESTURE, 0) == 1) { + mPasswordEntry.setOnTouchListener(new OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + return mDoubleTapGesture.onTouchEvent(event); + } + }); + } + // Set selected property on so the view can send accessibility events. mPasswordEntry.setSelected(true); diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardGestureView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardGestureView.java new file mode 100644 index 000000000000..b931b8ca5c67 --- /dev/null +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardGestureView.java @@ -0,0 +1,412 @@ +/* + * 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. + */ +package com.android.keyguard; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.accounts.AccountManagerCallback; +import android.accounts.AccountManagerFuture; +import android.accounts.AuthenticatorException; +import android.accounts.OperationCanceledException; +import android.content.Context; +import android.gesture.Gesture; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.PowerManager; +import android.os.CountDownTimer; +import android.os.SystemClock; +import android.os.UserHandle; +import android.provider.Settings; +import android.util.AttributeSet; +import android.util.Log; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; + +import com.android.internal.widget.LockPatternUtils; +import com.android.internal.widget.LockGestureView; + +import java.io.IOException; +import java.util.List; + +public class KeyguardGestureView extends LinearLayout implements KeyguardSecurityView { + + private static final String TAG = "SecurityGestureView"; + private static final boolean DEBUG = false; + + // how long before we clear the wrong gesture + private static final int GESTURE_CLEAR_TIMEOUT_MS = 2000; + + // how long we stay awake after touch events + private static final int UNLOCK_GESTURE_WAKE_INTERVAL_MS = 2000; + + private int mFailedGestureAttemptsSinceLastTimeout = 0; + private int mTotalFailedGestureAttempts = 0; + private CountDownTimer mCountdownTimer = null; + private LockPatternUtils mLockPatternUtils; + private LockGestureView mLockGestureView; + private Button mForgotGestureButton; + private KeyguardSecurityCallback mCallback; + private boolean mEnableFallback; + + /** + * Keeps track of the last time we poked the wake lock during dispatching of the touch event. + * Initialized to something guaranteed to make us poke the wakelock when the user starts + * drawing the gesture. + * @see #dispatchTouchEvent(android.view.MotionEvent) + */ + private long mLastPokeTime = -UNLOCK_GESTURE_WAKE_INTERVAL_MS; + + /** + * Useful for clearing out the wrong gesture after a delay + */ + private Runnable mCancelGestureRunnable = new Runnable() { + public void run() { + mLockGestureView.clearGesture(); + } + }; + private Rect mTempRect = new Rect(); + private SecurityMessageDisplay mSecurityMessageDisplay; + private View mEcaView; + private Drawable mBouncerFrame; + private GestureDetector mDoubleTapGesture; + + enum FooterMode { + Normal, + ForgotLockGesture, + VerifyUnlocked + } + + public KeyguardGestureView(Context context) { + this(context, null); + } + + public KeyguardGestureView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public void setKeyguardCallback(KeyguardSecurityCallback callback) { + mCallback = callback; + } + + public void setLockPatternUtils(LockPatternUtils utils) { + mLockPatternUtils = utils; + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mLockPatternUtils = mLockPatternUtils == null + ? new LockPatternUtils(mContext) : mLockPatternUtils; + + mDoubleTapGesture = new GestureDetector(mContext, + new GestureDetector.SimpleOnGestureListener() { + @Override + public boolean onDoubleTap(MotionEvent e) { + PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + if (pm != null) pm.goToSleep(e.getEventTime()); + return true; + } + }); + mLockGestureView = (LockGestureView) findViewById(R.id.lock_gesture_view); + mLockGestureView.setSaveEnabled(false); + mLockGestureView.setFocusable(false); + mLockGestureView.setOnGestureListener(new UnlockGestureListener()); + + // stealth mode will be the same for the life of this screen + mLockGestureView.setInStealthMode(!mLockPatternUtils.isVisibleGestureEnabled()); + + if (Settings.AOKP.getInt(mContext.getContentResolver(), + Settings.AOKP.DOUBLE_TAP_SLEEP_GESTURE, 0) == 1) { + mLockGestureView.setOnTouchListener(new OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + return mDoubleTapGesture.onTouchEvent(event); + } + }); + } + + mForgotGestureButton = (Button) findViewById(R.id.forgot_password_button); + // note: some configurations don't have an emergency call area + if (mForgotGestureButton != null) { + mForgotGestureButton.setText(R.string.kg_forgot_gesture_button_text); + mForgotGestureButton.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + mCallback.showBackupSecurity(); + } + }); + } + + setFocusableInTouchMode(true); + + maybeEnableFallback(mContext); + mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this); + mEcaView = findViewById(R.id.keyguard_selector_fade_container); + View bouncerFrameView = findViewById(R.id.keyguard_bouncer_frame); + if (bouncerFrameView != null) { + mBouncerFrame = bouncerFrameView.getBackground(); + } + } + + private void updateFooter(FooterMode mode) { + if (mForgotGestureButton == null) return; // no ECA? no footer + + switch (mode) { + case Normal: + if (DEBUG) Log.d(TAG, "mode normal"); + mForgotGestureButton.setVisibility(View.GONE); + break; + case ForgotLockGesture: + if (DEBUG) Log.d(TAG, "mode ForgotLockGesture"); + mForgotGestureButton.setVisibility(View.VISIBLE); + break; + case VerifyUnlocked: + if (DEBUG) Log.d(TAG, "mode VerifyUnlocked"); + mForgotGestureButton.setVisibility(View.GONE); + } + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + boolean result = super.onTouchEvent(ev); + // as long as the user is entering a gesture (i.e sending a touch event that was handled + // by this screen), keep poking the wake lock so that the screen will stay on. + final long elapsed = SystemClock.elapsedRealtime() - mLastPokeTime; + if (result && (elapsed > (UNLOCK_GESTURE_WAKE_INTERVAL_MS - 100))) { + mLastPokeTime = SystemClock.elapsedRealtime(); + } + mTempRect.set(0, 0, 0, 0); + offsetRectIntoDescendantCoords(mLockGestureView, mTempRect); + ev.offsetLocation(mTempRect.left, mTempRect.top); + result = mLockGestureView.dispatchTouchEvent(ev) || result; + ev.offsetLocation(-mTempRect.left, -mTempRect.top); + return result; + } + + public void reset() { + // reset lock gesture + mLockGestureView.enableInput(); + mLockGestureView.setEnabled(true); + mLockGestureView.clearGesture(); + + // if the user is currently locked out, enforce it. + long deadline = mLockPatternUtils.getLockoutAttemptDeadline(); + if (deadline != 0) { + handleAttemptLockout(deadline); + } else { + displayDefaultSecurityMessage(); + } + + // the footer depends on how many total attempts the user has failed + if (mCallback.isVerifyUnlockOnly()) { + updateFooter(FooterMode.VerifyUnlocked); + } else if (mEnableFallback && + (mTotalFailedGestureAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) { + updateFooter(FooterMode.ForgotLockGesture); + } else { + updateFooter(FooterMode.Normal); + } + + } + + private void displayDefaultSecurityMessage() { + if (KeyguardUpdateMonitor.getInstance(mContext).getMaxBiometricUnlockAttemptsReached()) { + mSecurityMessageDisplay.setMessage(R.string.faceunlock_multiple_failures, true); + } else { + mSecurityMessageDisplay.setMessage(R.string.kg_gesture_instructions, false); + } + } + + @Override + public void showUsabilityHint() { + } + + /** TODO: hook this up */ + public void cleanUp() { + if (DEBUG) Log.v(TAG, "Cleanup() called on " + this); + mLockPatternUtils = null; + mLockGestureView.setOnGestureListener(null); + } + + @Override + public void onWindowFocusChanged(boolean hasWindowFocus) { + super.onWindowFocusChanged(hasWindowFocus); + if (hasWindowFocus) { + // when timeout dialog closes we want to update our state + reset(); + } + } + + private class UnlockGestureListener implements LockGestureView.OnLockGestureListener { + + public void onGestureStart() { + mLockGestureView.setDisplayMode(LockGestureView.DisplayMode.Correct); + mLockGestureView.removeCallbacks(mCancelGestureRunnable); + } + + public void onGestureCleared() { + } + + public void onGestureDetected(Gesture gesture) { + mCallback.userActivity(UNLOCK_GESTURE_WAKE_INTERVAL_MS); + if (mLockPatternUtils.checkGesture(gesture)) { + mCallback.reportSuccessfulUnlockAttempt(); + mLockGestureView.setDisplayMode(LockGestureView.DisplayMode.Correct); + mTotalFailedGestureAttempts = 0; + mCallback.dismiss(true); + } else { + mLockGestureView.setDisplayMode(LockGestureView.DisplayMode.Wrong); + mTotalFailedGestureAttempts++; + mFailedGestureAttemptsSinceLastTimeout++; + mCallback.reportFailedUnlockAttempt(); + if (mFailedGestureAttemptsSinceLastTimeout + >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) { + long deadline = mLockPatternUtils.setLockoutAttemptDeadline(); + handleAttemptLockout(deadline); + } else { + mSecurityMessageDisplay.setMessage(R.string.kg_wrong_gesture, true); + mLockGestureView.postDelayed(mCancelGestureRunnable, GESTURE_CLEAR_TIMEOUT_MS); + } + } + } + } + + private void maybeEnableFallback(Context context) { + // Ask the account manager if we have an account that can be used as a + // fallback in case the user forgets his or her gesture. + AccountAnalyzer accountAnalyzer = new AccountAnalyzer(AccountManager.get(context)); + accountAnalyzer.start(); + } + + private class AccountAnalyzer implements AccountManagerCallback { + private final AccountManager mAccountManager; + private final Account[] mAccounts; + private int mAccountIndex; + + private AccountAnalyzer(AccountManager accountManager) { + mAccountManager = accountManager; + mAccounts = accountManager.getAccountsByTypeAsUser("com.google", + new UserHandle(mLockPatternUtils.getCurrentUser())); + } + + private void next() { + // if we are ready to enable the fallback or if we depleted the list of accounts + // then finish and get out + if (mEnableFallback || mAccountIndex >= mAccounts.length) { + return; + } + + // lookup the confirmCredentials intent for the current account + mAccountManager.confirmCredentialsAsUser(mAccounts[mAccountIndex], null, null, this, + null, new UserHandle(mLockPatternUtils.getCurrentUser())); + } + + public void start() { + mEnableFallback = false; + mAccountIndex = 0; + next(); + } + + public void run(AccountManagerFuture future) { + try { + Bundle result = future.getResult(); + if (result.getParcelable(AccountManager.KEY_INTENT) != null) { + mEnableFallback = true; + } + } catch (OperationCanceledException e) { + // just skip the account if we are unable to query it + } catch (IOException e) { + // just skip the account if we are unable to query it + } catch (AuthenticatorException e) { + // just skip the account if we are unable to query it + } finally { + mAccountIndex++; + next(); + } + } + } + + private void handleAttemptLockout(long elapsedRealtimeDeadline) { + mLockGestureView.clearGesture(); + mLockGestureView.setEnabled(false); + final long elapsedRealtime = SystemClock.elapsedRealtime(); + if (mEnableFallback) { + updateFooter(FooterMode.ForgotLockGesture); + } + + mCountdownTimer = new CountDownTimer(elapsedRealtimeDeadline - elapsedRealtime, 1000) { + + @Override + public void onTick(long millisUntilFinished) { + final int secondsRemaining = (int) (millisUntilFinished / 1000); + mSecurityMessageDisplay.setMessage( + R.string.kg_too_many_failed_attempts_countdown, true, secondsRemaining); + } + + @Override + public void onFinish() { + mLockGestureView.setEnabled(true); + displayDefaultSecurityMessage(); + // TODO mUnlockIcon.setVisibility(View.VISIBLE); + mFailedGestureAttemptsSinceLastTimeout = 0; + if (mEnableFallback) { + updateFooter(FooterMode.ForgotLockGesture); + } else { + updateFooter(FooterMode.Normal); + } + } + + }.start(); + } + + @Override + public boolean needsInput() { + return false; + } + + @Override + public void onPause() { + if (mCountdownTimer != null) { + mCountdownTimer.cancel(); + mCountdownTimer = null; + } + } + + @Override + public void onResume(int reason) { + reset(); + } + + @Override + public KeyguardSecurityCallback getCallback() { + return mCallback; + } + + @Override + public void showBouncer(int duration) { + KeyguardSecurityViewHelper. + showBouncer(mSecurityMessageDisplay, mEcaView, mBouncerFrame, duration); + } + + @Override + public void hideBouncer(int duration) { + KeyguardSecurityViewHelper. + hideBouncer(mSecurityMessageDisplay, mEcaView, mBouncerFrame, duration); + } +} diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java index 4ffb3b621433..68da7d1e083b 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java @@ -469,11 +469,10 @@ private int getDisabledFeatures(DevicePolicyManager dpm) { } private boolean widgetsDisabled() { - boolean disabledByLowRamDevice = ActivityManager.isLowRamDeviceStatic(); boolean disabledByDpm = (mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0; boolean disabledByUser = !mLockPatternUtils.getWidgetsEnabled(); - return disabledByLowRamDevice || disabledByDpm || disabledByUser; + return disabledByDpm || disabledByUser; } private boolean cameraDisabledByDpm() { @@ -664,6 +663,9 @@ private void showTimeoutDialog() { case Password: messageId = R.string.kg_too_many_failed_password_attempts_dialog_message; break; + case Gesture: + messageId = R.string.kg_too_many_failed_gesture_attempts_dialog_message; + break; } if (messageId != 0) { @@ -807,6 +809,7 @@ private void showNextSecurityScreenOrFinish(boolean authenticated) { case PIN: case Account: case Biometric: + case Gesture: finish = true; break; @@ -1142,6 +1145,7 @@ private int getSecurityViewIdForMode(SecurityMode securityMode) { case Account: return R.id.keyguard_account_view; case SimPin: return R.id.keyguard_sim_pin_view; case SimPuk: return R.id.keyguard_sim_puk_view; + case Gesture: return R.id.keyguard_gesture_view; } return 0; } @@ -1156,6 +1160,7 @@ private int getLayoutIdFor(SecurityMode securityMode) { case Account: return R.layout.keyguard_account_view; case SimPin: return R.layout.keyguard_sim_pin_view; case SimPuk: return R.layout.keyguard_sim_puk_view; + case Gesture: return R.layout.keyguard_gesture_view; default: return 0; } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java index 3b72f4327e3b..633cb930b085 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java @@ -25,11 +25,14 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.os.PowerManager; import android.os.CountDownTimer; import android.os.SystemClock; import android.os.UserHandle; +import android.provider.Settings; import android.util.AttributeSet; import android.util.Log; +import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; import android.widget.Button; @@ -88,6 +91,8 @@ public void run() { private View mEcaView; private Drawable mBouncerFrame; + private GestureDetector mDoubleTapGesture; + enum FooterMode { Normal, ForgotLockPattern, @@ -117,10 +122,21 @@ protected void onFinishInflate() { mLockPatternUtils = mLockPatternUtils == null ? new LockPatternUtils(mContext) : mLockPatternUtils; + mDoubleTapGesture = new GestureDetector(mContext, + new GestureDetector.SimpleOnGestureListener() { + @Override + public boolean onDoubleTap(MotionEvent e) { + PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + if (pm != null) pm.goToSleep(e.getEventTime()); + return true; + } + }); + mLockPatternView = (LockPatternView) findViewById(R.id.lockPatternView); mLockPatternView.setSaveEnabled(false); mLockPatternView.setFocusable(false); mLockPatternView.setOnPatternListener(new UnlockPatternListener()); + mLockPatternView.setLockPatternUtils(mLockPatternUtils); // stealth mode will be the same for the life of this screen mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled()); @@ -128,6 +144,18 @@ protected void onFinishInflate() { // vibrate mode will be the same for the life of this screen mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled()); + mLockPatternView.setLockPatternSize(mLockPatternUtils.getLockPatternSize()); + + if (Settings.AOKP.getInt(mContext.getContentResolver(), + Settings.AOKP.DOUBLE_TAP_SLEEP_GESTURE, 0) == 1) { + mLockPatternView.setOnTouchListener(new OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + return mDoubleTapGesture.onTouchEvent(event); + } + }); + } + mForgotPatternButton = (Button) findViewById(R.id.forgot_password_button); // note: some configurations don't have an emergency call area if (mForgotPatternButton != null) { diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java index 4129e33fb07b..299db6599314 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java @@ -36,7 +36,8 @@ enum SecurityMode { Biometric, // Unlock with a biometric key (e.g. finger print or face unlock) Account, // Unlock by entering an account's login and password. SimPin, // Unlock by entering a sim pin. - SimPuk // Unlock by entering a sim puk + SimPuk, // Unlock by entering a sim puk + Gesture // unlock by drawing a gesture } private Context mContext; @@ -103,6 +104,12 @@ SecurityMode getSecurityMode() { SecurityMode.Account : SecurityMode.Pattern; } break; + case DevicePolicyManager.PASSWORD_QUALITY_GESTURE_WEAK: + if (mLockPatternUtils.isLockGestureEnabled()) { + mode = mLockPatternUtils.isPermanentlyLocked() ? + SecurityMode.Account : SecurityMode.Gesture; + } + break; default: throw new IllegalStateException("Unknown unlock mode:" + mode); @@ -123,7 +130,8 @@ SecurityMode getAlternateFor(SecurityMode mode) { if (isBiometricUnlockEnabled() && !isBiometricUnlockSuppressed() && (mode == SecurityMode.Password || mode == SecurityMode.PIN - || mode == SecurityMode.Pattern)) { + || mode == SecurityMode.Pattern + || mode == SecurityMode.Gesture )) { return SecurityMode.Biometric; } return mode; // no alternate, return what was given @@ -141,6 +149,8 @@ SecurityMode getBackupSecurityMode(SecurityMode mode) { return getSecurityMode(); case Pattern: return SecurityMode.Account; + case Gesture: + return SecurityMode.Account; } return mode; // no backup, return current security mode } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSelectorView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSelectorView.java index 9c5c617e3037..944120ad021e 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardSelectorView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSelectorView.java @@ -37,6 +37,7 @@ import android.util.AttributeSet; import android.util.Log; import android.util.Slog; +import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; @@ -78,6 +79,7 @@ public class KeyguardSelectorView extends LinearLayout implements KeyguardSecuri private UnlockReceiver mUnlockReceiver; private IntentFilter mUnlockFilter; private float mBatteryLevel; + private GestureDetector mDoubleTapGesture; OnTriggerListener mOnTriggerListener = new OnTriggerListener() { @@ -222,6 +224,26 @@ public boolean onTouch(View v, MotionEvent event) { mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this); View bouncerFrameView = findViewById(R.id.keyguard_selector_view_frame); mBouncerFrame = bouncerFrameView.getBackground(); + + mDoubleTapGesture = new GestureDetector(mContext, + new GestureDetector.SimpleOnGestureListener() { + @Override + public boolean onDoubleTap(MotionEvent e) { + PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + if (pm != null) pm.goToSleep(e.getEventTime()); + return true; + } + }); + + if (Settings.AOKP.getInt(mContext.getContentResolver(), + Settings.AOKP.DOUBLE_TAP_SLEEP_GESTURE, 0) == 1) { + mGlowPadView.setOnTouchListener(new OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + return mDoubleTapGesture.onTouchEvent(event); + } + }); + } } public void setCarrierArea(View carrierArea) { diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java index 84592855c164..864fe1eec92b 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java @@ -747,17 +747,12 @@ public synchronized void onScreenTurnedOn(final IKeyguardShowCallback callback) if (DEBUG) Log.d(TAG, "onScreenTurnedOn()"); mScreenOn = true; - final IBinder token; - - // If keyguard is disabled, we need to inform PhoneWindowManager with a null + // If keyguard is not showing, we need to inform PhoneWindowManager with a null // token so it doesn't wait for us to draw... - final boolean disabled = - mLockPatternUtils.isLockScreenDisabled() && !mLockPatternUtils.isSecure(); - if (mKeyguardHost == null || disabled) { - token = null; - } else { - token = mKeyguardHost.getWindowToken(); - } + final IBinder token = isShowing() ? mKeyguardHost.getWindowToken() : null; + + if (DEBUG && token == null) Slog.v(TAG, "send wm null token: " + + (mKeyguardHost == null ? "host was null" : "not showing")); if (mKeyguardView != null) { mKeyguardView.onScreenTurnedOn(); diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java index 3ba7b34e1d0a..0f7dbc4a73e0 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java @@ -55,6 +55,7 @@ import android.view.WindowManagerPolicy; import com.android.internal.telephony.IccCardConstants; +import com.android.internal.util.aokp.QuietHoursHelper; import com.android.internal.widget.LockPatternUtils; @@ -1196,6 +1197,10 @@ private void playSounds(boolean locked) { return; } + if (QuietHoursHelper.inQuietHours(mContext, Settings.AOKP.QUIET_HOURS_SYSTEM)) { + return; + } + final ContentResolver cr = mContext.getContentResolver(); if (Settings.System.getInt(cr, Settings.System.LOCKSCREEN_SOUNDS_ENABLED, 1) == 1) { final int whichSound = locked @@ -1214,6 +1219,13 @@ private void playSounds(boolean locked) { // If music is playing, don't play the sound if (mAudioManager.isMusicActive()) return; + // If user is in a call, don't play the sound + TelephonyManager tm = (TelephonyManager) mContext. + getSystemService(Context.TELEPHONY_SERVICE); + if (tm != null && (tm.isOffhook() || tm.isRinging())) { + return; + } + mLockSoundStreamId = mLockSounds.play(whichSound, mLockSoundVolume, mLockSoundVolume, 1/*priortiy*/, 0/*loop*/, 1.0f/*rate*/); } diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml index dfff160cd547..6f0a15c42d21 100644 --- a/packages/SettingsProvider/res/values/defaults.xml +++ b/packages/SettingsProvider/res/values/defaults.xml @@ -21,8 +21,8 @@ 60000 false - cell,bluetooth,wifi,nfc,wimax - bluetooth,wifi,nfc + cell,bluetooth,wifi,nfc,wimax,ant + bluetooth,wifi,nfc,ant true true true diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index 3224385fde5b..dccb78b48fcd 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -1697,7 +1697,7 @@ private void upgradeLockPatternLocation(SQLiteDatabase db) { try { LockPatternUtils lpu = new LockPatternUtils(mContext); List cellPattern = - LockPatternUtils.stringToPattern(lockPattern); + lpu.stringToPattern(lockPattern); lpu.saveLockPattern(cellPattern); } catch (IllegalArgumentException e) { // Don't want corrupted lock pattern to hang the reboot process diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index da3e811674d8..7eaffd774720 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -107,6 +107,9 @@ android:description="@string/permdesc_screenrecord" android:label="@string/permlab_screenrecord" /> + + + + - - + - - - diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags index ab45d99dc857..e38688dae0eb 100644 --- a/packages/SystemUI/proguard.flags +++ b/packages/SystemUI/proguard.flags @@ -7,4 +7,6 @@ public void setGlowScale(float); } +-keep class com.android.systemui.statusbar.BaseStatusBar -keep class com.android.systemui.statusbar.tv.TvStatusBar +-keep class com.android.systemui.statusbar.phone.PhoneStatusBar diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_2g3g_on.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_2g3g_on.png new file mode 100644 index 000000000000..324ce12ccda2 Binary files /dev/null and b/packages/SystemUI/res/drawable-hdpi/ic_qs_2g3g_on.png differ diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_2g_off.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_2g_off.png deleted file mode 100644 index b8395ebabba7..000000000000 Binary files a/packages/SystemUI/res/drawable-hdpi/ic_qs_2g_off.png and /dev/null differ diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_2g_on.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_2g_on.png index 3166796bb48a..954d3644a6e2 100644 Binary files a/packages/SystemUI/res/drawable-hdpi/ic_qs_2g_on.png and b/packages/SystemUI/res/drawable-hdpi/ic_qs_2g_on.png differ diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_3g_on.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_3g_on.png new file mode 100644 index 000000000000..94ad007bfdd9 Binary files /dev/null and b/packages/SystemUI/res/drawable-hdpi/ic_qs_3g_on.png differ diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_lte_off.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_lte_off.png deleted file mode 100644 index e79be205257c..000000000000 Binary files a/packages/SystemUI/res/drawable-hdpi/ic_qs_lte_off.png and /dev/null differ diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_lte_on.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_lte_on.png index b240c7d0f45e..e2486f396e23 100644 Binary files a/packages/SystemUI/res/drawable-hdpi/ic_qs_lte_on.png and b/packages/SystemUI/res/drawable-hdpi/ic_qs_lte_on.png differ diff --git a/packages/SystemUI/res/drawable-hdpi/ic_recent_lock_app.png b/packages/SystemUI/res/drawable-hdpi/ic_recent_lock_app.png new file mode 100644 index 000000000000..6f3d6718f849 Binary files /dev/null and b/packages/SystemUI/res/drawable-hdpi/ic_recent_lock_app.png differ diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_2g3g_on.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_2g3g_on.png new file mode 100644 index 000000000000..15bbab6940b0 Binary files /dev/null and b/packages/SystemUI/res/drawable-mdpi/ic_qs_2g3g_on.png differ diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_2g_on.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_2g_on.png new file mode 100644 index 000000000000..0605590d00ae Binary files /dev/null and b/packages/SystemUI/res/drawable-mdpi/ic_qs_2g_on.png differ diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_3g_on.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_3g_on.png new file mode 100644 index 000000000000..1d8e519de7fc Binary files /dev/null and b/packages/SystemUI/res/drawable-mdpi/ic_qs_3g_on.png differ diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_lte_on.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_lte_on.png new file mode 100644 index 000000000000..65b74688b3f6 Binary files /dev/null and b/packages/SystemUI/res/drawable-mdpi/ic_qs_lte_on.png differ diff --git a/packages/SystemUI/res/drawable-mdpi/ic_recent_lock_app.png b/packages/SystemUI/res/drawable-mdpi/ic_recent_lock_app.png new file mode 100644 index 000000000000..03be49b13c6c Binary files /dev/null and b/packages/SystemUI/res/drawable-mdpi/ic_recent_lock_app.png differ diff --git a/packages/SystemUI/res/drawable-nodpi/jandycane.png b/packages/SystemUI/res/drawable-nodpi/jandycane.png deleted file mode 100644 index 278cfec49546..000000000000 Binary files a/packages/SystemUI/res/drawable-nodpi/jandycane.png and /dev/null differ diff --git a/packages/SystemUI/res/drawable-nodpi/redunicorn.png b/packages/SystemUI/res/drawable-nodpi/redunicorn.png deleted file mode 100644 index de17e53e9dec..000000000000 Binary files a/packages/SystemUI/res/drawable-nodpi/redunicorn.png and /dev/null differ diff --git a/packages/SystemUI/res/drawable-nodpi/redunicorn0.png b/packages/SystemUI/res/drawable-nodpi/redunicorn0.png deleted file mode 100644 index de17e53e9dec..000000000000 Binary files a/packages/SystemUI/res/drawable-nodpi/redunicorn0.png and /dev/null differ diff --git a/packages/SystemUI/res/drawable-nodpi/redunicorn1.png b/packages/SystemUI/res/drawable-nodpi/redunicorn1.png deleted file mode 100644 index de17e53e9dec..000000000000 Binary files a/packages/SystemUI/res/drawable-nodpi/redunicorn1.png and /dev/null differ diff --git a/packages/SystemUI/res/drawable-nodpi/redunicorn2.png b/packages/SystemUI/res/drawable-nodpi/redunicorn2.png deleted file mode 100644 index de17e53e9dec..000000000000 Binary files a/packages/SystemUI/res/drawable-nodpi/redunicorn2.png and /dev/null differ diff --git a/packages/SystemUI/res/drawable-nodpi/unicorn.png b/packages/SystemUI/res/drawable-nodpi/unicorn.png new file mode 100644 index 000000000000..8ee3645d3ba6 Binary files /dev/null and b/packages/SystemUI/res/drawable-nodpi/unicorn.png differ diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_2g3g_on.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_2g3g_on.png new file mode 100644 index 000000000000..fbb52fd44483 Binary files /dev/null and b/packages/SystemUI/res/drawable-xhdpi/ic_qs_2g3g_on.png differ diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_2g_off.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_2g_off.png deleted file mode 100644 index ffa6ac4b9107..000000000000 Binary files a/packages/SystemUI/res/drawable-xhdpi/ic_qs_2g_off.png and /dev/null differ diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_2g_on.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_2g_on.png index b5130f343621..e6a3bd22ccbf 100644 Binary files a/packages/SystemUI/res/drawable-xhdpi/ic_qs_2g_on.png and b/packages/SystemUI/res/drawable-xhdpi/ic_qs_2g_on.png differ diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_3g_on.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_3g_on.png new file mode 100644 index 000000000000..c45639beca96 Binary files /dev/null and b/packages/SystemUI/res/drawable-xhdpi/ic_qs_3g_on.png differ diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_lte_off.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_lte_off.png deleted file mode 100644 index 8e8bf177f3d2..000000000000 Binary files a/packages/SystemUI/res/drawable-xhdpi/ic_qs_lte_off.png and /dev/null differ diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_lte_on.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_lte_on.png index 46ad7c977235..2ac4019790e0 100644 Binary files a/packages/SystemUI/res/drawable-xhdpi/ic_qs_lte_on.png and b/packages/SystemUI/res/drawable-xhdpi/ic_qs_lte_on.png differ diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_recent_lock_app.png b/packages/SystemUI/res/drawable-xhdpi/ic_recent_lock_app.png new file mode 100644 index 000000000000..f12f46d1f143 Binary files /dev/null and b/packages/SystemUI/res/drawable-xhdpi/ic_recent_lock_app.png differ diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_2g3g_on.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_2g3g_on.png new file mode 100644 index 000000000000..5f728843f838 Binary files /dev/null and b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_2g3g_on.png differ diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_2g_on.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_2g_on.png new file mode 100644 index 000000000000..00ade8df3778 Binary files /dev/null and b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_2g_on.png differ diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_3g_on.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_3g_on.png new file mode 100644 index 000000000000..803d478cb3c9 Binary files /dev/null and b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_3g_on.png differ diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_lte_on.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_lte_on.png new file mode 100644 index 000000000000..daa447834af8 Binary files /dev/null and b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_lte_on.png differ diff --git a/packages/SystemUI/res/layout-land/status_bar_recent_item.xml b/packages/SystemUI/res/layout-land/status_bar_recent_item.xml index 12576417857a..da54b1c39229 100644 --- a/packages/SystemUI/res/layout-land/status_bar_recent_item.xml +++ b/packages/SystemUI/res/layout-land/status_bar_recent_item.xml @@ -49,6 +49,13 @@ android:layout_width="@dimen/status_bar_recents_thumbnail_width" android:layout_height="@dimen/status_bar_recents_thumbnail_height" /> + @@ -36,4 +37,25 @@ android:layout_gravity="center_vertical" systemui:text="@string/status_bar_settings_auto_brightness_label" /> + + + diff --git a/packages/SystemUI/res/layout/status_bar_recent_item.xml b/packages/SystemUI/res/layout/status_bar_recent_item.xml index 6290bb32fc9b..91e262478767 100644 --- a/packages/SystemUI/res/layout/status_bar_recent_item.xml +++ b/packages/SystemUI/res/layout/status_bar_recent_item.xml @@ -63,6 +63,13 @@ android:layout_width="@dimen/status_bar_recents_thumbnail_width" android:layout_height="@dimen/status_bar_recents_thumbnail_height" /> + + diff --git a/packages/SystemUI/res/values-af/strings_custom.xml b/packages/SystemUI/res/values-af/strings_custom.xml index ca1ea94980ef..f89585f90d90 100644 --- a/packages/SystemUI/res/values-af/strings_custom.xml +++ b/packages/SystemUI/res/values-af/strings_custom.xml @@ -1,5 +1,5 @@ - + + + + + + + + + + + + + - + + HSPA+ + Καταγραφή της οθόνης + Διακοπή + Εναλλαγή δείκτη + Δεν είναι δυνατή η καταγραφή οθόνης + RAM + Εκκινήθηκε από εφαρμογή. + Κλείσε + diff --git a/packages/SystemUI/res/values-el/strings_toggles.xml b/packages/SystemUI/res/values-el/strings_toggles.xml index c1a514f79199..c8c6a938430f 100644 --- a/packages/SystemUI/res/values-el/strings_toggles.xml +++ b/packages/SystemUI/res/values-el/strings_toggles.xml @@ -1,5 +1,5 @@ - + - + + Τρέχων Χρήστης + Φωτεινότητα + Ένταση ήχου + Ρυθμίσεις + WiFi + Σήμα + Αυτόματη περιστροφή + Ρολόι + GPS + Τοποθεσία + Μπαταρία + Λειτουργία αεροπλάνου + Bluetooth + Έπαρση + Δόνηση + Αθόρυβο + Ταχεία Φόρτιση + Συγχρονισμός + NFC + Φακός + Πρόσδεση USB + Απόκρυψη NavBar + Γρήγορη εγγραφή + Ώρες ησυχίας + Αναστολή + Γραμμή Κατάστασης + Στιγμιότυπο οθόνης + Επανεκκίνηση + Προσαρμοσμένο + Παραμονή σε κανονική κατάσταση λειτουργίας + Ασύρματο ADB + HeadsUP + Έπαρση + Γίνεται εγγραφή... + Γρήγορη εγγραφή + Παρατεταμένο πάτημα για εγγραφή + Απουσία δραστηριότητας + Επανεκκίνηση + diff --git a/packages/SystemUI/res/values-es/strings_custom.xml b/packages/SystemUI/res/values-es/strings_custom.xml index aff8022d0205..9efdf4c23eaf 100644 --- a/packages/SystemUI/res/values-es/strings_custom.xml +++ b/packages/SystemUI/res/values-es/strings_custom.xml @@ -1,5 +1,5 @@ - + - HSPA + + HSPA+ La pantalla está siendo grabada Grabación de pantalla Parar @@ -44,4 +44,7 @@ Permite la aplicación capturar una grabación de la pantalla. Esto está protegido por SystemUI. Ventana de la aplicación Cerrar + Fijar aplicación en la lista + Mostrar los unicornios + Mostrar los unicornios junto con postres de Android dentro del sueño diff --git a/packages/SystemUI/res/values-es/strings_toggles.xml b/packages/SystemUI/res/values-es/strings_toggles.xml index 273d50fee34b..afc9aa8947a7 100644 --- a/packages/SystemUI/res/values-es/strings_toggles.xml +++ b/packages/SystemUI/res/values-es/strings_toggles.xml @@ -1,5 +1,5 @@ - + + + diff --git a/packages/SystemUI/res/values-fa/strings_toggles.xml b/packages/SystemUI/res/values-fa/strings_toggles.xml new file mode 100644 index 000000000000..594e2bced062 --- /dev/null +++ b/packages/SystemUI/res/values-fa/strings_toggles.xml @@ -0,0 +1,18 @@ + + + + diff --git a/packages/SystemUI/res/values-fi/strings_custom.xml b/packages/SystemUI/res/values-fi/strings_custom.xml index 642ac42115df..d582ba5f5f09 100644 --- a/packages/SystemUI/res/values-fi/strings_custom.xml +++ b/packages/SystemUI/res/values-fi/strings_custom.xml @@ -1,5 +1,5 @@ - + + + + + + HSPA+ A képernyő felvételre kerül Képernyő felvétele Állj + Egérmutató ki-be kapcsolása Nem lehet a képernyőfelvételt elindítani + %d%% Programikonok elrejtése - Képernyőfelvétel rögzítés engedélyezése a programnak. Ez a rendszer által védett. + RAM + %1$s szabad + %1$s foglalt + %1$s + %1$s + Alkalmazás által elindítva. + %1$d + folyamat és %2$d szolgáltatás + %1$d + folyamat és %2$d szolgáltatás + %1$d + folyamat és %2$d szolgáltatás + %1$d + folyamat és %2$d szolgáltatás + Felhasználó: %1$s + Képernyőmentés készítése + Lehetővé teszi, hogy az alkalmazás képernyőmentést készítsen. Ezt a SystemUI védi. + Képernyőmentés %1$d mp múlva + Képernyő felvétel készítése + Képernyő felvétel készítésének engedélyezése a programnak. Ez a SystemUI által védett. + Alkalmazás ablak + Bezárás + Alkalmazás rögzítése a listában + Egyszarvúak megjelenítése + Álmodozásokban az egyszarvúak megjelenítése az Android desszertekel együtt diff --git a/packages/SystemUI/res/values-hu/strings_toggles.xml b/packages/SystemUI/res/values-hu/strings_toggles.xml index c1a514f79199..4251aff6ef4f 100644 --- a/packages/SystemUI/res/values-hu/strings_toggles.xml +++ b/packages/SystemUI/res/values-hu/strings_toggles.xml @@ -1,5 +1,5 @@ - + - + + Jelenlegi felhasználó + Fényerő + Hangerő + Beállítások + WiFi + Jel + Auto-forgatás + Óra + GPS + Hely + IME + Akkumulátor + Repülőgép üzemmód + Bluetooth + Swagger + Rezgés + Néma + Gyors töltés + Szinkronizálás + NFC + Zseblámpa + WiFi megosztás + USB megosztás + Hálózati mód + Kedvenc személyek + Hang állapota + Navigációs sáv elrejtése + Gyors felvétel + Csendes órák + Alvás + Állapotsáv + Képernyőmentés + Újraindítás + Egyedi + Ébren marad + Vezeték nélküli ADB + Teljes képernyős mód + Képernyő felvétel + Felugró értesítések + Swagger + Swagger Vasárnap + FBGT + Felvétel... + A felvétel rögzítve + Lejátszás... + Gyors felvétel + Felvétel készítéséhez hosszan nyomja + Alvás + Újraindítás + A készülék újraindul + Ébren tartás Be + Ébren tartás Ki + %s + Vezeték nélküli ADB ki + Hangerő + Rezgés Be + Rezgés Ki + Néma Be + Néma Ki + Gyors-tölt. Be + Gyors-tölt. Ki + Zseblámpa Be + Zseblámpa Ki + NFC Be + NFC Ki + Wifi Megosztás Be + Wifi Megosztás Ki + BT Megosztás Be + BT Megosztás Ki + USB Megosztás Be + USB Megosztás Ki + Szink. Be + Szink. Ki + Csak 2G + Csak 2G (CDMA) + Csak 2G (EvDo) + 2G/3G preferált + 2G/3G auto + Csak 3G + 2G/3G/4G preferált + 3G/4G preferált + Csak LTE + GPS Be + GPS Ki + Nagy pontosság + Energiatakarékos + GPS + Mobilnet Be + Mobilnet Ki + Nav.sáv rejtés Be + Nav.sáv rejtés Ki + Csendes órák Be + Csendes órák Ki + Hang be + Állapotsáv Be + Állapotsáv Ki + #SwaggerSunday + Képernyőmentés + Képernyő felvétel + Teljes képernyős mód Be + Teljes képernyős mód Ki + Ébresztés Ki + Felugró ért. Be + Felugró ért. Ki + diff --git a/packages/SystemUI/res/values-it/strings_custom.xml b/packages/SystemUI/res/values-it/strings_custom.xml index fc6081f54f21..de85bc71fce7 100644 --- a/packages/SystemUI/res/values-it/strings_custom.xml +++ b/packages/SystemUI/res/values-it/strings_custom.xml @@ -1,5 +1,5 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Lock app in list + + + Show unicorns + Show unicorns together with Android\'s desserts inside the dream + diff --git a/packages/SystemUI/res/values/strings_toggles.xml b/packages/SystemUI/res/values/strings_toggles.xml index bea5a6cca747..4fd8ff5cda71 100644 --- a/packages/SystemUI/res/values/strings_toggles.xml +++ b/packages/SystemUI/res/values/strings_toggles.xml @@ -38,8 +38,7 @@ Torch WiFi tether USB tether - 2G - LTE + Network mode Favorite contact Sound state Navbar hide @@ -90,10 +89,15 @@ USB Tether Off Sync On Sync Off - 2g On - 2g Off - LTE On - LTE Off + 2G Only + 2G (CDMA) Only + 2G (EvDo) Only + 2G/3G Pref + 2G/3G Auto + 3G Only + 2G/3G/4G Pref + 3G/4G Pref + LTE Only GPS On GPS Off High accuracy diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index cdb0b6679bb3..7bb94bab2b16 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -130,9 +130,8 @@