Merge "Refcounting is hard"
diff --git a/api/current.txt b/api/current.txt
index 3ea1aad..0d1cc97 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -30255,8 +30255,13 @@
public static final class AccessibilityNodeInfo.CollectionInfo {
method public int getColumnCount();
method public int getRowCount();
+ method public int getSelectionMode();
method public boolean isHierarchical();
method public static android.view.accessibility.AccessibilityNodeInfo.CollectionInfo obtain(int, int, boolean);
+ method public static android.view.accessibility.AccessibilityNodeInfo.CollectionInfo obtain(int, int, boolean, int);
+ field public static final int SELECTION_MODE_MULTIPLE = 2; // 0x2
+ field public static final int SELECTION_MODE_NONE = 0; // 0x0
+ field public static final int SELECTION_MODE_SINGLE = 1; // 0x1
}
public static final class AccessibilityNodeInfo.CollectionItemInfo {
@@ -30265,7 +30270,9 @@
method public int getRowIndex();
method public int getRowSpan();
method public boolean isHeading();
+ method public boolean isSelected();
method public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(int, int, int, int, boolean);
+ method public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(int, int, int, int, boolean, boolean);
}
public static final class AccessibilityNodeInfo.RangeInfo {
@@ -31628,12 +31635,10 @@
method public int getCheckedItemPosition();
method public android.util.SparseBooleanArray getCheckedItemPositions();
method public int getChoiceMode();
- method public int getFirstPositionForRow(int);
method public int getListPaddingBottom();
method public int getListPaddingLeft();
method public int getListPaddingRight();
method public int getListPaddingTop();
- method public int getRowForPosition(int);
method public android.view.View getSelectedView();
method public android.graphics.drawable.Drawable getSelector();
method public java.lang.CharSequence getTextFilter();
diff --git a/cmds/idmap/Android.mk b/cmds/idmap/Android.mk
new file mode 100644
index 0000000..ffa83f2
--- /dev/null
+++ b/cmds/idmap/Android.mk
@@ -0,0 +1,28 @@
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := idmap.cpp create.cpp scan.cpp inspect.cpp
+
+LOCAL_SHARED_LIBRARIES := liblog libutils libandroidfw
+
+LOCAL_MODULE := idmap
+
+LOCAL_C_INCLUDES := external/zlib
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_EXECUTABLE)
diff --git a/cmds/idmap/create.cpp b/cmds/idmap/create.cpp
new file mode 100644
index 0000000..ae35f7b
--- /dev/null
+++ b/cmds/idmap/create.cpp
@@ -0,0 +1,222 @@
+#include "idmap.h"
+
+#include <UniquePtr.h>
+#include <androidfw/AssetManager.h>
+#include <androidfw/ResourceTypes.h>
+#include <androidfw/ZipFileRO.h>
+#include <utils/String8.h>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+
+using namespace android;
+
+namespace {
+ int get_zip_entry_crc(const char *zip_path, const char *entry_name, uint32_t *crc)
+ {
+ UniquePtr<ZipFileRO> zip(ZipFileRO::open(zip_path));
+ if (zip.get() == NULL) {
+ return -1;
+ }
+ ZipEntryRO entry = zip->findEntryByName(entry_name);
+ if (entry == NULL) {
+ return -1;
+ }
+ if (!zip->getEntryInfo(entry, NULL, NULL, NULL, NULL, NULL, (long*)crc)) {
+ return -1;
+ }
+ zip->releaseEntry(entry);
+ return 0;
+ }
+
+ int open_idmap(const char *path)
+ {
+ int fd = TEMP_FAILURE_RETRY(open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644));
+ if (fd == -1) {
+ ALOGD("error: open %s: %s\n", path, strerror(errno));
+ goto fail;
+ }
+ if (fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0) {
+ ALOGD("error: fchmod %s: %s\n", path, strerror(errno));
+ goto fail;
+ }
+ if (TEMP_FAILURE_RETRY(flock(fd, LOCK_EX | LOCK_NB)) != 0) {
+ ALOGD("error: flock %s: %s\n", path, strerror(errno));
+ goto fail;
+ }
+
+ return fd;
+fail:
+ if (fd != -1) {
+ close(fd);
+ unlink(path);
+ }
+ return -1;
+ }
+
+ int write_idmap(int fd, const uint32_t *data, size_t size)
+ {
+ if (lseek(fd, SEEK_SET, 0) < 0) {
+ return -1;
+ }
+ size_t bytesLeft = size;
+ while (bytesLeft > 0) {
+ ssize_t w = TEMP_FAILURE_RETRY(write(fd, data + size - bytesLeft, bytesLeft));
+ if (w < 0) {
+ fprintf(stderr, "error: write: %s\n", strerror(errno));
+ return -1;
+ }
+ bytesLeft -= w;
+ }
+ return 0;
+ }
+
+ bool is_idmap_stale_fd(const char *target_apk_path, const char *overlay_apk_path, int idmap_fd)
+ {
+ static const size_t N = ResTable::IDMAP_HEADER_SIZE_BYTES;
+ struct stat st;
+ if (fstat(idmap_fd, &st) == -1) {
+ return true;
+ }
+ if (st.st_size < N) {
+ // file is empty or corrupt
+ return true;
+ }
+
+ char buf[N];
+ ssize_t bytesLeft = N;
+ if (lseek(idmap_fd, SEEK_SET, 0) < 0) {
+ return true;
+ }
+ for (;;) {
+ ssize_t r = TEMP_FAILURE_RETRY(read(idmap_fd, buf + N - bytesLeft, bytesLeft));
+ if (r < 0) {
+ return true;
+ }
+ bytesLeft -= r;
+ if (bytesLeft == 0) {
+ break;
+ }
+ if (r == 0) {
+ // "shouldn't happen"
+ return true;
+ }
+ }
+
+ uint32_t cached_target_crc, cached_overlay_crc;
+ String8 cached_target_path, cached_overlay_path;
+ if (!ResTable::getIdmapInfo(buf, N, &cached_target_crc, &cached_overlay_crc,
+ &cached_target_path, &cached_overlay_path)) {
+ return true;
+ }
+
+ if (cached_target_path != target_apk_path) {
+ return true;
+ }
+ if (cached_overlay_path != overlay_apk_path) {
+ return true;
+ }
+
+ uint32_t actual_target_crc, actual_overlay_crc;
+ if (get_zip_entry_crc(target_apk_path, AssetManager::RESOURCES_FILENAME,
+ &actual_target_crc) == -1) {
+ return true;
+ }
+ if (get_zip_entry_crc(overlay_apk_path, AssetManager::RESOURCES_FILENAME,
+ &actual_overlay_crc) == -1) {
+ return true;
+ }
+
+ return cached_target_crc != actual_target_crc || cached_overlay_crc != actual_overlay_crc;
+ }
+
+ bool is_idmap_stale_path(const char *target_apk_path, const char *overlay_apk_path,
+ const char *idmap_path)
+ {
+ struct stat st;
+ if (stat(idmap_path, &st) == -1) {
+ // non-existing idmap is always stale; on other errors, abort idmap generation
+ return errno == ENOENT;
+ }
+
+ int idmap_fd = TEMP_FAILURE_RETRY(open(idmap_path, O_RDONLY));
+ if (idmap_fd == -1) {
+ return false;
+ }
+ bool is_stale = is_idmap_stale_fd(target_apk_path, overlay_apk_path, idmap_fd);
+ close(idmap_fd);
+ return is_stale;
+ }
+
+ int create_idmap(const char *target_apk_path, const char *overlay_apk_path,
+ uint32_t **data, size_t *size)
+ {
+ uint32_t target_crc, overlay_crc;
+ if (get_zip_entry_crc(target_apk_path, AssetManager::RESOURCES_FILENAME,
+ &target_crc) == -1) {
+ return -1;
+ }
+ if (get_zip_entry_crc(overlay_apk_path, AssetManager::RESOURCES_FILENAME,
+ &overlay_crc) == -1) {
+ return -1;
+ }
+
+ AssetManager am;
+ bool b = am.createIdmap(target_apk_path, overlay_apk_path, target_crc, overlay_crc,
+ data, size);
+ return b ? 0 : -1;
+ }
+
+ int create_and_write_idmap(const char *target_apk_path, const char *overlay_apk_path,
+ int fd, bool check_if_stale)
+ {
+ if (check_if_stale) {
+ if (!is_idmap_stale_fd(target_apk_path, overlay_apk_path, fd)) {
+ // already up to date -- nothing to do
+ return 0;
+ }
+ }
+
+ uint32_t *data = NULL;
+ size_t size;
+
+ if (create_idmap(target_apk_path, overlay_apk_path, &data, &size) == -1) {
+ return -1;
+ }
+
+ if (write_idmap(fd, data, size) == -1) {
+ free(data);
+ return -1;
+ }
+
+ free(data);
+ return 0;
+ }
+}
+
+int idmap_create_path(const char *target_apk_path, const char *overlay_apk_path,
+ const char *idmap_path)
+{
+ if (!is_idmap_stale_path(target_apk_path, overlay_apk_path, idmap_path)) {
+ // already up to date -- nothing to do
+ return EXIT_SUCCESS;
+ }
+
+ int fd = open_idmap(idmap_path);
+ if (fd == -1) {
+ return EXIT_FAILURE;
+ }
+
+ int r = create_and_write_idmap(target_apk_path, overlay_apk_path, fd, false);
+ close(fd);
+ if (r != 0) {
+ unlink(idmap_path);
+ }
+ return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+int idmap_create_fd(const char *target_apk_path, const char *overlay_apk_path, int fd)
+{
+ return create_and_write_idmap(target_apk_path, overlay_apk_path, fd, true) == 0 ?
+ EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/cmds/idmap/idmap.cpp b/cmds/idmap/idmap.cpp
new file mode 100644
index 0000000..46c0edc
--- /dev/null
+++ b/cmds/idmap/idmap.cpp
@@ -0,0 +1,237 @@
+#include "idmap.h"
+
+#include <private/android_filesystem_config.h> // for AID_SYSTEM
+
+#include <stdlib.h>
+#include <string.h>
+
+namespace {
+ const char *usage = "NAME\n\
+ idmap - create or display idmap files\n\
+\n\
+SYNOPSIS \n\
+ idmap --help \n\
+ idmap --fd target overlay fd \n\
+ idmap --path target overlay idmap \n\
+ idmap --scan dir-to-scan target-to-look-for target dir-to-hold-idmaps \n\
+ idmap --inspect idmap \n\
+\n\
+DESCRIPTION \n\
+ Idmap files play an integral part in the runtime resource overlay framework. An idmap \n\
+ file contains a mapping of resource identifiers between overlay package and its target \n\
+ package; this mapping is used during resource lookup. Idmap files also act as control \n\
+ files by their existence: if not present, the corresponding overlay package is ignored \n\
+ when the resource context is created. \n\
+\n\
+ Idmap files are stored in /data/resource-cache. For each pair (target package, overlay \n\
+ package), there exists exactly one idmap file, or none if the overlay should not be used. \n\
+\n\
+NOMENCLATURE \n\
+ target: the original, non-overlay, package. Each target package may be associated with \n\
+ any number of overlay packages. \n\
+\n\
+ overlay: an overlay package. Each overlay package is associated with exactly one target \n\
+ package, specified in the overlay's manifest using the <overlay target=\"...\"/> \n\
+ tag. \n\
+\n\
+OPTIONS \n\
+ --help: display this help \n\
+\n\
+ --fd: create idmap for target package 'target' (path to apk) and overlay package 'overlay' \n\
+ (path to apk); write results to file descriptor 'fd' (integer). This invocation \n\
+ version is intended to be used by a parent process with higher privileges to call \n\
+ idmap in a controlled way: the parent will open a suitable file descriptor, fork, \n\
+ drop its privileges and exec. This tool will continue execution without the extra \n\
+ privileges, but still have write access to a file it could not have opened on its \n\
+ own. \n\
+\n\
+ --path: create idmap for target package 'target' (path to apk) and overlay package \n\
+ 'overlay' (path to apk); write results to 'idmap' (path). \n\
+\n\
+ --scan: non-recursively search directory 'dir-to-scan' (path) for overlay packages with \n\
+ target package 'target-to-look-for' (package name) present at 'target' (path to \n\
+ apk). For each overlay package found, create an idmap file in 'dir-to-hold-idmaps' \n\
+ (path). \n\
+\n\
+ --inspect: decode the binary format of 'idmap' (path) and display the contents in a \n\
+ debug-friendly format. \n\
+\n\
+EXAMPLES \n\
+ Create an idmap file: \n\
+\n\
+ $ adb shell idmap --path /system/app/target.apk \\ \n\
+ /vendor/overlay/overlay.apk \\ \n\
+ /data/resource-cache/vendor@overlay@overlay.apk@idmap \n\
+\n\
+ Display an idmap file: \n\
+\n\
+ $ adb shell idmap --inspect /data/resource-cache/vendor@overlay@overlay.apk@idmap \n\
+ SECTION ENTRY VALUE OFFSET COMMENT \n\
+ IDMAP HEADER magic 0x706d6469 0x0 \n\
+ base crc 0x484aa77f 0x1 \n\
+ overlay crc 0x03c66fa5 0x2 \n\
+ base path .......... 0x03-0x42 /system/app/target.apk \n\
+ overlay path .......... 0x43-0x82 /vendor/overlay/overlay.apk \n\
+ DATA HEADER types count 0x00000003 0x83 \n\
+ padding 0x00000000 0x84 \n\
+ type offset 0x00000004 0x85 absolute offset 0x87, xml \n\
+ type offset 0x00000007 0x86 absolute offset 0x8a, string \n\
+ DATA BLOCK entry count 0x00000001 0x87 \n\
+ entry offset 0x00000000 0x88 \n\
+ entry 0x7f020000 0x89 xml/integer \n\
+ DATA BLOCK entry count 0x00000002 0x8a \n\
+ entry offset 0x00000000 0x8b \n\
+ entry 0x7f030000 0x8c string/str \n\
+ entry 0x7f030001 0x8d string/str2 \n\
+\n\
+ In this example, the overlay package provides three alternative resource values:\n\
+ xml/integer, string/str and string/str2.\n\
+\n\
+NOTES \n\
+ This tool and its expected invocation from installd is modelled on dexopt.";
+
+ bool verify_directory_readable(const char *path)
+ {
+ return access(path, R_OK | X_OK) == 0;
+ }
+
+ bool verify_directory_writable(const char *path)
+ {
+ return access(path, W_OK) == 0;
+ }
+
+ bool verify_file_readable(const char *path)
+ {
+ return access(path, R_OK) == 0;
+ }
+
+ bool verify_root_or_system()
+ {
+ uid_t uid = getuid();
+ gid_t gid = getgid();
+
+ return (uid == 0 && gid == 0) || (uid == AID_SYSTEM && gid == AID_SYSTEM);
+ }
+
+ int maybe_create_fd(const char *target_apk_path, const char *overlay_apk_path,
+ const char *idmap_str)
+ {
+ // anyone (not just root or system) may do --fd -- the file has
+ // already been opened by someone else on our behalf
+
+ char *endptr;
+ int idmap_fd = strtol(idmap_str, &endptr, 10);
+ if (*endptr != '\0') {
+ fprintf(stderr, "error: failed to parse file descriptor argument %s\n", idmap_str);
+ return -1;
+ }
+
+ if (!verify_file_readable(target_apk_path)) {
+ ALOGD("error: failed to read apk %s: %s\n", target_apk_path, strerror(errno));
+ return -1;
+ }
+
+ if (!verify_file_readable(overlay_apk_path)) {
+ ALOGD("error: failed to read apk %s: %s\n", overlay_apk_path, strerror(errno));
+ return -1;
+ }
+
+ return idmap_create_fd(target_apk_path, overlay_apk_path, idmap_fd);
+ }
+
+ int maybe_create_path(const char *target_apk_path, const char *overlay_apk_path,
+ const char *idmap_path)
+ {
+ if (!verify_root_or_system()) {
+ fprintf(stderr, "error: permission denied: not user root or user system\n");
+ return -1;
+ }
+
+ if (!verify_file_readable(target_apk_path)) {
+ ALOGD("error: failed to read apk %s: %s\n", target_apk_path, strerror(errno));
+ return -1;
+ }
+
+ if (!verify_file_readable(overlay_apk_path)) {
+ ALOGD("error: failed to read apk %s: %s\n", overlay_apk_path, strerror(errno));
+ return -1;
+ }
+
+ return idmap_create_path(target_apk_path, overlay_apk_path, idmap_path);
+ }
+
+ int maybe_scan(const char *overlay_dir, const char *target_package_name,
+ const char *target_apk_path, const char *idmap_dir)
+ {
+ if (!verify_root_or_system()) {
+ fprintf(stderr, "error: permission denied: not user root or user system\n");
+ return -1;
+ }
+
+ if (!verify_directory_readable(overlay_dir)) {
+ ALOGD("error: no read access to %s: %s\n", overlay_dir, strerror(errno));
+ return -1;
+ }
+
+ if (!verify_file_readable(target_apk_path)) {
+ ALOGD("error: failed to read apk %s: %s\n", target_apk_path, strerror(errno));
+ return -1;
+ }
+
+ if (!verify_directory_writable(idmap_dir)) {
+ ALOGD("error: no write access to %s: %s\n", idmap_dir, strerror(errno));
+ return -1;
+ }
+
+ return idmap_scan(overlay_dir, target_package_name, target_apk_path, idmap_dir);
+ }
+
+ int maybe_inspect(const char *idmap_path)
+ {
+ // anyone (not just root or system) may do --inspect
+ if (!verify_file_readable(idmap_path)) {
+ ALOGD("error: failed to read idmap %s: %s\n", idmap_path, strerror(errno));
+ return -1;
+ }
+ return idmap_inspect(idmap_path);
+ }
+}
+
+int main(int argc, char **argv)
+{
+#if 0
+ {
+ char buf[1024];
+ buf[0] = '\0';
+ for (int i = 0; i < argc; ++i) {
+ strncat(buf, argv[i], sizeof(buf) - 1);
+ strncat(buf, " ", sizeof(buf) - 1);
+ }
+ ALOGD("%s:%d: uid=%d gid=%d argv=%s\n", __FILE__, __LINE__, getuid(), getgid(), buf);
+ }
+#endif
+
+ if (argc == 2 && !strcmp(argv[1], "--help")) {
+ printf("%s\n", usage);
+ return 0;
+ }
+
+ if (argc == 5 && !strcmp(argv[1], "--fd")) {
+ return maybe_create_fd(argv[2], argv[3], argv[4]);
+ }
+
+ if (argc == 5 && !strcmp(argv[1], "--path")) {
+ return maybe_create_path(argv[2], argv[3], argv[4]);
+ }
+
+ if (argc == 6 && !strcmp(argv[1], "--scan")) {
+ return maybe_scan(argv[2], argv[3], argv[4], argv[5]);
+ }
+
+ if (argc == 3 && !strcmp(argv[1], "--inspect")) {
+ return maybe_inspect(argv[2]);
+ }
+
+ fprintf(stderr, "Usage: don't use this (cf dexopt usage).\n");
+ return EXIT_FAILURE;
+}
diff --git a/cmds/idmap/idmap.h b/cmds/idmap/idmap.h
new file mode 100644
index 0000000..f507dd8
--- /dev/null
+++ b/cmds/idmap/idmap.h
@@ -0,0 +1,34 @@
+#ifndef _IDMAP_H_
+#define _IDMAP_H_
+
+#define LOG_TAG "idmap"
+
+#include <utils/Log.h>
+
+#include <errno.h>
+#include <stdio.h>
+
+#ifndef TEMP_FAILURE_RETRY
+// Used to retry syscalls that can return EINTR.
+#define TEMP_FAILURE_RETRY(exp) ({ \
+ typeof (exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (_rc == -1 && errno == EINTR); \
+ _rc; })
+#endif
+
+int idmap_create_path(const char *target_apk_path, const char *overlay_apk_path,
+ const char *idmap_path);
+
+int idmap_create_fd(const char *target_apk_path, const char *overlay_apk_path, int fd);
+
+// Regarding target_package_name: the idmap_scan implementation should
+// be able to extract this from the manifest in target_apk_path,
+// simplifying the external API.
+int idmap_scan(const char *overlay_dir, const char *target_package_name,
+ const char *target_apk_path, const char *idmap_dir);
+
+int idmap_inspect(const char *idmap_path);
+
+#endif // _IDMAP_H_
diff --git a/cmds/idmap/inspect.cpp b/cmds/idmap/inspect.cpp
new file mode 100644
index 0000000..a59f5d3
--- /dev/null
+++ b/cmds/idmap/inspect.cpp
@@ -0,0 +1,291 @@
+#include "idmap.h"
+
+#include <androidfw/AssetManager.h>
+#include <androidfw/ResourceTypes.h>
+#include <utils/String8.h>
+
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+using namespace android;
+
+#define NEXT(b, i, o) do { if (buf.next(&i, &o) < 0) { return -1; } } while (0)
+
+namespace {
+ static const uint32_t IDMAP_MAGIC = 0x706d6469;
+ static const size_t PATH_LENGTH = 256;
+ static const uint32_t IDMAP_HEADER_SIZE = (3 + 2 * (PATH_LENGTH / sizeof(uint32_t)));
+
+ void printe(const char *fmt, ...);
+
+ class IdmapBuffer {
+ private:
+ char *buf_;
+ size_t len_;
+ mutable size_t pos_;
+ public:
+ IdmapBuffer() : buf_((char *)MAP_FAILED), len_(0), pos_(0) {}
+
+ ~IdmapBuffer() {
+ if (buf_ != MAP_FAILED) {
+ munmap(buf_, len_);
+ }
+ }
+
+ int init(const char *idmap_path)
+ {
+ struct stat st;
+ int fd;
+
+ if (stat(idmap_path, &st) < 0) {
+ printe("failed to stat idmap '%s': %s\n", idmap_path, strerror(errno));
+ return -1;
+ }
+ len_ = st.st_size;
+ if ((fd = TEMP_FAILURE_RETRY(open(idmap_path, O_RDONLY))) < 0) {
+ printe("failed to open idmap '%s': %s\n", idmap_path, strerror(errno));
+ return -1;
+ }
+ if ((buf_ = (char*)mmap(NULL, len_, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
+ close(fd);
+ printe("failed to mmap idmap: %s\n", strerror(errno));
+ return -1;
+ }
+ close(fd);
+ return 0;
+ }
+
+ int next(uint32_t *i, uint32_t *offset) const
+ {
+ if (!buf_) {
+ printe("failed to read next uint32_t: buffer not initialized\n");
+ return -1;
+ }
+ if (pos_ + 4 > len_) {
+ printe("failed to read next uint32_t: end of buffer reached at pos=0x%08x\n",
+ pos_);
+ return -1;
+ }
+ *offset = pos_ / sizeof(uint32_t);
+ char a = buf_[pos_++];
+ char b = buf_[pos_++];
+ char c = buf_[pos_++];
+ char d = buf_[pos_++];
+ *i = (d << 24) | (c << 16) | (b << 8) | a;
+ return 0;
+ }
+
+ int nextPath(char *b, uint32_t *offset_start, uint32_t *offset_end) const
+ {
+ if (!buf_) {
+ printe("failed to read next path: buffer not initialized\n");
+ return -1;
+ }
+ if (pos_ + PATH_LENGTH > len_) {
+ printe("failed to read next path: end of buffer reached at pos=0x%08x\n", pos_);
+ return -1;
+ }
+ memcpy(b, buf_ + pos_, PATH_LENGTH);
+ *offset_start = pos_ / sizeof(uint32_t);
+ pos_ += PATH_LENGTH;
+ *offset_end = pos_ / sizeof(uint32_t) - 1;
+ return 0;
+ }
+ };
+
+ void printe(const char *fmt, ...)
+ {
+ va_list ap;
+
+ va_start(ap, fmt);
+ fprintf(stderr, "error: ");
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ }
+
+ void print_header()
+ {
+ printf("SECTION ENTRY VALUE OFFSET COMMENT\n");
+ }
+
+ void print(const char *section, const char *subsection, uint32_t value, uint32_t offset,
+ const char *fmt, ...)
+ {
+ va_list ap;
+
+ va_start(ap, fmt);
+ printf("%-12s %-12s 0x%08x 0x%-4x ", section, subsection, value, offset);
+ vprintf(fmt, ap);
+ printf("\n");
+ va_end(ap);
+ }
+
+ void print_path(const char *section, const char *subsection, uint32_t offset_start,
+ uint32_t offset_end, const char *fmt, ...)
+ {
+ va_list ap;
+
+ va_start(ap, fmt);
+ printf("%-12s %-12s .......... 0x%02x-0x%02x ", section, subsection, offset_start,
+ offset_end);
+ vprintf(fmt, ap);
+ printf("\n");
+ va_end(ap);
+ }
+
+ int resource_metadata(const AssetManager& am, uint32_t res_id,
+ String8 *package, String8 *type, String8 *name)
+ {
+ const ResTable& rt = am.getResources();
+ struct ResTable::resource_name data;
+ if (!rt.getResourceName(res_id, false, &data)) {
+ printe("failed to get resource name id=0x%08x\n", res_id);
+ return -1;
+ }
+ if (package) {
+ *package = String8(String16(data.package, data.packageLen));
+ }
+ if (type) {
+ *type = String8(String16(data.type, data.typeLen));
+ }
+ if (name) {
+ *name = String8(String16(data.name, data.nameLen));
+ }
+ return 0;
+ }
+
+ int package_id(const AssetManager& am)
+ {
+ return (am.getResources().getBasePackageId(0)) << 24;
+ }
+
+ int parse_idmap_header(const IdmapBuffer& buf, AssetManager& am)
+ {
+ uint32_t i, o, e;
+ char path[PATH_LENGTH];
+
+ NEXT(buf, i, o);
+ if (i != IDMAP_MAGIC) {
+ printe("not an idmap file: actual magic constant 0x%08x does not match expected magic "
+ "constant 0x%08x\n", i, IDMAP_MAGIC);
+ return -1;
+ }
+ print_header();
+ print("IDMAP HEADER", "magic", i, o, "");
+
+ NEXT(buf, i, o);
+ print("", "base crc", i, o, "");
+
+ NEXT(buf, i, o);
+ print("", "overlay crc", i, o, "");
+
+ if (buf.nextPath(path, &o, &e) < 0) {
+ // printe done from IdmapBuffer::nextPath
+ return -1;
+ }
+ print_path("", "base path", o, e, "%s", path);
+ if (!am.addAssetPath(String8(path), NULL)) {
+ printe("failed to add '%s' as asset path\n", path);
+ return -1;
+ }
+
+ if (buf.nextPath(path, &o, &e) < 0) {
+ // printe done from IdmapBuffer::nextPath
+ return -1;
+ }
+ print_path("", "overlay path", o, e, "%s", path);
+
+ return 0;
+ }
+
+ int parse_data_header(const IdmapBuffer& buf, const AssetManager& am, Vector<uint32_t>& types)
+ {
+ uint32_t i, o;
+ const uint32_t numeric_package = package_id(am);
+
+ NEXT(buf, i, o);
+ print("DATA HEADER", "types count", i, o, "");
+ const uint32_t N = i;
+
+ for (uint32_t j = 0; j < N; ++j) {
+ NEXT(buf, i, o);
+ if (i == 0) {
+ print("", "padding", i, o, "");
+ } else {
+ String8 type;
+ const uint32_t numeric_type = (j + 1) << 16;
+ const uint32_t res_id = numeric_package | numeric_type;
+ if (resource_metadata(am, res_id, NULL, &type, NULL) < 0) {
+ // printe done from resource_metadata
+ return -1;
+ }
+ print("", "type offset", i, o, "absolute offset 0x%02x, %s",
+ i + IDMAP_HEADER_SIZE, type.string());
+ types.add(numeric_type);
+ }
+ }
+
+ return 0;
+ }
+
+ int parse_data_block(const IdmapBuffer& buf, const AssetManager& am, size_t numeric_type)
+ {
+ uint32_t i, o, n, id_offset;
+ const uint32_t numeric_package = package_id(am);
+
+ NEXT(buf, i, o);
+ print("DATA BLOCK", "entry count", i, o, "");
+ n = i;
+
+ NEXT(buf, i, o);
+ print("", "entry offset", i, o, "");
+ id_offset = i;
+
+ for ( ; n > 0; --n) {
+ String8 type, name;
+
+ NEXT(buf, i, o);
+ if (i == 0) {
+ print("", "padding", i, o, "");
+ } else {
+ uint32_t res_id = numeric_package | numeric_type | id_offset;
+ if (resource_metadata(am, res_id, NULL, &type, &name) < 0) {
+ // printe done from resource_metadata
+ return -1;
+ }
+ print("", "entry", i, o, "%s/%s", type.string(), name.string());
+ }
+ ++id_offset;
+ }
+
+ return 0;
+ }
+}
+
+int idmap_inspect(const char *idmap_path)
+{
+ IdmapBuffer buf;
+ if (buf.init(idmap_path) < 0) {
+ // printe done from IdmapBuffer::init
+ return EXIT_FAILURE;
+ }
+ AssetManager am;
+ if (parse_idmap_header(buf, am) < 0) {
+ // printe done from parse_idmap_header
+ return EXIT_FAILURE;
+ }
+ Vector<uint32_t> types;
+ if (parse_data_header(buf, am, types) < 0) {
+ // printe done from parse_data_header
+ return EXIT_FAILURE;
+ }
+ const size_t N = types.size();
+ for (size_t i = 0; i < N; ++i) {
+ if (parse_data_block(buf, am, types.itemAt(i)) < 0) {
+ // printe done from parse_data_block
+ return EXIT_FAILURE;
+ }
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/cmds/idmap/scan.cpp b/cmds/idmap/scan.cpp
new file mode 100644
index 0000000..c5fc941
--- /dev/null
+++ b/cmds/idmap/scan.cpp
@@ -0,0 +1,244 @@
+#include "idmap.h"
+
+#include <UniquePtr.h>
+#include <androidfw/ResourceTypes.h>
+#include <androidfw/StreamingZipInflater.h>
+#include <androidfw/ZipFileRO.h>
+#include <private/android_filesystem_config.h> // for AID_SYSTEM
+#include <utils/SortedVector.h>
+#include <utils/String16.h>
+#include <utils/String8.h>
+
+#include <dirent.h>
+
+#define NO_OVERLAY_TAG (-1000)
+
+using namespace android;
+
+namespace {
+ struct Overlay {
+ Overlay() {}
+ Overlay(const String8& a, const String8& i, int p) :
+ apk_path(a), idmap_path(i), priority(p) {}
+
+ bool operator<(Overlay const& rhs) const
+ {
+ // Note: order is reversed by design
+ return rhs.priority < priority;
+ }
+
+ String8 apk_path;
+ String8 idmap_path;
+ int priority;
+ };
+
+ bool writePackagesList(const char *filename, const SortedVector<Overlay>& overlayVector)
+ {
+ FILE* fout = fopen(filename, "w");
+ if (fout == NULL) {
+ return false;
+ }
+
+ for (size_t i = 0; i < overlayVector.size(); ++i) {
+ const Overlay& overlay = overlayVector[i];
+ fprintf(fout, "%s %s\n", overlay.apk_path.string(), overlay.idmap_path.string());
+ }
+
+ fclose(fout);
+
+ // Make file world readable since Zygote (running as root) will read
+ // it when creating the initial AssetManger object
+ const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; // 0644
+ if (chmod(filename, mode) == -1) {
+ unlink(filename);
+ return false;
+ }
+
+ return true;
+ }
+
+ String8 flatten_path(const char *path)
+ {
+ String16 tmp(path);
+ tmp.replaceAll('/', '@');
+ return String8(tmp);
+ }
+
+ int mkdir_p(const String8& path, uid_t uid, gid_t gid)
+ {
+ static const mode_t mode =
+ S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH;
+ struct stat st;
+
+ if (stat(path.string(), &st) == 0) {
+ return 0;
+ }
+ if (mkdir_p(path.getPathDir(), uid, gid) < 0) {
+ return -1;
+ }
+ if (mkdir(path.string(), 0755) != 0) {
+ return -1;
+ }
+ if (chown(path.string(), uid, gid) == -1) {
+ return -1;
+ }
+ if (chmod(path.string(), mode) == -1) {
+ return -1;
+ }
+ return 0;
+ }
+
+ int parse_overlay_tag(const ResXMLTree& parser, const char *target_package_name)
+ {
+ const size_t N = parser.getAttributeCount();
+ String16 target;
+ int priority = -1;
+ for (size_t i = 0; i < N; ++i) {
+ size_t len;
+ String16 key(parser.getAttributeName(i, &len));
+ if (key == String16("targetPackage")) {
+ const uint16_t *p = parser.getAttributeStringValue(i, &len);
+ if (p) {
+ target = String16(p, len);
+ }
+ } else if (key == String16("priority")) {
+ Res_value v;
+ if (parser.getAttributeValue(i, &v) == sizeof(Res_value)) {
+ priority = v.data;
+ if (priority < 0 || priority > 9999) {
+ return -1;
+ }
+ }
+ }
+ }
+ if (target == String16(target_package_name)) {
+ return priority;
+ }
+ return NO_OVERLAY_TAG;
+ }
+
+ int parse_manifest(const void *data, size_t size, const char *target_package_name)
+ {
+ ResXMLTree parser(data, size);
+ if (parser.getError() != NO_ERROR) {
+ ALOGD("%s failed to init xml parser, error=0x%08x\n", __FUNCTION__, parser.getError());
+ return -1;
+ }
+
+ ResXMLParser::event_code_t type;
+ do {
+ type = parser.next();
+ if (type == ResXMLParser::START_TAG) {
+ size_t len;
+ String16 tag(parser.getElementName(&len));
+ if (tag == String16("overlay")) {
+ return parse_overlay_tag(parser, target_package_name);
+ }
+ }
+ } while (type != ResXMLParser::BAD_DOCUMENT && type != ResXMLParser::END_DOCUMENT);
+
+ return NO_OVERLAY_TAG;
+ }
+
+ int parse_apk(const char *path, const char *target_package_name)
+ {
+ UniquePtr<ZipFileRO> zip(ZipFileRO::open(path));
+ if (zip.get() == NULL) {
+ ALOGW("%s: failed to open zip %s\n", __FUNCTION__, path);
+ return -1;
+ }
+ ZipEntryRO entry;
+ if ((entry = zip->findEntryByName("AndroidManifest.xml")) == NULL) {
+ ALOGW("%s: failed to find entry AndroidManifest.xml\n", __FUNCTION__);
+ return -1;
+ }
+ size_t uncompLen = 0;
+ int method;
+ if (!zip->getEntryInfo(entry, &method, &uncompLen, NULL, NULL, NULL, NULL)) {
+ ALOGW("%s: failed to read entry info\n", __FUNCTION__);
+ return -1;
+ }
+ if (method != ZipFileRO::kCompressDeflated) {
+ ALOGW("%s: cannot handle zip compression method %d\n", __FUNCTION__, method);
+ return -1;
+ }
+ FileMap *dataMap = zip->createEntryFileMap(entry);
+ if (!dataMap) {
+ ALOGW("%s: failed to create FileMap\n", __FUNCTION__);
+ return -1;
+ }
+ char *buf = new char[uncompLen];
+ if (NULL == buf) {
+ ALOGW("%s: failed to allocate %d byte\n", __FUNCTION__, uncompLen);
+ dataMap->release();
+ return -1;
+ }
+ StreamingZipInflater inflater(dataMap, uncompLen);
+ if (inflater.read(buf, uncompLen) < 0) {
+ ALOGW("%s: failed to inflate %d byte\n", __FUNCTION__, uncompLen);
+ delete[] buf;
+ dataMap->release();
+ return -1;
+ }
+
+ int priority = parse_manifest(buf, uncompLen, target_package_name);
+ delete[] buf;
+ dataMap->release();
+ return priority;
+ }
+}
+
+int idmap_scan(const char *overlay_dir, const char *target_package_name,
+ const char *target_apk_path, const char *idmap_dir)
+{
+ String8 filename = String8(idmap_dir);
+ filename.appendPath("overlays.list");
+ if (unlink(filename.string()) != 0 && errno != ENOENT) {
+ return EXIT_FAILURE;
+ }
+
+ DIR *dir = opendir(overlay_dir);
+ if (dir == NULL) {
+ return EXIT_FAILURE;
+ }
+
+ SortedVector<Overlay> overlayVector;
+ struct dirent *dirent;
+ while ((dirent = readdir(dir)) != NULL) {
+ struct stat st;
+ char overlay_apk_path[PATH_MAX + 1];
+ snprintf(overlay_apk_path, PATH_MAX, "%s/%s", overlay_dir, dirent->d_name);
+ if (stat(overlay_apk_path, &st) < 0) {
+ continue;
+ }
+ if (!S_ISREG(st.st_mode)) {
+ continue;
+ }
+
+ int priority = parse_apk(overlay_apk_path, target_package_name);
+ if (priority < 0) {
+ continue;
+ }
+
+ String8 idmap_path(idmap_dir);
+ idmap_path.appendPath(flatten_path(overlay_apk_path + 1));
+ idmap_path.append("@idmap");
+
+ if (idmap_create_path(target_apk_path, overlay_apk_path, idmap_path.string()) != 0) {
+ ALOGE("error: failed to create idmap for target=%s overlay=%s idmap=%s\n",
+ target_apk_path, overlay_apk_path, idmap_path.string());
+ continue;
+ }
+
+ Overlay overlay(String8(overlay_apk_path), idmap_path, priority);
+ overlayVector.add(overlay);
+ }
+
+ closedir(dir);
+
+ if (!writePackagesList(filename.string(), overlayVector)) {
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index d1ded10..9ad2e76 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -39,6 +39,7 @@
import android.content.res.Resources;
import android.net.Uri;
import android.os.IUserManager;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -1011,6 +1012,27 @@
public void runCreateUser() {
String name;
+ int relatedUserId = -1;
+ int flags = 0;
+ String opt;
+ while ((opt = nextOption()) != null) {
+ if ("--relatedTo".equals(opt)) {
+ String optionData = nextOptionData();
+ if (optionData == null || !isNumber(optionData)) {
+ System.err.println("Error: no USER_ID specified");
+ showUsage();
+ return;
+ } else {
+ relatedUserId = Integer.parseInt(optionData);
+ }
+ } else if ("--managed".equals(opt)) {
+ flags |= UserInfo.FLAG_MANAGED_PROFILE;
+ } else {
+ System.err.println("Error: unknown option " + opt);
+ showUsage();
+ return;
+ }
+ }
String arg = nextArg();
if (arg == null) {
System.err.println("Error: no user name specified.");
@@ -1018,7 +1040,16 @@
}
name = arg;
try {
- final UserInfo info = mUm.createUser(name, 0);
+ UserInfo info = null;
+ if (relatedUserId < 0) {
+ info = mUm.createUser(name, flags);
+ } else {
+ if (Process.myUid() != 0) {
+ System.err.println("Error: not running as root.");
+ return;
+ }
+ info = mUm.createRelatedUser(name, flags, relatedUserId);
+ }
if (info != null) {
System.out.println("Success: created user id " + info.id);
} else {
@@ -1530,7 +1561,7 @@
System.err.println(" pm get-install-location");
System.err.println(" pm set-permission-enforced PERMISSION [true|false]");
System.err.println(" pm trim-caches DESIRED_FREE_SPACE");
- System.err.println(" pm create-user USER_NAME");
+ System.err.println(" pm create-user [--relatedTo USER_ID] [--managed] USER_NAME");
System.err.println(" pm remove-user USER_ID");
System.err.println(" pm get-max-users");
System.err.println("");
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index bf2a629..ee8d457 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -972,7 +972,7 @@
if (fragment.mAnimatingAway != null) {
fragment.mAnimatingAway = null;
moveToState(fragment, fragment.mStateAfterAnimating,
- 0, 0, false);
+ 0, 0, true);
}
}
});
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index 51ba2f6..b97734e 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -642,12 +642,13 @@
return _result;
}
- public int changeEncryptionPassword(String password) throws RemoteException {
+ public int changeEncryptionPassword(int type, String password) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeInt(type);
_data.writeString(password);
mRemote.transact(Stub.TRANSACTION_changeEncryptionPassword, _data, _reply, 0);
_reply.readException();
@@ -677,6 +678,22 @@
return _result;
}
+ public int getPasswordType() throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ int _result;
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ mRemote.transact(Stub.TRANSACTION_getPasswordType, _data, _reply, 0);
+ _reply.readException();
+ _result = _reply.readInt();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ return _result;
+ }
+
public StorageVolume[] getVolumeList() throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
@@ -829,6 +846,8 @@
static final int TRANSACTION_mkdirs = IBinder.FIRST_CALL_TRANSACTION + 34;
+ static final int TRANSACTION_getPasswordType = IBinder.FIRST_CALL_TRANSACTION + 36;
+
/**
* Cast an IBinder object into an IMountService interface, generating a
* proxy if needed.
@@ -1130,8 +1149,9 @@
}
case TRANSACTION_changeEncryptionPassword: {
data.enforceInterface(DESCRIPTOR);
+ int type = data.readInt();
String password = data.readString();
- int result = changeEncryptionPassword(password);
+ int result = changeEncryptionPassword(type, password);
reply.writeNoException();
reply.writeInt(result);
return true;
@@ -1181,6 +1201,13 @@
reply.writeInt(result);
return true;
}
+ case TRANSACTION_getPasswordType: {
+ data.enforceInterface(DESCRIPTOR);
+ int result = getPasswordType();
+ reply.writeNoException();
+ reply.writeInt(result);
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
}
@@ -1375,7 +1402,8 @@
/**
* Changes the encryption password.
*/
- public int changeEncryptionPassword(String password) throws RemoteException;
+ public int changeEncryptionPassword(int type, String password)
+ throws RemoteException;
/**
* Verify the encryption password against the stored volume. This method
@@ -1412,4 +1440,10 @@
* external storage data or OBB directory belonging to calling app.
*/
public int mkdirs(String callingPkg, String path) throws RemoteException;
+
+ /**
+ * Determines the type of the encryption password
+ * @return PasswordType
+ */
+ public int getPasswordType() throws RemoteException;
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index f5e728d..68b91cb 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -645,4 +645,14 @@
return Settings.Global.getLong(mResolver, Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES,
DEFAULT_FULL_THRESHOLD_BYTES);
}
+
+ /// Consts to match the password types in cryptfs.h
+ /** @hide */
+ public static final int CRYPT_TYPE_PASSWORD = 0;
+ /** @hide */
+ public static final int CRYPT_TYPE_DEFAULT = 1;
+ /** @hide */
+ public static final int CRYPT_TYPE_PATTERN = 2;
+ /** @hide */
+ public static final int CRYPT_TYPE_PIN = 3;
}
diff --git a/core/java/android/service/dreams/DreamManagerInternal.java b/core/java/android/service/dreams/DreamManagerInternal.java
new file mode 100644
index 0000000..17ea996
--- /dev/null
+++ b/core/java/android/service/dreams/DreamManagerInternal.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.dreams;
+
+/**
+ * Dream manager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class DreamManagerInternal {
+ /**
+ * Called by the power manager to start a dream.
+ */
+ public abstract void startDream();
+
+ /**
+ * Called by the power manager to stop a dream.
+ */
+ public abstract void stopDream();
+
+ /**
+ * Called by the power manager to determine whether a dream is running.
+ */
+ public abstract boolean isDreaming();
+}
diff --git a/core/java/android/view/SurfaceSession.java b/core/java/android/view/SurfaceSession.java
index 0dfd94a..3cf5af4 100644
--- a/core/java/android/view/SurfaceSession.java
+++ b/core/java/android/view/SurfaceSession.java
@@ -24,11 +24,11 @@
*/
public final class SurfaceSession {
// Note: This field is accessed by native code.
- private int mNativeClient; // SurfaceComposerClient*
+ private long mNativeClient; // SurfaceComposerClient*
- private static native int nativeCreate();
- private static native void nativeDestroy(int ptr);
- private static native void nativeKill(int ptr);
+ private static native long nativeCreate();
+ private static native void nativeDestroy(long ptr);
+ private static native void nativeKill(long ptr);
/** Create a new connection with the surface flinger. */
public SurfaceSession() {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index ef69948..11030d9 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -232,8 +232,6 @@
InputStage mFirstInputStage;
InputStage mFirstPostImeInputStage;
- boolean mFlipControllerFallbackKeys;
-
boolean mWindowAttributesChanged = false;
int mWindowAttributesChangesFlag = 0;
@@ -370,8 +368,6 @@
mNoncompatDensity = context.getResources().getDisplayMetrics().noncompatDensityDpi;
mFallbackEventHandler = PolicyManager.makeNewFallbackEventHandler(context);
mChoreographer = Choreographer.getInstance();
- mFlipControllerFallbackKeys =
- context.getResources().getBoolean(R.bool.flip_controller_fallback_keys);
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mAttachInfo.mScreenOn = powerManager.isScreenOn();
@@ -2930,9 +2926,6 @@
mView.dispatchConfigurationChanged(config);
}
}
-
- mFlipControllerFallbackKeys =
- mContext.getResources().getBoolean(R.bool.flip_controller_fallback_keys);
}
/**
@@ -4001,7 +3994,6 @@
private final SyntheticJoystickHandler mJoystick = new SyntheticJoystickHandler();
private final SyntheticTouchNavigationHandler mTouchNavigation =
new SyntheticTouchNavigationHandler();
- private final SyntheticKeyHandler mKeys = new SyntheticKeyHandler();
public SyntheticInputStage() {
super(null);
@@ -4024,12 +4016,7 @@
mTouchNavigation.process(event);
return FINISH_HANDLED;
}
- } else if (q.mEvent instanceof KeyEvent) {
- if (mKeys.process((KeyEvent) q.mEvent)) {
- return FINISH_HANDLED;
- }
}
-
return FORWARD;
}
@@ -4854,71 +4841,6 @@
};
}
- private KeyEvent getSyntheticFallbackKey(KeyEvent event) {
- // In some locales (like Japan) controllers use B for confirm and A for back, rather
- // than vice versa, so we need to special case this here since the input system itself
- // is not locale-aware.
- int keyCode;
- switch(event.getKeyCode()) {
- case KeyEvent.KEYCODE_BUTTON_A:
- case KeyEvent.KEYCODE_BUTTON_C:
- case KeyEvent.KEYCODE_BUTTON_X:
- case KeyEvent.KEYCODE_BUTTON_Z:
- keyCode = mFlipControllerFallbackKeys ?
- KeyEvent.KEYCODE_BACK : KeyEvent.KEYCODE_DPAD_CENTER;
- break;
- case KeyEvent.KEYCODE_BUTTON_B:
- case KeyEvent.KEYCODE_BUTTON_Y:
- keyCode = mFlipControllerFallbackKeys ?
- KeyEvent.KEYCODE_DPAD_CENTER : KeyEvent.KEYCODE_BACK;
- break;
- case KeyEvent.KEYCODE_BUTTON_THUMBL:
- case KeyEvent.KEYCODE_BUTTON_THUMBR:
- case KeyEvent.KEYCODE_BUTTON_START:
- case KeyEvent.KEYCODE_BUTTON_1:
- case KeyEvent.KEYCODE_BUTTON_2:
- case KeyEvent.KEYCODE_BUTTON_3:
- case KeyEvent.KEYCODE_BUTTON_4:
- case KeyEvent.KEYCODE_BUTTON_5:
- case KeyEvent.KEYCODE_BUTTON_6:
- case KeyEvent.KEYCODE_BUTTON_7:
- case KeyEvent.KEYCODE_BUTTON_8:
- case KeyEvent.KEYCODE_BUTTON_9:
- case KeyEvent.KEYCODE_BUTTON_10:
- case KeyEvent.KEYCODE_BUTTON_11:
- case KeyEvent.KEYCODE_BUTTON_12:
- case KeyEvent.KEYCODE_BUTTON_13:
- case KeyEvent.KEYCODE_BUTTON_14:
- case KeyEvent.KEYCODE_BUTTON_15:
- case KeyEvent.KEYCODE_BUTTON_16:
- keyCode = KeyEvent.KEYCODE_DPAD_CENTER;
- break;
- case KeyEvent.KEYCODE_BUTTON_SELECT:
- case KeyEvent.KEYCODE_BUTTON_MODE:
- keyCode = KeyEvent.KEYCODE_MENU;
- default:
- return null;
- }
- return KeyEvent.obtain(event.getDownTime(), event.getEventTime(),
- event.getAction(), keyCode, event.getRepeatCount(), event.getMetaState(),
- event.getDeviceId(), event.getScanCode(),
- event.getFlags() | KeyEvent.FLAG_FALLBACK, event.getSource(), null);
- }
-
-
- final class SyntheticKeyHandler {
-
- public boolean process(KeyEvent event) {
- KeyEvent syntheticKey = getSyntheticFallbackKey(event);
- if (syntheticKey != null) {
- enqueueInputEvent(syntheticKey);
- return true;
- }
- return false;
- }
-
- }
-
/**
* Returns true if the key is used for keyboard navigation.
* @param keyEvent The key event.
@@ -5857,29 +5779,22 @@
// Some fallback keys are decided by the ViewRoot as they might have special
// properties (e.g. are locale aware). These take precedence over fallbacks defined by
// the kcm.
- KeyEvent fallbackEvent = getSyntheticFallbackKey(event);
+ final KeyCharacterMap kcm = event.getKeyCharacterMap();
+ final int keyCode = event.getKeyCode();
+ final int metaState = event.getMetaState();
- if (fallbackEvent == null) {
- final KeyCharacterMap kcm = event.getKeyCharacterMap();
- final int keyCode = event.getKeyCode();
- final int metaState = event.getMetaState();
-
- // Check for fallback actions specified by the key character map.
- KeyCharacterMap.FallbackAction fallbackAction =
- kcm.getFallbackAction(keyCode, metaState);
- if (fallbackAction != null) {
- final int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK;
- fallbackEvent = KeyEvent.obtain(
- event.getDownTime(), event.getEventTime(),
- event.getAction(), fallbackAction.keyCode,
- event.getRepeatCount(), fallbackAction.metaState,
- event.getDeviceId(), event.getScanCode(),
- flags, event.getSource(), null);
- fallbackAction.recycle();
-
- }
- }
- if (fallbackEvent != null) {
+ // Check for fallback actions specified by the key character map.
+ KeyCharacterMap.FallbackAction fallbackAction =
+ kcm.getFallbackAction(keyCode, metaState);
+ if (fallbackAction != null) {
+ final int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK;
+ KeyEvent fallbackEvent = KeyEvent.obtain(
+ event.getDownTime(), event.getEventTime(),
+ event.getAction(), fallbackAction.keyCode,
+ event.getRepeatCount(), fallbackAction.metaState,
+ event.getDeviceId(), event.getScanCode(),
+ flags, event.getSource(), null);
+ fallbackAction.recycle();
dispatchInputEvent(fallbackEvent);
}
}
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 7603305..4fdbc1e 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -2283,6 +2283,7 @@
parcel.writeInt(mCollectionInfo.getRowCount());
parcel.writeInt(mCollectionInfo.getColumnCount());
parcel.writeInt(mCollectionInfo.isHierarchical() ? 1 : 0);
+ parcel.writeInt(mCollectionInfo.getSelectionMode());
} else {
parcel.writeInt(0);
}
@@ -2294,6 +2295,7 @@
parcel.writeInt(mCollectionItemInfo.getRowIndex());
parcel.writeInt(mCollectionItemInfo.getRowSpan());
parcel.writeInt(mCollectionItemInfo.isHeading() ? 1 : 0);
+ parcel.writeInt(mCollectionItemInfo.isSelected() ? 1 : 0);
} else {
parcel.writeInt(0);
}
@@ -2420,7 +2422,8 @@
mCollectionInfo = CollectionInfo.obtain(
parcel.readInt(),
parcel.readInt(),
- parcel.readInt() == 1);
+ parcel.readInt() == 1,
+ parcel.readInt());
}
if (parcel.readInt() == 1) {
@@ -2429,6 +2432,7 @@
parcel.readInt(),
parcel.readInt(),
parcel.readInt(),
+ parcel.readInt() == 1,
parcel.readInt() == 1);
}
}
@@ -2786,6 +2790,15 @@
* </p>
*/
public static final class CollectionInfo {
+ /** Selection mode where items are not selectable. */
+ public static final int SELECTION_MODE_NONE = 0;
+
+ /** Selection mode where a single item may be selected. */
+ public static final int SELECTION_MODE_SINGLE = 1;
+
+ /** Selection mode where multiple items may be selected. */
+ public static final int SELECTION_MODE_MULTIPLE = 2;
+
private static final int MAX_POOL_SIZE = 20;
private static final SynchronizedPool<CollectionInfo> sPool =
@@ -2794,17 +2807,17 @@
private int mRowCount;
private int mColumnCount;
private boolean mHierarchical;
+ private int mSelectionMode;
/**
* Obtains a pooled instance that is a clone of another one.
*
* @param other The instance to clone.
- *
* @hide
*/
public static CollectionInfo obtain(CollectionInfo other) {
- return CollectionInfo.obtain(other.mRowCount, other.mColumnCount,
- other.mHierarchical);
+ return CollectionInfo.obtain(other.mRowCount, other.mColumnCount, other.mHierarchical,
+ other.mSelectionMode);
}
/**
@@ -2814,15 +2827,35 @@
* @param columnCount The number of columns.
* @param hierarchical Whether the collection is hierarchical.
*/
- public static CollectionInfo obtain(int rowCount, int columnCount, boolean hierarchical) {
- final CollectionInfo info = sPool.acquire();
+ public static CollectionInfo obtain(int rowCount, int columnCount,
+ boolean hierarchical) {
+ return obtain(rowCount, columnCount, hierarchical, SELECTION_MODE_NONE);
+ }
+
+ /**
+ * Obtains a pooled instance.
+ *
+ * @param rowCount The number of rows.
+ * @param columnCount The number of columns.
+ * @param hierarchical Whether the collection is hierarchical.
+ * @param selectionMode The collection's selection mode, one of:
+ * <ul>
+ * <li>{@link #SELECTION_MODE_NONE}
+ * <li>{@link #SELECTION_MODE_SINGLE}
+ * <li>{@link #SELECTION_MODE_MULTIPLE}
+ * </ul>
+ */
+ public static CollectionInfo obtain(int rowCount, int columnCount,
+ boolean hierarchical, int selectionMode) {
+ final CollectionInfo info = sPool.acquire();
if (info == null) {
- return new CollectionInfo(rowCount, columnCount, hierarchical);
+ return new CollectionInfo(rowCount, columnCount, hierarchical, selectionMode);
}
info.mRowCount = rowCount;
info.mColumnCount = columnCount;
info.mHierarchical = hierarchical;
+ info.mSelectionMode = selectionMode;
return info;
}
@@ -2832,12 +2865,14 @@
* @param rowCount The number of rows.
* @param columnCount The number of columns.
* @param hierarchical Whether the collection is hierarchical.
+ * @param selectionMode The collection's selection mode.
*/
- private CollectionInfo(int rowCount, int columnCount,
- boolean hierarchical) {
+ private CollectionInfo(int rowCount, int columnCount, boolean hierarchical,
+ int selectionMode) {
mRowCount = rowCount;
mColumnCount = columnCount;
mHierarchical = hierarchical;
+ mSelectionMode = selectionMode;
}
/**
@@ -2868,6 +2903,20 @@
}
/**
+ * Gets the collection's selection mode.
+ *
+ * @return The collection's selection mode, one of:
+ * <ul>
+ * <li>{@link #SELECTION_MODE_NONE}
+ * <li>{@link #SELECTION_MODE_SINGLE}
+ * <li>{@link #SELECTION_MODE_MULTIPLE}
+ * </ul>
+ */
+ public int getSelectionMode() {
+ return mSelectionMode;
+ }
+
+ /**
* Recycles this instance.
*/
void recycle() {
@@ -2879,6 +2928,7 @@
mRowCount = 0;
mColumnCount = 0;
mHierarchical = false;
+ mSelectionMode = SELECTION_MODE_NONE;
}
}
@@ -2904,12 +2954,11 @@
* Obtains a pooled instance that is a clone of another one.
*
* @param other The instance to clone.
- *
* @hide
*/
public static CollectionItemInfo obtain(CollectionItemInfo other) {
- return CollectionItemInfo.obtain(other.mRowIndex, other.mRowSpan,
- other.mColumnIndex, other.mColumnSpan, other.mHeading);
+ return CollectionItemInfo.obtain(other.mRowIndex, other.mRowSpan, other.mColumnIndex,
+ other.mColumnSpan, other.mHeading, other.mSelected);
}
/**
@@ -2921,11 +2970,27 @@
* @param columnSpan The number of columns the item spans.
* @param heading Whether the item is a heading.
*/
- public static CollectionItemInfo obtain(int rowIndex, int rowSpan, int columnIndex,
- int columnSpan, boolean heading) {
+ public static CollectionItemInfo obtain(int rowIndex, int rowSpan,
+ int columnIndex, int columnSpan, boolean heading) {
+ return obtain(rowIndex, rowSpan, columnIndex, columnSpan, heading, false);
+ }
+
+ /**
+ * Obtains a pooled instance.
+ *
+ * @param rowIndex The row index at which the item is located.
+ * @param rowSpan The number of rows the item spans.
+ * @param columnIndex The column index at which the item is located.
+ * @param columnSpan The number of columns the item spans.
+ * @param heading Whether the item is a heading.
+ * @param selected Whether the item is selected.
+ */
+ public static CollectionItemInfo obtain(int rowIndex, int rowSpan,
+ int columnIndex, int columnSpan, boolean heading, boolean selected) {
final CollectionItemInfo info = sPool.acquire();
if (info == null) {
- return new CollectionItemInfo(rowIndex, rowSpan, columnIndex, columnSpan, heading);
+ return new CollectionItemInfo(
+ rowIndex, rowSpan, columnIndex, columnSpan, heading, selected);
}
info.mRowIndex = rowIndex;
@@ -2933,6 +2998,7 @@
info.mColumnIndex = columnIndex;
info.mColumnSpan = columnSpan;
info.mHeading = heading;
+ info.mSelected = selected;
return info;
}
@@ -2941,6 +3007,7 @@
private int mRowIndex;
private int mColumnSpan;
private int mRowSpan;
+ private boolean mSelected;
/**
* Creates a new instance.
@@ -2951,13 +3018,14 @@
* @param columnSpan The number of columns the item spans.
* @param heading Whether the item is a heading.
*/
- private CollectionItemInfo(int rowIndex, int rowSpan,
- int columnIndex, int columnSpan, boolean heading) {
+ private CollectionItemInfo(int rowIndex, int rowSpan, int columnIndex, int columnSpan,
+ boolean heading, boolean selected) {
mRowIndex = rowIndex;
mRowSpan = rowSpan;
mColumnIndex = columnIndex;
mColumnSpan = columnSpan;
mHeading = heading;
+ mSelected = selected;
}
/**
@@ -3007,6 +3075,15 @@
}
/**
+ * Gets if the collection item is selected.
+ *
+ * @return If the item is selected.
+ */
+ public boolean isSelected() {
+ return mSelected;
+ }
+
+ /**
* Recycles this instance.
*/
void recycle() {
@@ -3020,6 +3097,7 @@
mRowIndex = 0;
mRowSpan = 0;
mHeading = false;
+ mSelected = false;
}
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 0a755ca..63ce5a3 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -61,6 +61,7 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.CollectionInfo;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
@@ -421,7 +422,7 @@
/**
* Handles scrolling between positions within the list.
*/
- SubPositionScroller mPositionScroller;
+ AbsPositionScroller mPositionScroller;
/**
* The offset in pixels form the top of the AdapterView to the top
@@ -1491,6 +1492,21 @@
}
}
+ int getSelectionModeForAccessibility() {
+ final int choiceMode = getChoiceMode();
+ switch (choiceMode) {
+ case CHOICE_MODE_NONE:
+ return CollectionInfo.SELECTION_MODE_NONE;
+ case CHOICE_MODE_SINGLE:
+ return CollectionInfo.SELECTION_MODE_SINGLE;
+ case CHOICE_MODE_MULTIPLE:
+ case CHOICE_MODE_MULTIPLE_MODAL:
+ return CollectionInfo.SELECTION_MODE_MULTIPLE;
+ default:
+ return CollectionInfo.SELECTION_MODE_NONE;
+ }
+ }
+
@Override
public boolean performAccessibilityAction(int action, Bundle arguments) {
if (super.performAccessibilityAction(action, arguments)) {
@@ -4374,447 +4390,6 @@
}
}
- class PositionScroller implements Runnable {
- private static final int SCROLL_DURATION = 200;
-
- private static final int MOVE_DOWN_POS = 1;
- private static final int MOVE_UP_POS = 2;
- private static final int MOVE_DOWN_BOUND = 3;
- private static final int MOVE_UP_BOUND = 4;
- private static final int MOVE_OFFSET = 5;
-
- private int mMode;
- private int mTargetPos;
- private int mBoundPos;
- private int mLastSeenPos;
- private int mScrollDuration;
- private final int mExtraScroll;
-
- private int mOffsetFromTop;
-
- PositionScroller() {
- mExtraScroll = ViewConfiguration.get(mContext).getScaledFadingEdgeLength();
- }
-
- void start(final int position) {
- stop();
-
- if (mDataChanged) {
- // Wait until we're back in a stable state to try this.
- mPositionScrollAfterLayout = new Runnable() {
- @Override public void run() {
- start(position);
- }
- };
- return;
- }
-
- final int childCount = getChildCount();
- if (childCount == 0) {
- // Can't scroll without children.
- return;
- }
-
- final int firstPos = mFirstPosition;
- final int lastPos = firstPos + childCount - 1;
-
- int viewTravelCount;
- int clampedPosition = Math.max(0, Math.min(getCount() - 1, position));
- if (clampedPosition < firstPos) {
- viewTravelCount = firstPos - clampedPosition + 1;
- mMode = MOVE_UP_POS;
- } else if (clampedPosition > lastPos) {
- viewTravelCount = clampedPosition - lastPos + 1;
- mMode = MOVE_DOWN_POS;
- } else {
- scrollToVisible(clampedPosition, INVALID_POSITION, SCROLL_DURATION);
- return;
- }
-
- if (viewTravelCount > 0) {
- mScrollDuration = SCROLL_DURATION / viewTravelCount;
- } else {
- mScrollDuration = SCROLL_DURATION;
- }
- mTargetPos = clampedPosition;
- mBoundPos = INVALID_POSITION;
- mLastSeenPos = INVALID_POSITION;
-
- postOnAnimation(this);
- }
-
- void start(final int position, final int boundPosition) {
- stop();
-
- if (boundPosition == INVALID_POSITION) {
- start(position);
- return;
- }
-
- if (mDataChanged) {
- // Wait until we're back in a stable state to try this.
- mPositionScrollAfterLayout = new Runnable() {
- @Override public void run() {
- start(position, boundPosition);
- }
- };
- return;
- }
-
- final int childCount = getChildCount();
- if (childCount == 0) {
- // Can't scroll without children.
- return;
- }
-
- final int firstPos = mFirstPosition;
- final int lastPos = firstPos + childCount - 1;
-
- int viewTravelCount;
- int clampedPosition = Math.max(0, Math.min(getCount() - 1, position));
- if (clampedPosition < firstPos) {
- final int boundPosFromLast = lastPos - boundPosition;
- if (boundPosFromLast < 1) {
- // Moving would shift our bound position off the screen. Abort.
- return;
- }
-
- final int posTravel = firstPos - clampedPosition + 1;
- final int boundTravel = boundPosFromLast - 1;
- if (boundTravel < posTravel) {
- viewTravelCount = boundTravel;
- mMode = MOVE_UP_BOUND;
- } else {
- viewTravelCount = posTravel;
- mMode = MOVE_UP_POS;
- }
- } else if (clampedPosition > lastPos) {
- final int boundPosFromFirst = boundPosition - firstPos;
- if (boundPosFromFirst < 1) {
- // Moving would shift our bound position off the screen. Abort.
- return;
- }
-
- final int posTravel = clampedPosition - lastPos + 1;
- final int boundTravel = boundPosFromFirst - 1;
- if (boundTravel < posTravel) {
- viewTravelCount = boundTravel;
- mMode = MOVE_DOWN_BOUND;
- } else {
- viewTravelCount = posTravel;
- mMode = MOVE_DOWN_POS;
- }
- } else {
- scrollToVisible(clampedPosition, boundPosition, SCROLL_DURATION);
- return;
- }
-
- if (viewTravelCount > 0) {
- mScrollDuration = SCROLL_DURATION / viewTravelCount;
- } else {
- mScrollDuration = SCROLL_DURATION;
- }
- mTargetPos = clampedPosition;
- mBoundPos = boundPosition;
- mLastSeenPos = INVALID_POSITION;
-
- postOnAnimation(this);
- }
-
- void startWithOffset(int position, int offset) {
- startWithOffset(position, offset, SCROLL_DURATION);
- }
-
- void startWithOffset(final int position, int offset, final int duration) {
- stop();
-
- if (mDataChanged) {
- // Wait until we're back in a stable state to try this.
- final int postOffset = offset;
- mPositionScrollAfterLayout = new Runnable() {
- @Override public void run() {
- startWithOffset(position, postOffset, duration);
- }
- };
- return;
- }
-
- final int childCount = getChildCount();
- if (childCount == 0) {
- // Can't scroll without children.
- return;
- }
-
- offset += getPaddingTop();
-
- mTargetPos = Math.max(0, Math.min(getCount() - 1, position));
- mOffsetFromTop = offset;
- mBoundPos = INVALID_POSITION;
- mLastSeenPos = INVALID_POSITION;
- mMode = MOVE_OFFSET;
-
- final int firstPos = mFirstPosition;
- final int lastPos = firstPos + childCount - 1;
-
- int viewTravelCount;
- if (mTargetPos < firstPos) {
- viewTravelCount = firstPos - mTargetPos;
- } else if (mTargetPos > lastPos) {
- viewTravelCount = mTargetPos - lastPos;
- } else {
- // On-screen, just scroll.
- final int targetTop = getChildAt(mTargetPos - firstPos).getTop();
- smoothScrollBy(targetTop - offset, duration, true);
- return;
- }
-
- // Estimate how many screens we should travel
- final float screenTravelCount = (float) viewTravelCount / childCount;
- mScrollDuration = screenTravelCount < 1 ?
- duration : (int) (duration / screenTravelCount);
- mLastSeenPos = INVALID_POSITION;
-
- postOnAnimation(this);
- }
-
- /**
- * Scroll such that targetPos is in the visible padded region without scrolling
- * boundPos out of view. Assumes targetPos is onscreen.
- */
- void scrollToVisible(int targetPos, int boundPos, int duration) {
- final int firstPos = mFirstPosition;
- final int childCount = getChildCount();
- final int lastPos = firstPos + childCount - 1;
- final int paddedTop = mListPadding.top;
- final int paddedBottom = getHeight() - mListPadding.bottom;
-
- if (targetPos < firstPos || targetPos > lastPos) {
- Log.w(TAG, "scrollToVisible called with targetPos " + targetPos +
- " not visible [" + firstPos + ", " + lastPos + "]");
- }
- if (boundPos < firstPos || boundPos > lastPos) {
- // boundPos doesn't matter, it's already offscreen.
- boundPos = INVALID_POSITION;
- }
-
- final View targetChild = getChildAt(targetPos - firstPos);
- final int targetTop = targetChild.getTop();
- final int targetBottom = targetChild.getBottom();
- int scrollBy = 0;
-
- if (targetBottom > paddedBottom) {
- scrollBy = targetBottom - paddedBottom;
- }
- if (targetTop < paddedTop) {
- scrollBy = targetTop - paddedTop;
- }
-
- if (scrollBy == 0) {
- return;
- }
-
- if (boundPos >= 0) {
- final View boundChild = getChildAt(boundPos - firstPos);
- final int boundTop = boundChild.getTop();
- final int boundBottom = boundChild.getBottom();
- final int absScroll = Math.abs(scrollBy);
-
- if (scrollBy < 0 && boundBottom + absScroll > paddedBottom) {
- // Don't scroll the bound view off the bottom of the screen.
- scrollBy = Math.max(0, boundBottom - paddedBottom);
- } else if (scrollBy > 0 && boundTop - absScroll < paddedTop) {
- // Don't scroll the bound view off the top of the screen.
- scrollBy = Math.min(0, boundTop - paddedTop);
- }
- }
-
- smoothScrollBy(scrollBy, duration);
- }
-
- void stop() {
- removeCallbacks(this);
- }
-
- @Override
- public void run() {
- final int listHeight = getHeight();
- final int firstPos = mFirstPosition;
-
- switch (mMode) {
- case MOVE_DOWN_POS: {
- final int lastViewIndex = getChildCount() - 1;
- final int lastPos = firstPos + lastViewIndex;
-
- if (lastViewIndex < 0) {
- return;
- }
-
- if (lastPos == mLastSeenPos) {
- // No new views, let things keep going.
- postOnAnimation(this);
- return;
- }
-
- final View lastView = getChildAt(lastViewIndex);
- final int lastViewHeight = lastView.getHeight();
- final int lastViewTop = lastView.getTop();
- final int lastViewPixelsShowing = listHeight - lastViewTop;
- final int extraScroll = lastPos < mItemCount - 1 ?
- Math.max(mListPadding.bottom, mExtraScroll) : mListPadding.bottom;
-
- final int scrollBy = lastViewHeight - lastViewPixelsShowing + extraScroll;
- smoothScrollBy(scrollBy, mScrollDuration, true);
-
- mLastSeenPos = lastPos;
- if (lastPos < mTargetPos) {
- postOnAnimation(this);
- }
- break;
- }
-
- case MOVE_DOWN_BOUND: {
- final int nextViewIndex = 1;
- final int childCount = getChildCount();
-
- if (firstPos == mBoundPos || childCount <= nextViewIndex
- || firstPos + childCount >= mItemCount) {
- return;
- }
- final int nextPos = firstPos + nextViewIndex;
-
- if (nextPos == mLastSeenPos) {
- // No new views, let things keep going.
- postOnAnimation(this);
- return;
- }
-
- final View nextView = getChildAt(nextViewIndex);
- final int nextViewHeight = nextView.getHeight();
- final int nextViewTop = nextView.getTop();
- final int extraScroll = Math.max(mListPadding.bottom, mExtraScroll);
- if (nextPos < mBoundPos) {
- smoothScrollBy(Math.max(0, nextViewHeight + nextViewTop - extraScroll),
- mScrollDuration, true);
-
- mLastSeenPos = nextPos;
-
- postOnAnimation(this);
- } else {
- if (nextViewTop > extraScroll) {
- smoothScrollBy(nextViewTop - extraScroll, mScrollDuration, true);
- }
- }
- break;
- }
-
- case MOVE_UP_POS: {
- if (firstPos == mLastSeenPos) {
- // No new views, let things keep going.
- postOnAnimation(this);
- return;
- }
-
- final View firstView = getChildAt(0);
- if (firstView == null) {
- return;
- }
- final int firstViewTop = firstView.getTop();
- final int extraScroll = firstPos > 0 ?
- Math.max(mExtraScroll, mListPadding.top) : mListPadding.top;
-
- smoothScrollBy(firstViewTop - extraScroll, mScrollDuration, true);
-
- mLastSeenPos = firstPos;
-
- if (firstPos > mTargetPos) {
- postOnAnimation(this);
- }
- break;
- }
-
- case MOVE_UP_BOUND: {
- final int lastViewIndex = getChildCount() - 2;
- if (lastViewIndex < 0) {
- return;
- }
- final int lastPos = firstPos + lastViewIndex;
-
- if (lastPos == mLastSeenPos) {
- // No new views, let things keep going.
- postOnAnimation(this);
- return;
- }
-
- final View lastView = getChildAt(lastViewIndex);
- final int lastViewHeight = lastView.getHeight();
- final int lastViewTop = lastView.getTop();
- final int lastViewPixelsShowing = listHeight - lastViewTop;
- final int extraScroll = Math.max(mListPadding.top, mExtraScroll);
- mLastSeenPos = lastPos;
- if (lastPos > mBoundPos) {
- smoothScrollBy(-(lastViewPixelsShowing - extraScroll), mScrollDuration, true);
- postOnAnimation(this);
- } else {
- final int bottom = listHeight - extraScroll;
- final int lastViewBottom = lastViewTop + lastViewHeight;
- if (bottom > lastViewBottom) {
- smoothScrollBy(-(bottom - lastViewBottom), mScrollDuration, true);
- }
- }
- break;
- }
-
- case MOVE_OFFSET: {
- if (mLastSeenPos == firstPos) {
- // No new views, let things keep going.
- postOnAnimation(this);
- return;
- }
-
- mLastSeenPos = firstPos;
-
- final int childCount = getChildCount();
- final int position = mTargetPos;
- final int lastPos = firstPos + childCount - 1;
-
- int viewTravelCount = 0;
- if (position < firstPos) {
- viewTravelCount = firstPos - position + 1;
- } else if (position > lastPos) {
- viewTravelCount = position - lastPos;
- }
-
- // Estimate how many screens we should travel
- final float screenTravelCount = (float) viewTravelCount / childCount;
-
- final float modifier = Math.min(Math.abs(screenTravelCount), 1.f);
- if (position < firstPos) {
- final int distance = (int) (-getHeight() * modifier);
- final int duration = (int) (mScrollDuration * modifier);
- smoothScrollBy(distance, duration, true);
- postOnAnimation(this);
- } else if (position > lastPos) {
- final int distance = (int) (getHeight() * modifier);
- final int duration = (int) (mScrollDuration * modifier);
- smoothScrollBy(distance, duration, true);
- postOnAnimation(this);
- } else {
- // On-screen, just scroll.
- final int targetTop = getChildAt(position - firstPos).getTop();
- final int distance = targetTop - mOffsetFromTop;
- final int duration = (int) (mScrollDuration *
- ((float) Math.abs(distance) / getHeight()));
- smoothScrollBy(distance, duration, true);
- }
- break;
- }
-
- default:
- break;
- }
- }
- }
-
/**
* The amount of friction applied to flings. The default value
* is {@link ViewConfiguration#getScrollFriction}.
@@ -4837,13 +4412,20 @@
}
/**
+ * Override this for better control over position scrolling.
+ */
+ AbsPositionScroller createPositionScroller() {
+ return new PositionScroller();
+ }
+
+ /**
* Smoothly scroll to the specified adapter position. The view will
* scroll such that the indicated position is displayed.
* @param position Scroll to this adapter position.
*/
public void smoothScrollToPosition(int position) {
if (mPositionScroller == null) {
- mPositionScroller = new SubPositionScroller();
+ mPositionScroller = createPositionScroller();
}
mPositionScroller.start(position);
}
@@ -4862,7 +4444,7 @@
*/
public void smoothScrollToPositionFromTop(int position, int offset, int duration) {
if (mPositionScroller == null) {
- mPositionScroller = new SubPositionScroller();
+ mPositionScroller = createPositionScroller();
}
mPositionScroller.startWithOffset(position, offset, duration);
}
@@ -4880,7 +4462,7 @@
*/
public void smoothScrollToPositionFromTop(int position, int offset) {
if (mPositionScroller == null) {
- mPositionScroller = new SubPositionScroller();
+ mPositionScroller = createPositionScroller();
}
mPositionScroller.startWithOffset(position, offset, offset);
}
@@ -4896,7 +4478,7 @@
*/
public void smoothScrollToPosition(int position, int boundPosition) {
if (mPositionScroller == null) {
- mPositionScroller = new SubPositionScroller();
+ mPositionScroller = createPositionScroller();
}
mPositionScroller.start(position, boundPosition);
}
@@ -6997,26 +6579,6 @@
}
/**
- * Returns the height of a row, which is computed as the maximum height of
- * the items in the row.
- *
- * @param row the row index
- * @return row height in pixels
- */
- private int getHeightForRow(int row) {
- final int firstRowPosition = getFirstPositionForRow(row);
- final int lastRowPosition = getFirstPositionForRow(row + 1);
- int maxHeight = 0;
- for (int i = firstRowPosition; i < lastRowPosition; i++) {
- final int height = getHeightForPosition(i);
- if (height > maxHeight) {
- maxHeight = height;
- }
- }
- return maxHeight;
- }
-
- /**
* Returns the height of the view for the specified position.
*
* @param position the item position
@@ -7026,10 +6588,12 @@
final int firstVisiblePosition = getFirstVisiblePosition();
final int childCount = getChildCount();
final int index = position - firstVisiblePosition;
- if (position >= 0 && position < childCount) {
+ if (index >= 0 && index < childCount) {
+ // Position is on-screen, use existing view.
final View view = getChildAt(index);
return view.getHeight();
} else {
+ // Position is off-screen, obtain & recycle view.
final View view = obtainView(position, mIsScrap);
view.measure(mWidthMeasureSpec, MeasureSpec.UNSPECIFIED);
final int height = view.getMeasuredHeight();
@@ -7039,26 +6603,6 @@
}
/**
- * Returns the row for the specified item position.
- *
- * @param position the item position
- * @return the row index
- */
- public int getRowForPosition(int position) {
- return position;
- }
-
- /**
- * Returns the first item position within the specified row.
- *
- * @param row the row
- * @return the item position
- */
- public int getFirstPositionForRow(int row) {
- return row;
- }
-
- /**
* Sets the selected item and positions the selection y pixels from the top edge
* of the ListView. (If in touch mode, the item will not be selected but it will
* still be positioned appropriately.)
@@ -7097,10 +6641,474 @@
}
}
- class SubPositionScroller {
+ /**
+ * Abstract positon scroller used to handle smooth scrolling.
+ */
+ static abstract class AbsPositionScroller {
+ public abstract void start(int position);
+ public abstract void start(int position, int boundPosition);
+ public abstract void startWithOffset(int position, int offset);
+ public abstract void startWithOffset(int position, int offset, int duration);
+ public abstract void stop();
+ }
+
+ /**
+ * Default position scroller that simulates a fling.
+ */
+ class PositionScroller extends AbsPositionScroller implements Runnable {
+ private static final int SCROLL_DURATION = 200;
+
+ private static final int MOVE_DOWN_POS = 1;
+ private static final int MOVE_UP_POS = 2;
+ private static final int MOVE_DOWN_BOUND = 3;
+ private static final int MOVE_UP_BOUND = 4;
+ private static final int MOVE_OFFSET = 5;
+
+ private int mMode;
+ private int mTargetPos;
+ private int mBoundPos;
+ private int mLastSeenPos;
+ private int mScrollDuration;
+ private final int mExtraScroll;
+
+ private int mOffsetFromTop;
+
+ PositionScroller() {
+ mExtraScroll = ViewConfiguration.get(mContext).getScaledFadingEdgeLength();
+ }
+
+ @Override
+ public void start(final int position) {
+ stop();
+
+ if (mDataChanged) {
+ // Wait until we're back in a stable state to try this.
+ mPositionScrollAfterLayout = new Runnable() {
+ @Override public void run() {
+ start(position);
+ }
+ };
+ return;
+ }
+
+ final int childCount = getChildCount();
+ if (childCount == 0) {
+ // Can't scroll without children.
+ return;
+ }
+
+ final int firstPos = mFirstPosition;
+ final int lastPos = firstPos + childCount - 1;
+
+ int viewTravelCount;
+ int clampedPosition = Math.max(0, Math.min(getCount() - 1, position));
+ if (clampedPosition < firstPos) {
+ viewTravelCount = firstPos - clampedPosition + 1;
+ mMode = MOVE_UP_POS;
+ } else if (clampedPosition > lastPos) {
+ viewTravelCount = clampedPosition - lastPos + 1;
+ mMode = MOVE_DOWN_POS;
+ } else {
+ scrollToVisible(clampedPosition, INVALID_POSITION, SCROLL_DURATION);
+ return;
+ }
+
+ if (viewTravelCount > 0) {
+ mScrollDuration = SCROLL_DURATION / viewTravelCount;
+ } else {
+ mScrollDuration = SCROLL_DURATION;
+ }
+ mTargetPos = clampedPosition;
+ mBoundPos = INVALID_POSITION;
+ mLastSeenPos = INVALID_POSITION;
+
+ postOnAnimation(this);
+ }
+
+ @Override
+ public void start(final int position, final int boundPosition) {
+ stop();
+
+ if (boundPosition == INVALID_POSITION) {
+ start(position);
+ return;
+ }
+
+ if (mDataChanged) {
+ // Wait until we're back in a stable state to try this.
+ mPositionScrollAfterLayout = new Runnable() {
+ @Override public void run() {
+ start(position, boundPosition);
+ }
+ };
+ return;
+ }
+
+ final int childCount = getChildCount();
+ if (childCount == 0) {
+ // Can't scroll without children.
+ return;
+ }
+
+ final int firstPos = mFirstPosition;
+ final int lastPos = firstPos + childCount - 1;
+
+ int viewTravelCount;
+ int clampedPosition = Math.max(0, Math.min(getCount() - 1, position));
+ if (clampedPosition < firstPos) {
+ final int boundPosFromLast = lastPos - boundPosition;
+ if (boundPosFromLast < 1) {
+ // Moving would shift our bound position off the screen. Abort.
+ return;
+ }
+
+ final int posTravel = firstPos - clampedPosition + 1;
+ final int boundTravel = boundPosFromLast - 1;
+ if (boundTravel < posTravel) {
+ viewTravelCount = boundTravel;
+ mMode = MOVE_UP_BOUND;
+ } else {
+ viewTravelCount = posTravel;
+ mMode = MOVE_UP_POS;
+ }
+ } else if (clampedPosition > lastPos) {
+ final int boundPosFromFirst = boundPosition - firstPos;
+ if (boundPosFromFirst < 1) {
+ // Moving would shift our bound position off the screen. Abort.
+ return;
+ }
+
+ final int posTravel = clampedPosition - lastPos + 1;
+ final int boundTravel = boundPosFromFirst - 1;
+ if (boundTravel < posTravel) {
+ viewTravelCount = boundTravel;
+ mMode = MOVE_DOWN_BOUND;
+ } else {
+ viewTravelCount = posTravel;
+ mMode = MOVE_DOWN_POS;
+ }
+ } else {
+ scrollToVisible(clampedPosition, boundPosition, SCROLL_DURATION);
+ return;
+ }
+
+ if (viewTravelCount > 0) {
+ mScrollDuration = SCROLL_DURATION / viewTravelCount;
+ } else {
+ mScrollDuration = SCROLL_DURATION;
+ }
+ mTargetPos = clampedPosition;
+ mBoundPos = boundPosition;
+ mLastSeenPos = INVALID_POSITION;
+
+ postOnAnimation(this);
+ }
+
+ @Override
+ public void startWithOffset(int position, int offset) {
+ startWithOffset(position, offset, SCROLL_DURATION);
+ }
+
+ @Override
+ public void startWithOffset(final int position, int offset, final int duration) {
+ stop();
+
+ if (mDataChanged) {
+ // Wait until we're back in a stable state to try this.
+ final int postOffset = offset;
+ mPositionScrollAfterLayout = new Runnable() {
+ @Override public void run() {
+ startWithOffset(position, postOffset, duration);
+ }
+ };
+ return;
+ }
+
+ final int childCount = getChildCount();
+ if (childCount == 0) {
+ // Can't scroll without children.
+ return;
+ }
+
+ offset += getPaddingTop();
+
+ mTargetPos = Math.max(0, Math.min(getCount() - 1, position));
+ mOffsetFromTop = offset;
+ mBoundPos = INVALID_POSITION;
+ mLastSeenPos = INVALID_POSITION;
+ mMode = MOVE_OFFSET;
+
+ final int firstPos = mFirstPosition;
+ final int lastPos = firstPos + childCount - 1;
+
+ int viewTravelCount;
+ if (mTargetPos < firstPos) {
+ viewTravelCount = firstPos - mTargetPos;
+ } else if (mTargetPos > lastPos) {
+ viewTravelCount = mTargetPos - lastPos;
+ } else {
+ // On-screen, just scroll.
+ final int targetTop = getChildAt(mTargetPos - firstPos).getTop();
+ smoothScrollBy(targetTop - offset, duration, true);
+ return;
+ }
+
+ // Estimate how many screens we should travel
+ final float screenTravelCount = (float) viewTravelCount / childCount;
+ mScrollDuration = screenTravelCount < 1 ?
+ duration : (int) (duration / screenTravelCount);
+ mLastSeenPos = INVALID_POSITION;
+
+ postOnAnimation(this);
+ }
+
+ /**
+ * Scroll such that targetPos is in the visible padded region without scrolling
+ * boundPos out of view. Assumes targetPos is onscreen.
+ */
+ private void scrollToVisible(int targetPos, int boundPos, int duration) {
+ final int firstPos = mFirstPosition;
+ final int childCount = getChildCount();
+ final int lastPos = firstPos + childCount - 1;
+ final int paddedTop = mListPadding.top;
+ final int paddedBottom = getHeight() - mListPadding.bottom;
+
+ if (targetPos < firstPos || targetPos > lastPos) {
+ Log.w(TAG, "scrollToVisible called with targetPos " + targetPos +
+ " not visible [" + firstPos + ", " + lastPos + "]");
+ }
+ if (boundPos < firstPos || boundPos > lastPos) {
+ // boundPos doesn't matter, it's already offscreen.
+ boundPos = INVALID_POSITION;
+ }
+
+ final View targetChild = getChildAt(targetPos - firstPos);
+ final int targetTop = targetChild.getTop();
+ final int targetBottom = targetChild.getBottom();
+ int scrollBy = 0;
+
+ if (targetBottom > paddedBottom) {
+ scrollBy = targetBottom - paddedBottom;
+ }
+ if (targetTop < paddedTop) {
+ scrollBy = targetTop - paddedTop;
+ }
+
+ if (scrollBy == 0) {
+ return;
+ }
+
+ if (boundPos >= 0) {
+ final View boundChild = getChildAt(boundPos - firstPos);
+ final int boundTop = boundChild.getTop();
+ final int boundBottom = boundChild.getBottom();
+ final int absScroll = Math.abs(scrollBy);
+
+ if (scrollBy < 0 && boundBottom + absScroll > paddedBottom) {
+ // Don't scroll the bound view off the bottom of the screen.
+ scrollBy = Math.max(0, boundBottom - paddedBottom);
+ } else if (scrollBy > 0 && boundTop - absScroll < paddedTop) {
+ // Don't scroll the bound view off the top of the screen.
+ scrollBy = Math.min(0, boundTop - paddedTop);
+ }
+ }
+
+ smoothScrollBy(scrollBy, duration);
+ }
+
+ @Override
+ public void stop() {
+ removeCallbacks(this);
+ }
+
+ @Override
+ public void run() {
+ final int listHeight = getHeight();
+ final int firstPos = mFirstPosition;
+
+ switch (mMode) {
+ case MOVE_DOWN_POS: {
+ final int lastViewIndex = getChildCount() - 1;
+ final int lastPos = firstPos + lastViewIndex;
+
+ if (lastViewIndex < 0) {
+ return;
+ }
+
+ if (lastPos == mLastSeenPos) {
+ // No new views, let things keep going.
+ postOnAnimation(this);
+ return;
+ }
+
+ final View lastView = getChildAt(lastViewIndex);
+ final int lastViewHeight = lastView.getHeight();
+ final int lastViewTop = lastView.getTop();
+ final int lastViewPixelsShowing = listHeight - lastViewTop;
+ final int extraScroll = lastPos < mItemCount - 1 ?
+ Math.max(mListPadding.bottom, mExtraScroll) : mListPadding.bottom;
+
+ final int scrollBy = lastViewHeight - lastViewPixelsShowing + extraScroll;
+ smoothScrollBy(scrollBy, mScrollDuration, true);
+
+ mLastSeenPos = lastPos;
+ if (lastPos < mTargetPos) {
+ postOnAnimation(this);
+ }
+ break;
+ }
+
+ case MOVE_DOWN_BOUND: {
+ final int nextViewIndex = 1;
+ final int childCount = getChildCount();
+
+ if (firstPos == mBoundPos || childCount <= nextViewIndex
+ || firstPos + childCount >= mItemCount) {
+ return;
+ }
+ final int nextPos = firstPos + nextViewIndex;
+
+ if (nextPos == mLastSeenPos) {
+ // No new views, let things keep going.
+ postOnAnimation(this);
+ return;
+ }
+
+ final View nextView = getChildAt(nextViewIndex);
+ final int nextViewHeight = nextView.getHeight();
+ final int nextViewTop = nextView.getTop();
+ final int extraScroll = Math.max(mListPadding.bottom, mExtraScroll);
+ if (nextPos < mBoundPos) {
+ smoothScrollBy(Math.max(0, nextViewHeight + nextViewTop - extraScroll),
+ mScrollDuration, true);
+
+ mLastSeenPos = nextPos;
+
+ postOnAnimation(this);
+ } else {
+ if (nextViewTop > extraScroll) {
+ smoothScrollBy(nextViewTop - extraScroll, mScrollDuration, true);
+ }
+ }
+ break;
+ }
+
+ case MOVE_UP_POS: {
+ if (firstPos == mLastSeenPos) {
+ // No new views, let things keep going.
+ postOnAnimation(this);
+ return;
+ }
+
+ final View firstView = getChildAt(0);
+ if (firstView == null) {
+ return;
+ }
+ final int firstViewTop = firstView.getTop();
+ final int extraScroll = firstPos > 0 ?
+ Math.max(mExtraScroll, mListPadding.top) : mListPadding.top;
+
+ smoothScrollBy(firstViewTop - extraScroll, mScrollDuration, true);
+
+ mLastSeenPos = firstPos;
+
+ if (firstPos > mTargetPos) {
+ postOnAnimation(this);
+ }
+ break;
+ }
+
+ case MOVE_UP_BOUND: {
+ final int lastViewIndex = getChildCount() - 2;
+ if (lastViewIndex < 0) {
+ return;
+ }
+ final int lastPos = firstPos + lastViewIndex;
+
+ if (lastPos == mLastSeenPos) {
+ // No new views, let things keep going.
+ postOnAnimation(this);
+ return;
+ }
+
+ final View lastView = getChildAt(lastViewIndex);
+ final int lastViewHeight = lastView.getHeight();
+ final int lastViewTop = lastView.getTop();
+ final int lastViewPixelsShowing = listHeight - lastViewTop;
+ final int extraScroll = Math.max(mListPadding.top, mExtraScroll);
+ mLastSeenPos = lastPos;
+ if (lastPos > mBoundPos) {
+ smoothScrollBy(-(lastViewPixelsShowing - extraScroll), mScrollDuration, true);
+ postOnAnimation(this);
+ } else {
+ final int bottom = listHeight - extraScroll;
+ final int lastViewBottom = lastViewTop + lastViewHeight;
+ if (bottom > lastViewBottom) {
+ smoothScrollBy(-(bottom - lastViewBottom), mScrollDuration, true);
+ }
+ }
+ break;
+ }
+
+ case MOVE_OFFSET: {
+ if (mLastSeenPos == firstPos) {
+ // No new views, let things keep going.
+ postOnAnimation(this);
+ return;
+ }
+
+ mLastSeenPos = firstPos;
+
+ final int childCount = getChildCount();
+ final int position = mTargetPos;
+ final int lastPos = firstPos + childCount - 1;
+
+ int viewTravelCount = 0;
+ if (position < firstPos) {
+ viewTravelCount = firstPos - position + 1;
+ } else if (position > lastPos) {
+ viewTravelCount = position - lastPos;
+ }
+
+ // Estimate how many screens we should travel
+ final float screenTravelCount = (float) viewTravelCount / childCount;
+
+ final float modifier = Math.min(Math.abs(screenTravelCount), 1.f);
+ if (position < firstPos) {
+ final int distance = (int) (-getHeight() * modifier);
+ final int duration = (int) (mScrollDuration * modifier);
+ smoothScrollBy(distance, duration, true);
+ postOnAnimation(this);
+ } else if (position > lastPos) {
+ final int distance = (int) (getHeight() * modifier);
+ final int duration = (int) (mScrollDuration * modifier);
+ smoothScrollBy(distance, duration, true);
+ postOnAnimation(this);
+ } else {
+ // On-screen, just scroll.
+ final int targetTop = getChildAt(position - firstPos).getTop();
+ final int distance = targetTop - mOffsetFromTop;
+ final int duration = (int) (mScrollDuration *
+ ((float) Math.abs(distance) / getHeight()));
+ smoothScrollBy(distance, duration, true);
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+ }
+
+ /**
+ * Abstract position scroller that handles sub-position scrolling but has no
+ * understanding of layout.
+ */
+ abstract class AbsSubPositionScroller extends AbsPositionScroller {
private static final int DEFAULT_SCROLL_DURATION = 200;
- private SubScroller mSubScroller;
+ private final SubScroller mSubScroller = new SubScroller();
/**
* The target offset in pixels between the top of the list and the top
@@ -7171,9 +7179,6 @@
return;
}
- if (mSubScroller == null) {
- mSubScroller = new SubScroller();
- }
mSubScroller.startScroll(startSubRow, endSubRow, duration);
postOnAnimation(mAnimationFrame);
@@ -7228,28 +7233,67 @@
return Math.max(boundSubRow, targetSubRow);
}
- public void start(int position, int boundPosition) {
- scrollToPosition(position, false, 0, boundPosition, DEFAULT_SCROLL_DURATION);
- }
-
- public void startWithOffset(int position, int offset, int duration) {
- scrollToPosition(position, true, offset, INVALID_POSITION, duration);
- }
-
+ @Override
public void start(int position) {
scrollToPosition(position, false, 0, INVALID_POSITION, DEFAULT_SCROLL_DURATION);
}
+ @Override
+ public void start(int position, int boundPosition) {
+ scrollToPosition(position, false, 0, boundPosition, DEFAULT_SCROLL_DURATION);
+ }
+
+ @Override
+ public void startWithOffset(int position, int offset) {
+ scrollToPosition(position, true, offset, INVALID_POSITION, DEFAULT_SCROLL_DURATION);
+ }
+
+ @Override
+ public void startWithOffset(int position, int offset, int duration) {
+ scrollToPosition(position, true, offset, INVALID_POSITION, duration);
+ }
+
+ @Override
public void stop() {
removeCallbacks(mAnimationFrame);
}
+ /**
+ * Returns the height of a row, which is computed as the maximum height of
+ * the items in the row.
+ *
+ * @param row the row index
+ * @return row height in pixels
+ */
+ public abstract int getHeightForRow(int row);
+
+ /**
+ * Returns the row for the specified item position.
+ *
+ * @param position the item position
+ * @return the row index
+ */
+ public abstract int getRowForPosition(int position);
+
+ /**
+ * Returns the first item position within the specified row.
+ *
+ * @param row the row
+ * @return the position of the first item in the row
+ */
+ public abstract int getFirstPositionForRow(int row);
+
private void onAnimationFrame() {
final boolean shouldPost = mSubScroller.computePosition();
final float subRow = mSubScroller.getPosition();
final int row = (int) subRow;
final int position = getFirstPositionForRow(row);
+ if (position >= getCount()) {
+ // Invalid position, abort scrolling.
+ return;
+ }
+
final int rowHeight = getHeightForRow(row);
final int offset = (int) (rowHeight * (subRow - row));
final int addOffset = (int) (mOffset * mSubScroller.getInterpolatedValue());
@@ -7271,7 +7315,7 @@
/**
* Scroller capable of returning floating point positions.
*/
- private static class SubScroller {
+ static class SubScroller {
private final Interpolator mInterpolator;
private float mStartPosition;
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index 0b424f7..6743002 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -35,6 +35,8 @@
import android.view.accessibility.AccessibilityNodeInfo.CollectionInfo;
import android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo;
import android.view.animation.GridLayoutAnimationController;
+import android.widget.AbsListView.AbsPositionScroller;
+import android.widget.ListView.ListViewPositionScroller;
import android.widget.RemoteViews.RemoteView;
import java.lang.annotation.Retention;
@@ -1027,13 +1029,8 @@
}
@Override
- public int getRowForPosition(int position) {
- return position / mNumColumns;
- }
-
- @Override
- public int getFirstPositionForRow(int row) {
- return row * mNumColumns;
+ AbsPositionScroller createPositionScroller() {
+ return new GridViewPositionScroller();
}
@Override
@@ -2327,7 +2324,9 @@
final int columnsCount = getNumColumns();
final int rowsCount = getCount() / columnsCount;
- final CollectionInfo collectionInfo = CollectionInfo.obtain(columnsCount, rowsCount, false);
+ final int selectionMode = getSelectionModeForAccessibility();
+ final CollectionInfo collectionInfo = CollectionInfo.obtain(
+ columnsCount, rowsCount, false, selectionMode);
info.setCollectionInfo(collectionInfo);
}
@@ -2354,7 +2353,38 @@
final LayoutParams lp = (LayoutParams) view.getLayoutParams();
final boolean isHeading = lp != null && lp.viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER;
- final CollectionItemInfo itemInfo = CollectionItemInfo.obtain(column, 1, row, 1, isHeading);
+ final boolean isSelected = isItemChecked(position);
+ final CollectionItemInfo itemInfo = CollectionItemInfo.obtain(
+ column, 1, row, 1, isHeading, isSelected);
info.setCollectionItemInfo(itemInfo);
}
+
+ /**
+ * Sub-position scroller that understands the layout of a GridView.
+ */
+ class GridViewPositionScroller extends AbsSubPositionScroller {
+ @Override
+ public int getRowForPosition(int position) {
+ return position / mNumColumns;
+ }
+
+ @Override
+ public int getFirstPositionForRow(int row) {
+ return row * mNumColumns;
+ }
+
+ @Override
+ public int getHeightForRow(int row) {
+ final int firstRowPosition = row * mNumColumns;
+ final int lastRowPosition = Math.min(getCount(), firstRowPosition + mNumColumns);
+ int maxHeight = 0;
+ for (int i = firstRowPosition; i < lastRowPosition; i++) {
+ final int height = getHeightForPosition(i);
+ if (height > maxHeight) {
+ maxHeight = height;
+ }
+ }
+ return maxHeight;
+ }
+ }
}
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index f937cd6..cef2138 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -3775,13 +3775,8 @@
}
@Override
- public int getRowForPosition(int position) {
- return position;
- }
-
- @Override
- public int getFirstPositionForRow(int row) {
- return row;
+ AbsPositionScroller createPositionScroller() {
+ return new ListViewPositionScroller();
}
@Override
@@ -3796,7 +3791,8 @@
info.setClassName(ListView.class.getName());
final int count = getCount();
- final CollectionInfo collectionInfo = CollectionInfo.obtain(1, count, false);
+ final int selectionMode = getSelectionModeForAccessibility();
+ final CollectionInfo collectionInfo = CollectionInfo.obtain(1, count, false, selectionMode);
info.setCollectionInfo(collectionInfo);
}
@@ -3807,7 +3803,29 @@
final LayoutParams lp = (LayoutParams) view.getLayoutParams();
final boolean isHeading = lp != null && lp.viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER;
- final CollectionItemInfo itemInfo = CollectionItemInfo.obtain(0, 1, position, 1, isHeading);
+ final boolean isSelected = isItemChecked(position);
+ final CollectionItemInfo itemInfo = CollectionItemInfo.obtain(
+ 0, 1, position, 1, isHeading, isSelected);
info.setCollectionItemInfo(itemInfo);
}
+
+ /**
+ * Sub-position scroller that understands the layout of a ListView.
+ */
+ class ListViewPositionScroller extends AbsSubPositionScroller {
+ @Override
+ public int getRowForPosition(int position) {
+ return position;
+ }
+
+ @Override
+ public int getFirstPositionForRow(int row) {
+ return row;
+ }
+
+ @Override
+ public int getHeightForRow(int row) {
+ return getHeightForPosition(row);
+ }
+ }
}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 3419f15..f5c18f5 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -30,10 +30,12 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.storage.IMountService;
+import android.os.storage.StorageManager;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Slog;
import android.view.IWindowManager;
import android.view.View;
import android.widget.Button;
@@ -498,6 +500,13 @@
getLockSettings().setLockPattern(patternToString(pattern), getCurrentOrCallingUserId());
DevicePolicyManager dpm = getDevicePolicyManager();
if (pattern != null) {
+
+ int userHandle = getCurrentOrCallingUserId();
+ if (userHandle == UserHandle.USER_OWNER) {
+ String stringPattern = patternToString(pattern);
+ updateEncryptionPassword(StorageManager.CRYPT_TYPE_PATTERN, stringPattern);
+ }
+
setBoolean(PATTERN_EVER_CHOSEN_KEY, true);
if (!isFallback) {
deleteGallery();
@@ -565,7 +574,7 @@
}
/** Update the encryption password if it is enabled **/
- private void updateEncryptionPassword(String password) {
+ private void updateEncryptionPassword(int type, String password) {
DevicePolicyManager dpm = getDevicePolicyManager();
if (dpm.getStorageEncryptionStatus(getCurrentOrCallingUserId())
!= DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE) {
@@ -580,7 +589,7 @@
IMountService mountService = IMountService.Stub.asInterface(service);
try {
- mountService.changeEncryptionPassword(password);
+ mountService.changeEncryptionPassword(type, password);
} catch (RemoteException e) {
Log.e(TAG, "Error changing encryption password", e);
}
@@ -623,12 +632,15 @@
getLockSettings().setLockPassword(password, userHandle);
DevicePolicyManager dpm = getDevicePolicyManager();
if (password != null) {
+ int computedQuality = computePasswordQuality(password);
+
if (userHandle == UserHandle.USER_OWNER) {
// Update the encryption password.
- updateEncryptionPassword(password);
+ int type = computedQuality == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
+ ? StorageManager.CRYPT_TYPE_PIN : StorageManager.CRYPT_TYPE_PASSWORD;
+ updateEncryptionPassword(type, password);
}
- int computedQuality = computePasswordQuality(password);
if (!isFallback) {
deleteGallery();
setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality), userHandle);
@@ -675,8 +687,7 @@
0, 0, 0, 0, 0, 0, 0, userHandle);
}
// Add the password to the password history. We assume all
- // password
- // hashes have the same length for simplicity of implementation.
+ // password hashes have the same length for simplicity of implementation.
String passwordHistory = getString(PASSWORD_HISTORY_KEY, userHandle);
if (passwordHistory == null) {
passwordHistory = new String();
@@ -695,6 +706,11 @@
}
setString(PASSWORD_HISTORY_KEY, passwordHistory, userHandle);
} else {
+ if (userHandle == UserHandle.USER_OWNER) {
+ // Update the encryption password.
+ updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, password);
+ }
+
dpm.setActivePasswordState(
DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0,
userHandle);
diff --git a/core/java/com/android/server/SystemService.java b/core/java/com/android/server/SystemService.java
index 0c89f94..194a084 100644
--- a/core/java/com/android/server/SystemService.java
+++ b/core/java/com/android/server/SystemService.java
@@ -85,14 +85,6 @@
}
/**
- * Services are not yet available. This is a good place to do setup work that does
- * not require other services.
- *
- * @param context The system context.
- */
- public void onCreate(Context context) {}
-
- /**
* Called when the dependencies listed in the @Service class-annotation are available
* and after the chosen start phase.
* When this method returns, the service should be published.
diff --git a/core/jni/android_view_SurfaceSession.cpp b/core/jni/android_view_SurfaceSession.cpp
index 87e339c..609c565 100644
--- a/core/jni/android_view_SurfaceSession.cpp
+++ b/core/jni/android_view_SurfaceSession.cpp
@@ -35,22 +35,22 @@
sp<SurfaceComposerClient> android_view_SurfaceSession_getClient(
JNIEnv* env, jobject surfaceSessionObj) {
return reinterpret_cast<SurfaceComposerClient*>(
- env->GetIntField(surfaceSessionObj, gSurfaceSessionClassInfo.mNativeClient));
+ env->GetLongField(surfaceSessionObj, gSurfaceSessionClassInfo.mNativeClient));
}
-static jint nativeCreate(JNIEnv* env, jclass clazz) {
+static jlong nativeCreate(JNIEnv* env, jclass clazz) {
SurfaceComposerClient* client = new SurfaceComposerClient();
client->incStrong((void*)nativeCreate);
- return reinterpret_cast<jint>(client);
+ return reinterpret_cast<jlong>(client);
}
-static void nativeDestroy(JNIEnv* env, jclass clazz, jint ptr) {
+static void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
SurfaceComposerClient* client = reinterpret_cast<SurfaceComposerClient*>(ptr);
client->decStrong((void*)nativeCreate);
}
-static void nativeKill(JNIEnv* env, jclass clazz, jint ptr) {
+static void nativeKill(JNIEnv* env, jclass clazz, jlong ptr) {
SurfaceComposerClient* client = reinterpret_cast<SurfaceComposerClient*>(ptr);
client->dispose();
}
@@ -58,11 +58,11 @@
static JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
- { "nativeCreate", "()I",
+ { "nativeCreate", "()J",
(void*)nativeCreate },
- { "nativeDestroy", "(I)V",
+ { "nativeDestroy", "(J)V",
(void*)nativeDestroy },
- { "nativeKill", "(I)V",
+ { "nativeKill", "(J)V",
(void*)nativeKill }
};
@@ -72,7 +72,7 @@
LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
jclass clazz = env->FindClass("android/view/SurfaceSession");
- gSurfaceSessionClassInfo.mNativeClient = env->GetFieldID(clazz, "mNativeClient", "I");
+ gSurfaceSessionClassInfo.mNativeClient = env->GetFieldID(clazz, "mNativeClient", "J");
return 0;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index b7b1222..943cbfe 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1549,7 +1549,7 @@
@hide -->
<permission android:name="android.permission.FORCE_STOP_PACKAGES"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
- android:protectionLevel="signature"
+ android:protectionLevel="signature|system"
android:label="@string/permlab_forceStopPackages"
android:description="@string/permdesc_forceStopPackages" />
diff --git a/core/res/res/values-ja/bools.xml b/core/res/res/values-ja/bools.xml
deleted file mode 100644
index 59cf744..0000000
--- a/core/res/res/values-ja/bools.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<resources>
- <bool name="flip_controller_fallback_keys">true</bool>
-</resources>
-
diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml
index 10a5d85..18e4f2f 100644
--- a/core/res/res/values/bools.xml
+++ b/core/res/res/values/bools.xml
@@ -26,5 +26,4 @@
<bool name="show_ongoing_ime_switcher">true</bool>
<bool name="action_bar_expanded_action_views_exclusive">true</bool>
<bool name="target_honeycomb_needs_options_menu">true</bool>
- <bool name="flip_controller_fallback_keys">false</bool>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 000caef..91bef50 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1175,10 +1175,14 @@
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE]-->
<string name="permdesc_manageCaCertificates">Allows the app to install and uninstall CA certificates as trusted credentials.</string>
- <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permlab_bindIdleService">bind to idle services</string>
- <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permdesc_bindIdleService">This permission allows the Android system to bind to an application\'s idle services.</string>
+ <!-- Title of a permission that is never presented to the user. This is not a
+ permission that an application must be granted by the user. Instead, it
+ is part of a mechanism that applications use to indicate to the system
+ that they want to do occasional work while the device is idle. -->
+ <string name="permlab_bindIdleService">run application during idle time</string>
+ <!-- Description of an application permission, so that the user can understand
+ what is being done if they are curious. -->
+ <string name="permdesc_bindIdleService">This permission allows the Android system to run the application in the background while the device is not in use.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_diagnostic">read/write to resources owned by diag</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 3ac762f..e574e8d 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -289,7 +289,6 @@
<java-symbol type="bool" name="config_useFixedVolume" />
<java-symbol type="bool" name="config_forceDefaultOrientation" />
<java-symbol type="bool" name="config_wifi_batched_scan_supported" />
- <java-symbol type="bool" name="flip_controller_fallback_keys" />
<java-symbol type="integer" name="config_cursorWindowSize" />
<java-symbol type="integer" name="config_extraFreeKbytesAdjust" />
diff --git a/data/keyboards/Generic.kcm b/data/keyboards/Generic.kcm
index 695a74f..01d22ee 100644
--- a/data/keyboards/Generic.kcm
+++ b/data/keyboards/Generic.kcm
@@ -477,4 +477,128 @@
ctrl: fallback MENU
}
-### Gamepad buttons are handled by the view root ###
+### Gamepad buttons ###
+
+key BUTTON_A {
+ base: fallback DPAD_CENTER
+}
+
+key BUTTON_B {
+ base: fallback BACK
+}
+
+key BUTTON_C {
+ base: fallback DPAD_CENTER
+}
+
+key BUTTON_X {
+ base: fallback DPAD_CENTER
+}
+
+key BUTTON_Y {
+ base: fallback BACK
+}
+
+key BUTTON_Z {
+ base: fallback DPAD_CENTER
+}
+
+key BUTTON_L1 {
+ base: none
+}
+
+key BUTTON_R1 {
+ base: none
+}
+
+key BUTTON_L2 {
+ base: none
+}
+
+key BUTTON_R2 {
+ base: none
+}
+
+key BUTTON_THUMBL {
+ base: fallback DPAD_CENTER
+}
+
+key BUTTON_THUMBR {
+ base: fallback DPAD_CENTER
+}
+
+key BUTTON_START {
+ base: fallback DPAD_CENTER
+}
+
+key BUTTON_SELECT {
+ base: fallback MENU
+}
+
+key BUTTON_MODE {
+ base: fallback MENU
+}
+
+key BUTTON_1 {
+ base: fallback DPAD_CENTER
+}
+
+key BUTTON_2 {
+ base: fallback DPAD_CENTER
+}
+
+key BUTTON_3 {
+ base: fallback DPAD_CENTER
+}
+
+key BUTTON_4 {
+ base: fallback DPAD_CENTER
+}
+
+key BUTTON_5 {
+ base: fallback DPAD_CENTER
+}
+
+key BUTTON_6 {
+ base: fallback DPAD_CENTER
+}
+
+key BUTTON_7 {
+ base: fallback DPAD_CENTER
+}
+
+key BUTTON_8 {
+ base: fallback DPAD_CENTER
+}
+
+key BUTTON_9 {
+ base: fallback DPAD_CENTER
+}
+
+key BUTTON_10 {
+ base: fallback DPAD_CENTER
+}
+
+key BUTTON_11 {
+ base: fallback DPAD_CENTER
+}
+
+key BUTTON_12 {
+ base: fallback DPAD_CENTER
+}
+
+key BUTTON_13 {
+ base: fallback DPAD_CENTER
+}
+
+key BUTTON_14 {
+ base: fallback DPAD_CENTER
+}
+
+key BUTTON_15 {
+ base: fallback DPAD_CENTER
+}
+
+key BUTTON_16 {
+ base: fallback DPAD_CENTER
+}
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index a345e91..331a34e 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -113,6 +113,15 @@
*/
public static final int SWEEP_GRADIENT = 2;
+ /** Radius is in pixels. */
+ private static final int RADIUS_TYPE_PIXELS = 0;
+
+ /** Radius is a fraction of the base size. */
+ private static final int RADIUS_TYPE_FRACTION = 1;
+
+ /** Radius is a fraction of the bounds size. */
+ private static final int RADIUS_TYPE_FRACTION_PARENT = 2;
+
private GradientState mGradientState;
private final Paint mFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
@@ -892,18 +901,26 @@
y0 = r.top + (r.bottom - r.top) * st.mCenterY;
float radius = st.mGradientRadius;
- if (st.mGradientRadiusUnit == TypedValue.COMPLEX_UNIT_FRACTION) {
+ if (st.mGradientRadiusType == RADIUS_TYPE_FRACTION) {
radius *= Math.min(st.mWidth, st.mHeight);
- } else if (st.mGradientRadiusUnit == TypedValue.COMPLEX_UNIT_FRACTION_PARENT) {
+ } else if (st.mGradientRadiusType == RADIUS_TYPE_FRACTION_PARENT) {
radius *= Math.min(r.width(), r.height());
}
-
+
if (st.mUseLevel) {
radius *= getLevel() / 10000.0f;
}
+
mGradientRadius = radius;
+
+ if (radius == 0) {
+ // We can't have a shader with zero radius, so let's
+ // have a very, very small radius.
+ radius = 0.001f;
+ }
+
mFillPaint.setShader(new RadialGradient(
- x0, y0, mGradientRadius, colors, null, Shader.TileMode.CLAMP));
+ x0, y0, radius, colors, null, Shader.TileMode.CLAMP));
} else if (st.mGradient == SWEEP_GRADIENT) {
x0 = r.left + (r.right - r.left) * st.mCenterX;
y0 = r.top + (r.bottom - r.top) * st.mCenterY;
@@ -1082,16 +1099,24 @@
com.android.internal.R.styleable.GradientDrawableGradient_gradientRadius);
if (tv != null) {
final float radius;
- final int unit;
+ final int radiusType;
if (tv.type == TypedValue.TYPE_FRACTION) {
radius = tv.getFraction(1.0f, 1.0f);
- unit = tv.data & TypedValue.COMPLEX_UNIT_MASK;
+
+ final int unit = (tv.data >> TypedValue.COMPLEX_UNIT_SHIFT)
+ & TypedValue.COMPLEX_UNIT_MASK;
+ if (unit == TypedValue.COMPLEX_UNIT_FRACTION_PARENT) {
+ radiusType = RADIUS_TYPE_FRACTION_PARENT;
+ } else {
+ radiusType = RADIUS_TYPE_FRACTION;
+ }
} else {
radius = tv.getDimension(r.getDisplayMetrics());
- unit = TypedValue.COMPLEX_UNIT_PX;
+ radiusType = RADIUS_TYPE_PIXELS;
}
+
st.mGradientRadius = radius;
- st.mGradientRadiusUnit = unit;
+ st.mGradientRadiusType = radiusType;
} else if (gradientType == RADIAL_GRADIENT) {
throw new XmlPullParserException(
a.getPositionDescription()
@@ -1253,7 +1278,7 @@
private float mCenterX = 0.5f;
private float mCenterY = 0.5f;
private float mGradientRadius = 0.5f;
- private int mGradientRadiusUnit = TypedValue.COMPLEX_UNIT_PX;
+ private int mGradientRadiusType = RADIUS_TYPE_PIXELS;
private boolean mUseLevel;
private boolean mUseLevelForShape;
private boolean mOpaque;
@@ -1295,7 +1320,7 @@
mCenterX = state.mCenterX;
mCenterY = state.mCenterY;
mGradientRadius = state.mGradientRadius;
- mGradientRadiusUnit = state.mGradientRadiusUnit;
+ mGradientRadiusType = state.mGradientRadiusType;
mUseLevel = state.mUseLevel;
mUseLevelForShape = state.mUseLevelForShape;
mOpaque = state.mOpaque;
@@ -1414,7 +1439,7 @@
public void setGradientRadius(float gradientRadius, int type) {
mGradientRadius = gradientRadius;
- mGradientRadiusUnit = type;
+ mGradientRadiusType = type;
}
}
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 9fe3ac1..115786c 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -333,6 +333,10 @@
* <p>
* The application is responsible for calling release() on the Surface when
* done.
+ * <p>
+ * The Surface must be rendered with a hardware-accelerated API, such as OpenGL ES.
+ * {@link android.view.Surface#lockCanvas(android.graphics.Rect)} may fail or produce
+ * unexpected results.
*/
public native final Surface createInputSurface();
diff --git a/rs/java/android/renderscript/AllocationAdapter.java b/rs/java/android/renderscript/AllocationAdapter.java
index 6c1b1ed..3522a52 100644
--- a/rs/java/android/renderscript/AllocationAdapter.java
+++ b/rs/java/android/renderscript/AllocationAdapter.java
@@ -219,7 +219,6 @@
}
static public AllocationAdapter create2D(RenderScript rs, Allocation a) {
- android.util.Log.e("rs", "create2d " + a);
rs.validate();
AllocationAdapter aa = new AllocationAdapter(0, rs, a);
aa.mConstrainedLOD = true;
diff --git a/rs/java/android/renderscript/BaseObj.java b/rs/java/android/renderscript/BaseObj.java
index 842aa23..b386dd7 100644
--- a/rs/java/android/renderscript/BaseObj.java
+++ b/rs/java/android/renderscript/BaseObj.java
@@ -122,7 +122,8 @@
// must include nObjDestroy in the critical section
ReentrantReadWriteLock.ReadLock rlock = mRS.mRWLock.readLock();
rlock.lock();
- if(mRS.isAlive()) {
+ // AllocationAdapters are BaseObjs with an ID of 0 but should not be passed to nObjDestroy
+ if(mRS.isAlive() && mID != 0) {
mRS.nObjDestroy(mID);
}
rlock.unlock();
diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java
index a4a9070..89dba9f 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -99,6 +99,20 @@
static File mCacheDir;
+ // this should be a monotonically increasing ID
+ // used in conjunction with the API version of a device
+ static final long sMinorID = 1;
+
+ /**
+ * Returns an identifier that can be used to identify a particular
+ * minor version of RS.
+ *
+ * @hide
+ */
+ public static long getMinorID() {
+ return sMinorID;
+ }
+
/**
* Sets the directory to use as a persistent storage for the
* renderscript object file cache.
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index e6e4bca..0f2e56c 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -73,13 +73,16 @@
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
+import org.apache.commons.codec.binary.Hex;
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
@@ -189,6 +192,12 @@
public static final int FstrimCompleted = 700;
}
+ /** List of crypto types.
+ * These must match CRYPT_TYPE_XXX in cryptfs.h AND their
+ * corresponding commands in CommandListener.cpp */
+ public static final String[] CRYPTO_TYPES
+ = { "password", "default", "pattern", "pin" };
+
private Context mContext;
private NativeDaemonConnector mConnector;
@@ -2036,6 +2045,14 @@
}
}
+ private String toHex(String password) {
+ if (password == null) {
+ return null;
+ }
+ byte[] bytes = password.getBytes(StandardCharsets.UTF_8);
+ return new String(Hex.encodeHex(bytes));
+ }
+
@Override
public int decryptStorage(String password) {
if (TextUtils.isEmpty(password)) {
@@ -2053,7 +2070,7 @@
final NativeDaemonEvent event;
try {
- event = mConnector.execute("cryptfs", "checkpw", new SensitiveArg(password));
+ event = mConnector.execute("cryptfs", "checkpw", new SensitiveArg(toHex(password)));
final int code = Integer.parseInt(event.getMessage());
if (code == 0) {
@@ -2092,7 +2109,8 @@
}
try {
- mConnector.execute("cryptfs", "enablecrypto", "inplace", new SensitiveArg(password));
+ mConnector.execute("cryptfs", "enablecrypto", "inplace",
+ new SensitiveArg(toHex(password)));
} catch (NativeDaemonConnectorException e) {
// Encryption failed
return e.getCode();
@@ -2101,11 +2119,11 @@
return 0;
}
- public int changeEncryptionPassword(String password) {
- if (TextUtils.isEmpty(password)) {
- throw new IllegalArgumentException("password cannot be empty");
- }
-
+ /** Set the password for encrypting the master key.
+ * @param type One of the CRYPTO_TYPE_XXX consts defined in StorageManager.
+ * @param password The password to set.
+ */
+ public int changeEncryptionPassword(int type, String password) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
"no permission to access the crypt keeper");
@@ -2117,7 +2135,8 @@
final NativeDaemonEvent event;
try {
- event = mConnector.execute("cryptfs", "changepw", new SensitiveArg(password));
+ event = mConnector.execute("cryptfs", "changepw", CRYPTO_TYPES[type],
+ new SensitiveArg(toHex(password)));
return Integer.parseInt(event.getMessage());
} catch (NativeDaemonConnectorException e) {
// Encryption failed
@@ -2150,7 +2169,7 @@
final NativeDaemonEvent event;
try {
- event = mConnector.execute("cryptfs", "verifypw", new SensitiveArg(password));
+ event = mConnector.execute("cryptfs", "verifypw", new SensitiveArg(toHex(password)));
Slog.i(TAG, "cryptfs verifypw => " + event.getMessage());
return Integer.parseInt(event.getMessage());
} catch (NativeDaemonConnectorException e) {
@@ -2159,6 +2178,29 @@
}
}
+ /**
+ * Get the type of encryption used to encrypt the master key.
+ * @return The type, one of the CRYPT_TYPE_XXX consts from StorageManager.
+ */
+ @Override
+ public int getPasswordType() throws RemoteException {
+
+ waitForReady();
+
+ final NativeDaemonEvent event;
+ try {
+ event = mConnector.execute("cryptfs", "getpwtype");
+ for (int i = 0; i < CRYPTO_TYPES.length; ++i) {
+ if (CRYPTO_TYPES[i].equals(event.getMessage()))
+ return i;
+ }
+
+ throw new IllegalStateException("unexpected return from cryptfs");
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
@Override
public int mkdirs(String callingPkg, String appPath) {
final int userId = UserHandle.getUserId(Binder.getCallingUid());
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index f5acc4c..ffb113c 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -18,7 +18,9 @@
import com.android.internal.util.DumpUtils;
import com.android.server.FgThread;
+import com.android.server.SystemService;
+import android.Manifest;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -35,6 +37,8 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
+import android.service.dreams.DreamManagerInternal;
+import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
import android.util.Slog;
@@ -50,7 +54,7 @@
*
* @hide
*/
-public final class DreamManagerService extends IDreamManager.Stub {
+public final class DreamManagerService extends SystemService {
private static final boolean DEBUG = false;
private static final String TAG = "DreamManagerService";
@@ -67,6 +71,7 @@
private boolean mCurrentDreamIsTest;
public DreamManagerService(Context context) {
+ super(context);
mContext = context;
mHandler = new DreamHandler(FgThread.get().getLooper());
mController = new DreamController(context, mHandler, mControllerListener);
@@ -74,27 +79,27 @@
mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
}
- public void systemRunning() {
- mContext.registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- synchronized (mLock) {
- stopDreamLocked();
- }
- }
- }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
+ @Override
+ public void onStart() {
+ publishBinderService(DreamService.DREAM_SERVICE, new BinderService());
+ publishLocalService(DreamManagerInternal.class, new LocalService());
}
@Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (mContext.checkCallingOrSelfPermission("android.permission.DUMP")
- != PackageManager.PERMISSION_GRANTED) {
- pw.println("Permission Denial: can't dump DreamManager from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid());
- return;
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ synchronized (mLock) {
+ stopDreamLocked();
+ }
+ }
+ }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
}
+ }
+ private void dumpInternal(PrintWriter pw) {
pw.println("DREAM MANAGER (dumpsys dreams)");
pw.println();
@@ -112,156 +117,57 @@
}, pw, 200);
}
- @Override // Binder call
- public ComponentName[] getDreamComponents() {
- checkPermission(android.Manifest.permission.READ_DREAM_STATE);
-
- final int userId = UserHandle.getCallingUserId();
- final long ident = Binder.clearCallingIdentity();
- try {
- return getDreamComponentsForUser(userId);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- @Override // Binder call
- public void setDreamComponents(ComponentName[] componentNames) {
- checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
-
- final int userId = UserHandle.getCallingUserId();
- final long ident = Binder.clearCallingIdentity();
- try {
- Settings.Secure.putStringForUser(mContext.getContentResolver(),
- Settings.Secure.SCREENSAVER_COMPONENTS,
- componentsToString(componentNames),
- userId);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- @Override // Binder call
- public ComponentName getDefaultDreamComponent() {
- checkPermission(android.Manifest.permission.READ_DREAM_STATE);
-
- final int userId = UserHandle.getCallingUserId();
- final long ident = Binder.clearCallingIdentity();
- try {
- String name = Settings.Secure.getStringForUser(mContext.getContentResolver(),
- Settings.Secure.SCREENSAVER_DEFAULT_COMPONENT,
- userId);
- return name == null ? null : ComponentName.unflattenFromString(name);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- @Override // Binder call
- public boolean isDreaming() {
- checkPermission(android.Manifest.permission.READ_DREAM_STATE);
-
+ private boolean isDreamingInternal() {
synchronized (mLock) {
return mCurrentDreamToken != null && !mCurrentDreamIsTest;
}
}
- @Override // Binder call
- public void dream() {
- checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
-
- final long ident = Binder.clearCallingIdentity();
- try {
- // Ask the power manager to nap. It will eventually call back into
- // startDream() if/when it is appropriate to start dreaming.
- // Because napping could cause the screen to turn off immediately if the dream
- // cannot be started, we keep one eye open and gently poke user activity.
- long time = SystemClock.uptimeMillis();
- mPowerManager.userActivity(time, true /*noChangeLights*/);
- mPowerManager.nap(time);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
+ private void requestDreamInternal() {
+ // Ask the power manager to nap. It will eventually call back into
+ // startDream() if/when it is appropriate to start dreaming.
+ // Because napping could cause the screen to turn off immediately if the dream
+ // cannot be started, we keep one eye open and gently poke user activity.
+ long time = SystemClock.uptimeMillis();
+ mPowerManager.userActivity(time, true /*noChangeLights*/);
+ mPowerManager.nap(time);
}
- @Override // Binder call
- public void testDream(ComponentName dream) {
- checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
+ private void requestAwakenInternal() {
+ // Treat an explicit request to awaken as user activity so that the
+ // device doesn't immediately go to sleep if the timeout expired,
+ // for example when being undocked.
+ long time = SystemClock.uptimeMillis();
+ mPowerManager.userActivity(time, false /*noChangeLights*/);
+ stopDreamInternal();
+ }
- if (dream == null) {
- throw new IllegalArgumentException("dream must not be null");
+ private void finishSelfInternal(IBinder token) {
+ if (DEBUG) {
+ Slog.d(TAG, "Dream finished: " + token);
}
- final int callingUserId = UserHandle.getCallingUserId();
- final int currentUserId = ActivityManager.getCurrentUser();
- if (callingUserId != currentUserId) {
- // This check is inherently prone to races but at least it's something.
- Slog.w(TAG, "Aborted attempt to start a test dream while a different "
- + " user is active: callingUserId=" + callingUserId
- + ", currentUserId=" + currentUserId);
- return;
- }
- final long ident = Binder.clearCallingIdentity();
- try {
- synchronized (mLock) {
- startDreamLocked(dream, true /*isTest*/, callingUserId);
+ // Note that a dream finishing and self-terminating is not
+ // itself considered user activity. If the dream is ending because
+ // the user interacted with the device then user activity will already
+ // have been poked so the device will stay awake a bit longer.
+ // If the dream is ending on its own for other reasons and no wake
+ // locks are held and the user activity timeout has expired then the
+ // device may simply go to sleep.
+ synchronized (mLock) {
+ if (mCurrentDreamToken == token) {
+ stopDreamLocked();
}
- } finally {
- Binder.restoreCallingIdentity(ident);
}
}
- @Override // Binder call
- public void awaken() {
- checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
-
- final long ident = Binder.clearCallingIdentity();
- try {
- // Treat an explicit request to awaken as user activity so that the
- // device doesn't immediately go to sleep if the timeout expired,
- // for example when being undocked.
- long time = SystemClock.uptimeMillis();
- mPowerManager.userActivity(time, false /*noChangeLights*/);
- stopDream();
- } finally {
- Binder.restoreCallingIdentity(ident);
+ private void testDreamInternal(ComponentName dream, int userId) {
+ synchronized (mLock) {
+ startDreamLocked(dream, true /*isTest*/, userId);
}
}
- @Override // Binder call
- public void finishSelf(IBinder token) {
- // Requires no permission, called by Dream from an arbitrary process.
- if (token == null) {
- throw new IllegalArgumentException("token must not be null");
- }
-
- final long ident = Binder.clearCallingIdentity();
- try {
- if (DEBUG) {
- Slog.d(TAG, "Dream finished: " + token);
- }
-
- // Note that a dream finishing and self-terminating is not
- // itself considered user activity. If the dream is ending because
- // the user interacted with the device then user activity will already
- // have been poked so the device will stay awake a bit longer.
- // If the dream is ending on its own for other reasons and no wake
- // locks are held and the user activity timeout has expired then the
- // device may simply go to sleep.
- synchronized (mLock) {
- if (mCurrentDreamToken == token) {
- stopDreamLocked();
- }
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- /**
- * Called by the power manager to start a dream.
- */
- public void startDream() {
+ private void startDreamInternal() {
int userId = ActivityManager.getCurrentUser();
ComponentName dream = chooseDreamForUser(userId);
if (dream != null) {
@@ -271,10 +177,7 @@
}
}
- /**
- * Called by the power manager to stop a dream.
- */
- public void stopDream() {
+ private void stopDreamInternal() {
synchronized (mLock) {
stopDreamLocked();
}
@@ -305,7 +208,7 @@
// fallback to the default dream component if necessary
if (validComponents.isEmpty()) {
- ComponentName defaultDream = getDefaultDreamComponent();
+ ComponentName defaultDream = getDefaultDreamComponentForUser(userId);
if (defaultDream != null) {
Slog.w(TAG, "Falling back to default dream " + defaultDream);
validComponents.add(defaultDream);
@@ -314,6 +217,20 @@
return validComponents.toArray(new ComponentName[validComponents.size()]);
}
+ private void setDreamComponentsForUser(int userId, ComponentName[] componentNames) {
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Settings.Secure.SCREENSAVER_COMPONENTS,
+ componentsToString(componentNames),
+ userId);
+ }
+
+ private ComponentName getDefaultDreamComponentForUser(int userId) {
+ String name = Settings.Secure.getStringForUser(mContext.getContentResolver(),
+ Settings.Secure.SCREENSAVER_DEFAULT_COMPONENT,
+ userId);
+ return name == null ? null : ComponentName.unflattenFromString(name);
+ }
+
private boolean serviceExists(ComponentName name) {
try {
return name != null && mContext.getPackageManager().getServiceInfo(name, 0) != null;
@@ -423,4 +340,155 @@
super(looper, null, true /*async*/);
}
}
+
+ private final class BinderService extends IDreamManager.Stub {
+ @Override // Binder call
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump DreamManager from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ dumpInternal(pw);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
+ public ComponentName[] getDreamComponents() {
+ checkPermission(android.Manifest.permission.READ_DREAM_STATE);
+
+ final int userId = UserHandle.getCallingUserId();
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return getDreamComponentsForUser(userId);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
+ public void setDreamComponents(ComponentName[] componentNames) {
+ checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
+
+ final int userId = UserHandle.getCallingUserId();
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ setDreamComponentsForUser(userId, componentNames);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
+ public ComponentName getDefaultDreamComponent() {
+ checkPermission(android.Manifest.permission.READ_DREAM_STATE);
+
+ final int userId = UserHandle.getCallingUserId();
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return getDefaultDreamComponentForUser(userId);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
+ public boolean isDreaming() {
+ checkPermission(android.Manifest.permission.READ_DREAM_STATE);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return isDreamingInternal();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
+ public void dream() {
+ checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ requestDreamInternal();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
+ public void testDream(ComponentName dream) {
+ if (dream == null) {
+ throw new IllegalArgumentException("dream must not be null");
+ }
+ checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
+
+ final int callingUserId = UserHandle.getCallingUserId();
+ final int currentUserId = ActivityManager.getCurrentUser();
+ if (callingUserId != currentUserId) {
+ // This check is inherently prone to races but at least it's something.
+ Slog.w(TAG, "Aborted attempt to start a test dream while a different "
+ + " user is active: callingUserId=" + callingUserId
+ + ", currentUserId=" + currentUserId);
+ return;
+ }
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ testDreamInternal(dream, callingUserId);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
+ public void awaken() {
+ checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ requestAwakenInternal();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
+ public void finishSelf(IBinder token) {
+ // Requires no permission, called by Dream from an arbitrary process.
+ if (token == null) {
+ throw new IllegalArgumentException("token must not be null");
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ finishSelfInternal(token);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ private final class LocalService extends DreamManagerInternal {
+ @Override
+ public void startDream() {
+ startDreamInternal();
+ }
+
+ @Override
+ public void stopDream() {
+ stopDreamInternal();
+ }
+
+ @Override
+ public boolean isDreaming() {
+ return isDreamingInternal();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index b2344e6..ecde184 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -20,12 +20,12 @@
import com.android.internal.app.IBatteryStats;
import com.android.server.BatteryService;
import com.android.server.EventLogTags;
+import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
import com.android.server.twilight.TwilightManager;
import com.android.server.Watchdog;
-import com.android.server.dreams.DreamManagerService;
import android.Manifest;
import android.content.BroadcastReceiver;
@@ -57,6 +57,7 @@
import android.os.UserHandle;
import android.os.WorkSource;
import android.provider.Settings;
+import android.service.dreams.DreamManagerInternal;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
@@ -181,7 +182,7 @@
private DisplayPowerController mDisplayPowerController;
private WirelessChargerDetector mWirelessChargerDetector;
private SettingsObserver mSettingsObserver;
- private DreamManagerService mDreamManager;
+ private DreamManagerInternal mDreamManager;
private Light mAttentionLight;
private final Object mLock = new Object();
@@ -433,10 +434,10 @@
}
}
- public void systemReady(TwilightManager twilight, DreamManagerService dreamManager) {
+ public void systemReady() {
synchronized (mLock) {
mSystemReady = true;
- mDreamManager = dreamManager;
+ mDreamManager = LocalServices.getService(DreamManagerInternal.class);
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mScreenBrightnessSettingMinimum = pm.getMinimumScreenBrightnessSetting();
@@ -454,7 +455,8 @@
// The display power controller runs on the power manager service's
// own handler thread to ensure timely operation.
mDisplayPowerController = new DisplayPowerController(mHandler.getLooper(),
- mContext, mNotifier, mLightsManager, twilight, sensorManager,
+ mContext, mNotifier, mLightsManager,
+ LocalServices.getService(TwilightManager.class), sensorManager,
mDisplaySuspendBlocker, mDisplayBlanker,
mDisplayPowerControllerCallbacks, mHandler);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 85e5e23..943471a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -74,7 +74,6 @@
import com.android.server.search.SearchManagerService;
import com.android.server.statusbar.StatusBarManagerService;
import com.android.server.storage.DeviceStorageMonitorService;
-import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightService;
import com.android.server.usb.UsbService;
import com.android.server.wallpaper.WallpaperManagerService;
@@ -303,7 +302,6 @@
DockObserver dock = null;
UsbService usb = null;
SerialService serial = null;
- TwilightManager twilight = null;
RecognitionManagerService recognition = null;
NetworkTimeUpdateService networkTimeUpdater = null;
CommonTimeManagementService commonTimeMgmtService = null;
@@ -460,7 +458,6 @@
CountryDetectorService countryDetector = null;
TextServicesManagerService tsms = null;
LockSettingsService lockSettings = null;
- DreamManagerService dreamy = null;
AssetAtlasService atlas = null;
MediaRouterService mediaRouter = null;
@@ -776,7 +773,6 @@
}
mSystemServiceManager.startService(TwilightService.class);
- twilight = LocalServices.getService(TwilightManager.class);
mSystemServiceManager.startService(UiModeManagerService.class);
@@ -852,16 +848,10 @@
}
}
- if (!disableNonCoreServices &&
- context.getResources().getBoolean(R.bool.config_dreamsSupported)) {
- try {
- Slog.i(TAG, "Dreams Service");
- // Dreams (interactive idle-time views, a/k/a screen savers)
- dreamy = new DreamManagerService(context);
- ServiceManager.addService(DreamService.DREAM_SERVICE, dreamy);
- } catch (Throwable e) {
- reportWtf("starting DreamManagerService", e);
- }
+ if (!disableNonCoreServices
+ && context.getResources().getBoolean(R.bool.config_dreamsSupported)) {
+ // Dreams (interactive idle-time views, a/k/a screen savers)
+ mSystemServiceManager.startService(DreamManagerService.class);
}
if (!disableNonCoreServices) {
@@ -956,7 +946,7 @@
try {
// TODO: use boot phase
- mPowerManagerService.systemReady(twilight, dreamy);
+ mPowerManagerService.systemReady();
} catch (Throwable e) {
reportWtf("making Power Manager Service ready", e);
}
@@ -993,7 +983,6 @@
final CommonTimeManagementService commonTimeMgmtServiceF = commonTimeMgmtService;
final TextServicesManagerService textServiceManagerServiceF = tsms;
final StatusBarManagerService statusBarF = statusBar;
- final DreamManagerService dreamyF = dreamy;
final AssetAtlasService atlasF = atlas;
final InputManagerService inputManagerF = inputManager;
final TelephonyRegistry telephonyRegistryF = telephonyRegistry;
@@ -1106,11 +1095,6 @@
reportWtf("Notifying TextServicesManagerService running", e);
}
try {
- if (dreamyF != null) dreamyF.systemRunning();
- } catch (Throwable e) {
- reportWtf("Notifying DreamManagerService running", e);
- }
- try {
if (atlasF != null) atlasF.systemRunning();
} catch (Throwable e) {
reportWtf("Notifying AssetAtlasService running", e);
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 4be11b8..59bdf64 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -97,6 +97,7 @@
public static final int CMD_ENABLE_MOBILE_PROVISIONING = BASE + 37;
public static final int CMD_IS_PROVISIONING_APN = BASE + 38;
public static final int EVENT_PROVISIONING_APN_ALARM = BASE + 39;
+ public static final int CMD_NET_STAT_POLL = BASE + 40;
/***** Constants *****/