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 *****/