Merge "Introduce <application> attribute android:fullBackupOnly={boolean}"
diff --git a/api/current.txt b/api/current.txt
index aef48c1..1e3f1f7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -895,7 +895,7 @@
     field public static final int permissionFlags = 16843719; // 0x10103c7
     field public static final int permissionGroup = 16842762; // 0x101000a
     field public static final int permissionGroupFlags = 16843717; // 0x10103c5
-    field public static final int persistable = 16843823; // 0x101042f
+    field public static final int persistableMode = 16843823; // 0x101042f
     field public static final int persistent = 16842765; // 0x101000d
     field public static final int persistentDrawingCache = 16842990; // 0x10100ee
     field public static final deprecated int phoneNumber = 16843111; // 0x1010167
@@ -5195,6 +5195,7 @@
     method public java.lang.CharSequence onDisableRequested(android.content.Context, android.content.Intent);
     method public void onDisabled(android.content.Context, android.content.Intent);
     method public void onEnabled(android.content.Context, android.content.Intent);
+    method public void onLockTaskModeChanged(android.content.Context, android.content.Intent, boolean, java.lang.String);
     method public void onPasswordChanged(android.content.Context, android.content.Intent);
     method public void onPasswordExpiring(android.content.Context, android.content.Intent);
     method public void onPasswordFailed(android.content.Context, android.content.Intent);
@@ -5204,6 +5205,7 @@
     field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLED = "android.app.action.DEVICE_ADMIN_DISABLED";
     field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLE_REQUESTED = "android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED";
     field public static final java.lang.String ACTION_DEVICE_ADMIN_ENABLED = "android.app.action.DEVICE_ADMIN_ENABLED";
+    field public static final java.lang.String ACTION_LOCK_TASK_CHANGED = "android.app.action.ACTION_LOCK_TASK_CHANGED";
     field public static final java.lang.String ACTION_PASSWORD_CHANGED = "android.app.action.ACTION_PASSWORD_CHANGED";
     field public static final java.lang.String ACTION_PASSWORD_EXPIRING = "android.app.action.ACTION_PASSWORD_EXPIRING";
     field public static final java.lang.String ACTION_PASSWORD_FAILED = "android.app.action.ACTION_PASSWORD_FAILED";
@@ -5211,6 +5213,8 @@
     field public static final java.lang.String ACTION_PROFILE_PROVISIONING_COMPLETE = "android.app.action.ACTION_PROFILE_PROVISIONING_COMPLETE";
     field public static final java.lang.String DEVICE_ADMIN_META_DATA = "android.app.device_admin";
     field public static final java.lang.String EXTRA_DISABLE_WARNING = "android.app.extra.DISABLE_WARNING";
+    field public static final java.lang.String EXTRA_LOCK_TASK_ENTERING = "android.app.extra.LOCK_TASK_ENTERING";
+    field public static final java.lang.String EXTRA_LOCK_TASK_PACKAGE = "android.app.extra.LOCK_TASK_PACKAGE";
   }
 
   public class DevicePolicyManager {
@@ -5218,7 +5222,7 @@
     method public void addPersistentPreferredActivity(android.content.ComponentName, android.content.IntentFilter, android.content.ComponentName);
     method public void addUserRestriction(android.content.ComponentName, java.lang.String);
     method public void clearCrossProfileIntentFilters(android.content.ComponentName);
-    method public void clearDeviceOwnerApp();
+    method public void clearDeviceOwnerApp(java.lang.String);
     method public void clearPackagePersistentPreferredActivities(android.content.ComponentName, java.lang.String);
     method public void clearUserRestriction(android.content.ComponentName, java.lang.String);
     method public android.os.UserHandle createAndInitializeUser(android.content.ComponentName, java.lang.String, java.lang.String, android.content.ComponentName, android.os.Bundle);
@@ -5250,7 +5254,7 @@
     method public boolean isAdminActive(android.content.ComponentName);
     method public boolean isApplicationBlocked(android.content.ComponentName, java.lang.String);
     method public boolean isDeviceOwnerApp(java.lang.String);
-    method public boolean isLockTaskPermitted(android.content.ComponentName);
+    method public boolean isLockTaskPermitted(java.lang.String);
     method public boolean isMasterVolumeMuted(android.content.ComponentName);
     method public boolean isProfileOwnerApp(java.lang.String);
     method public void lockNow();
@@ -5264,7 +5268,7 @@
     method public void setCameraDisabled(android.content.ComponentName, boolean);
     method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
     method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
-    method public void setLockTaskComponents(android.content.ComponentName[]) throws java.lang.SecurityException;
+    method public void setLockTaskPackages(java.lang.String[]) throws java.lang.SecurityException;
     method public void setMasterVolumeMuted(android.content.ComponentName, boolean);
     method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int);
     method public void setMaximumTimeToLock(android.content.ComponentName, long);
@@ -8063,6 +8067,7 @@
     field public static final int DOCUMENT_LAUNCH_INTO_EXISTING = 1; // 0x1
     field public static final int DOCUMENT_LAUNCH_NEVER = 3; // 0x3
     field public static final int DOCUMENT_LAUNCH_NONE = 0; // 0x0
+    field public static final int DO_NOT_PERSIST = 1; // 0x1
     field public static final int FLAG_ALLOW_TASK_REPARENTING = 64; // 0x40
     field public static final int FLAG_ALWAYS_RETAIN_TASK_STATE = 8; // 0x8
     field public static final int FLAG_AUTO_REMOVE_FROM_RECENTS = 8192; // 0x2000
@@ -8074,13 +8079,14 @@
     field public static final int FLAG_IMMERSIVE = 2048; // 0x800
     field public static final int FLAG_MULTIPROCESS = 1; // 0x1
     field public static final int FLAG_NO_HISTORY = 128; // 0x80
-    field public static final int FLAG_PERSISTABLE = 4096; // 0x1000
     field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000
     field public static final int FLAG_STATE_NOT_NEEDED = 16; // 0x10
     field public static final int LAUNCH_MULTIPLE = 0; // 0x0
     field public static final int LAUNCH_SINGLE_INSTANCE = 3; // 0x3
     field public static final int LAUNCH_SINGLE_TASK = 2; // 0x2
     field public static final int LAUNCH_SINGLE_TOP = 1; // 0x1
+    field public static final int PERSIST_ACROSS_REBOOTS = 2; // 0x2
+    field public static final int PERSIST_ROOT_ONLY = 0; // 0x0
     field public static final int SCREEN_ORIENTATION_BEHIND = 3; // 0x3
     field public static final int SCREEN_ORIENTATION_FULL_SENSOR = 10; // 0xa
     field public static final int SCREEN_ORIENTATION_FULL_USER = 13; // 0xd
@@ -8105,6 +8111,7 @@
     field public int maxRecents;
     field public java.lang.String parentActivityName;
     field public java.lang.String permission;
+    field public int persistableMode;
     field public int screenOrientation;
     field public int softInputMode;
     field public java.lang.String targetActivity;
@@ -8264,15 +8271,26 @@
   }
 
   public class LauncherApps {
+    method public void addOnAppsChangedCallback(android.content.pm.LauncherApps.OnAppsChangedCallback);
     method public void addOnAppsChangedListener(android.content.pm.LauncherApps.OnAppsChangedListener);
     method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
     method public boolean isActivityEnabledForProfile(android.content.ComponentName, android.os.UserHandle);
     method public boolean isPackageEnabledForProfile(java.lang.String, android.os.UserHandle);
+    method public void removeOnAppsChangedCallback(android.content.pm.LauncherApps.OnAppsChangedCallback);
     method public void removeOnAppsChangedListener(android.content.pm.LauncherApps.OnAppsChangedListener);
     method public android.content.pm.LauncherActivityInfo resolveActivity(android.content.Intent, android.os.UserHandle);
     method public void startActivityForProfile(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
   }
 
+  public static abstract class LauncherApps.OnAppsChangedCallback {
+    ctor public LauncherApps.OnAppsChangedCallback();
+    method public abstract void onPackageAdded(java.lang.String, android.os.UserHandle);
+    method public abstract void onPackageChanged(java.lang.String, android.os.UserHandle);
+    method public abstract void onPackageRemoved(java.lang.String, android.os.UserHandle);
+    method public abstract void onPackagesAvailable(java.lang.String[], android.os.UserHandle, boolean);
+    method public abstract void onPackagesUnavailable(java.lang.String[], android.os.UserHandle, boolean);
+  }
+
   public static abstract interface LauncherApps.OnAppsChangedListener {
     method public abstract void onPackageAdded(android.os.UserHandle, java.lang.String);
     method public abstract void onPackageChanged(android.os.UserHandle, java.lang.String);
@@ -15835,6 +15853,7 @@
   }
 
   public static final class TvContract.Channels implements android.media.tv.TvContract.BaseTvColumns {
+    method public static final java.lang.String getVideoResolution(java.lang.String);
     field public static final java.lang.String COLUMN_BROWSABLE = "browsable";
     field public static final java.lang.String COLUMN_DESCRIPTION = "description";
     field public static final java.lang.String COLUMN_DISPLAY_NAME = "display_name";
@@ -15848,6 +15867,7 @@
     field public static final java.lang.String COLUMN_TRANSPORT_STREAM_ID = "transport_stream_id";
     field public static final java.lang.String COLUMN_TYPE = "type";
     field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+    field public static final java.lang.String COLUMN_VIDEO_FORMAT = "video_format";
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/channel";
     field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/channel";
     field public static final android.net.Uri CONTENT_URI;
@@ -15879,6 +15899,22 @@
     field public static final int TYPE_SECAM = 3; // 0x3
     field public static final int TYPE_S_DMB = 393472; // 0x60100
     field public static final int TYPE_T_DMB = 393216; // 0x60000
+    field public static final java.lang.String VIDEO_FORMAT_1080I = "VIDEO_FORMAT_1080I";
+    field public static final java.lang.String VIDEO_FORMAT_1080P = "VIDEO_FORMAT_1080P";
+    field public static final java.lang.String VIDEO_FORMAT_2160P = "VIDEO_FORMAT_2160P";
+    field public static final java.lang.String VIDEO_FORMAT_240P = "VIDEO_FORMAT_240P";
+    field public static final java.lang.String VIDEO_FORMAT_360P = "VIDEO_FORMAT_360P";
+    field public static final java.lang.String VIDEO_FORMAT_4320P = "VIDEO_FORMAT_4320P";
+    field public static final java.lang.String VIDEO_FORMAT_480I = "VIDEO_FORMAT_480I";
+    field public static final java.lang.String VIDEO_FORMAT_480P = "VIDEO_FORMAT_480P";
+    field public static final java.lang.String VIDEO_FORMAT_576I = "VIDEO_FORMAT_576I";
+    field public static final java.lang.String VIDEO_FORMAT_576P = "VIDEO_FORMAT_576P";
+    field public static final java.lang.String VIDEO_FORMAT_720P = "VIDEO_FORMAT_720P";
+    field public static final java.lang.String VIDEO_RESOLUTION_ED = "VIDEO_RESOLUTION_ED";
+    field public static final java.lang.String VIDEO_RESOLUTION_FHD = "VIDEO_RESOLUTION_FHD";
+    field public static final java.lang.String VIDEO_RESOLUTION_HD = "VIDEO_RESOLUTION_HD";
+    field public static final java.lang.String VIDEO_RESOLUTION_SD = "VIDEO_RESOLUTION_SD";
+    field public static final java.lang.String VIDEO_RESOLUTION_UHD = "VIDEO_RESOLUTION_UHD";
   }
 
   public static final class TvContract.Channels.Logo {
@@ -15899,6 +15935,8 @@
     field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
     field public static final java.lang.String COLUMN_TITLE = "title";
     field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+    field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height";
+    field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width";
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/program";
     field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/program";
     field public static final android.net.Uri CONTENT_URI;
@@ -15907,17 +15945,17 @@
   public static final class TvContract.Programs.Genres {
     method public static java.lang.String[] decode(java.lang.String);
     method public static java.lang.String encode(java.lang.String...);
-    field public static final java.lang.String ANIMAL_WILDLIFE = "Animal/Wildlife";
-    field public static final java.lang.String COMEDY = "Comedy";
-    field public static final java.lang.String DRAMA = "Drama";
-    field public static final java.lang.String EDUCATION = "Education";
-    field public static final java.lang.String FAMILY_KIDS = "Family/Kids";
-    field public static final java.lang.String GAMING = "Gaming";
-    field public static final java.lang.String MOVIES = "Movies";
-    field public static final java.lang.String NEWS = "News";
-    field public static final java.lang.String SHOPPING = "Shopping";
-    field public static final java.lang.String SPORTS = "Sports";
-    field public static final java.lang.String TRAVEL = "Travel";
+    field public static final java.lang.String ANIMAL_WILDLIFE = "ANIMAL_WILDLIFE";
+    field public static final java.lang.String COMEDY = "COMEDY";
+    field public static final java.lang.String DRAMA = "DRAMA";
+    field public static final java.lang.String EDUCATION = "EDUCATION";
+    field public static final java.lang.String FAMILY_KIDS = "FAMILY_KIDS";
+    field public static final java.lang.String GAMING = "GAMING";
+    field public static final java.lang.String MOVIES = "MOVIES";
+    field public static final java.lang.String NEWS = "NEWS";
+    field public static final java.lang.String SHOPPING = "SHOPPING";
+    field public static final java.lang.String SPORTS = "SPORTS";
+    field public static final java.lang.String TRAVEL = "TRAVEL";
   }
 
   public final class TvInputInfo implements android.os.Parcelable {
@@ -21565,7 +21603,7 @@
     method public void setUserRestrictions(android.os.Bundle, android.os.UserHandle);
     field public static final java.lang.String DISALLOW_ADD_USER = "no_add_user";
     field public static final java.lang.String DISALLOW_ADJUST_VOLUME = "no_adjust_volume";
-    field public static final java.lang.String DISALLOW_CONFIG_APPS = "no_config_apps";
+    field public static final java.lang.String DISALLOW_APPS_CONTROL = "no_control_apps";
     field public static final java.lang.String DISALLOW_CONFIG_BLUETOOTH = "no_config_bluetooth";
     field public static final java.lang.String DISALLOW_CONFIG_CELL_BROADCASTS = "no_config_cell_broadcasts";
     field public static final java.lang.String DISALLOW_CONFIG_CREDENTIALS = "no_config_credentials";
@@ -26039,7 +26077,6 @@
 package android.service.fingerprint {
 
   public class FingerprintManager {
-    ctor public FingerprintManager(android.content.Context, android.service.fingerprint.IFingerprintService);
     method public void enroll(long);
     method public void enrollCancel();
     method public boolean enrolledAndEnabled();
@@ -26080,22 +26117,6 @@
     method public static boolean removeFingerprintIdForUser(int, android.content.ContentResolver, int);
   }
 
-  public abstract interface IFingerprintService implements android.os.IInterface {
-    method public abstract void enroll(android.os.IBinder, long, int) throws android.os.RemoteException;
-    method public abstract void enrollCancel(android.os.IBinder, int) throws android.os.RemoteException;
-    method public abstract void remove(android.os.IBinder, int, int) throws android.os.RemoteException;
-    method public abstract void startListening(android.os.IBinder, android.service.fingerprint.IFingerprintServiceReceiver, int) throws android.os.RemoteException;
-    method public abstract void stopListening(android.os.IBinder, int) throws android.os.RemoteException;
-  }
-
-  public abstract interface IFingerprintServiceReceiver implements android.os.IInterface {
-    method public abstract void onAcquired(int) throws android.os.RemoteException;
-    method public abstract void onEnrollResult(int, int) throws android.os.RemoteException;
-    method public abstract void onError(int) throws android.os.RemoteException;
-    method public abstract void onProcessed(int) throws android.os.RemoteException;
-    method public abstract void onRemoved(int) throws android.os.RemoteException;
-  }
-
 }
 
 package android.service.notification {
diff --git a/cmds/idmap/create.cpp b/cmds/idmap/create.cpp
index ae35f7b..593a197 100644
--- a/cmds/idmap/create.cpp
+++ b/cmds/idmap/create.cpp
@@ -105,7 +105,7 @@
 
         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,
+        if (!ResTable::getIdmapInfo(buf, N, NULL, &cached_target_crc, &cached_overlay_crc,
                     &cached_target_path, &cached_overlay_path)) {
             return true;
         }
diff --git a/cmds/idmap/idmap.cpp b/cmds/idmap/idmap.cpp
index 46c0edc..90cfa2c 100644
--- a/cmds/idmap/idmap.cpp
+++ b/cmds/idmap/idmap.cpp
@@ -66,26 +66,32 @@
       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\
+      SECTION      ENTRY        VALUE      COMMENT \n\
+      IDMAP HEADER magic        0x706d6469 \n\
+                   base crc     0xb65a383f \n\
+                   overlay crc  0x7b9675e8 \n\
+                   base path    .......... /path/to/target.apk \n\
+                   overlay path .......... /path/to/overlay.apk \n\
+      DATA HEADER  target pkg   0x0000007f \n\
+                   types count  0x00000003 \n\
+      DATA BLOCK   target type  0x00000002 \n\
+                   overlay type 0x00000002 \n\
+                   entry count  0x00000001 \n\
+                   entry offset 0x00000000 \n\
+                   entry        0x00000000 drawable/drawable \n\
+      DATA BLOCK   target type  0x00000003 \n\
+                   overlay type 0x00000003 \n\
+                   entry count  0x00000001 \n\
+                   entry offset 0x00000000 \n\
+                   entry        0x00000000 xml/integer \n\
+      DATA BLOCK   target type  0x00000004 \n\
+                   overlay type 0x00000004 \n\
+                   entry count  0x00000001 \n\
+                   entry offset 0x00000000 \n\
+                   entry        0x00000000 raw/lorem_ipsum \n\
 \n\
       In this example, the overlay package provides three alternative resource values:\n\
-      xml/integer, string/str and string/str2.\n\
+      drawable/drawable, xml/integer, and raw/lorem_ipsum \n\
 \n\
 NOTES \n\
       This tool and its expected invocation from installd is modelled on dexopt.";
diff --git a/cmds/idmap/inspect.cpp b/cmds/idmap/inspect.cpp
index a59f5d3..b9ac8a5 100644
--- a/cmds/idmap/inspect.cpp
+++ b/cmds/idmap/inspect.cpp
@@ -10,92 +10,108 @@
 
 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 uint32_t IDMAP_MAGIC = 0x504D4449;
     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_;
+            const char* buf_;
             size_t len_;
-            mutable size_t pos_;
+            size_t pos_;
         public:
-            IdmapBuffer() : buf_((char *)MAP_FAILED), len_(0), pos_(0) {}
+            IdmapBuffer() : buf_((const char *)MAP_FAILED), len_(0), pos_(0) {}
 
             ~IdmapBuffer() {
                 if (buf_ != MAP_FAILED) {
-                    munmap(buf_, len_);
+                    munmap(const_cast<char*>(buf_), len_);
                 }
             }
 
-            int init(const char *idmap_path)
-            {
+            status_t 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;
+                    return UNKNOWN_ERROR;
                 }
                 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;
+                    return UNKNOWN_ERROR;
                 }
-                if ((buf_ = (char*)mmap(NULL, len_, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
+                if ((buf_ = (const 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;
+                    return UNKNOWN_ERROR;
                 }
                 close(fd);
-                return 0;
+                return NO_ERROR;
             }
 
-            int next(uint32_t *i, uint32_t *offset) const
-            {
+            status_t nextUint32(uint32_t* i) {
                 if (!buf_) {
                     printe("failed to read next uint32_t: buffer not initialized\n");
-                    return -1;
+                    return UNKNOWN_ERROR;
                 }
-                if (pos_ + 4 > len_) {
+
+                if (pos_ + sizeof(uint32_t) > len_) {
                     printe("failed to read next uint32_t: end of buffer reached at pos=0x%08x\n",
                             pos_);
-                    return -1;
+                    return UNKNOWN_ERROR;
                 }
-                *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;
+
+                if ((reinterpret_cast<uintptr_t>(buf_ + pos_) & 0x3) != 0) {
+                    printe("failed to read next uint32_t: not aligned on 4-byte boundary\n");
+                    return UNKNOWN_ERROR;
+                }
+
+                *i = dtohl(*reinterpret_cast<const uint32_t*>(buf_ + pos_));
+                pos_ += sizeof(uint32_t);
+                return NO_ERROR;
             }
 
-            int nextPath(char *b, uint32_t *offset_start, uint32_t *offset_end) const
-            {
+            status_t nextUint16(uint16_t* i) {
+                if (!buf_) {
+                    printe("failed to read next uint16_t: buffer not initialized\n");
+                    return UNKNOWN_ERROR;
+                }
+
+                if (pos_ + sizeof(uint16_t) > len_) {
+                    printe("failed to read next uint16_t: end of buffer reached at pos=0x%08x\n",
+                            pos_);
+                    return UNKNOWN_ERROR;
+                }
+
+                if ((reinterpret_cast<uintptr_t>(buf_ + pos_) & 0x1) != 0) {
+                    printe("failed to read next uint32_t: not aligned on 2-byte boundary\n");
+                    return UNKNOWN_ERROR;
+                }
+
+                *i = dtohs(*reinterpret_cast<const uint16_t*>(buf_ + pos_));
+                pos_ += sizeof(uint16_t);
+                return NO_ERROR;
+            }
+
+            status_t nextPath(char *b) {
                 if (!buf_) {
                     printe("failed to read next path: buffer not initialized\n");
-                    return -1;
+                    return UNKNOWN_ERROR;
                 }
                 if (pos_ + PATH_LENGTH > len_) {
                     printe("failed to read next path: end of buffer reached at pos=0x%08x\n", pos_);
-                    return -1;
+                    return UNKNOWN_ERROR;
                 }
                 memcpy(b, buf_ + pos_, PATH_LENGTH);
-                *offset_start = pos_ / sizeof(uint32_t);
                 pos_ += PATH_LENGTH;
-                *offset_end = pos_ / sizeof(uint32_t) - 1;
-                return 0;
+                return NO_ERROR;
             }
     };
 
-    void printe(const char *fmt, ...)
-    {
+    void printe(const char *fmt, ...) {
         va_list ap;
 
         va_start(ap, fmt);
@@ -104,44 +120,37 @@
         va_end(ap);
     }
 
-    void print_header()
-    {
-        printf("SECTION      ENTRY        VALUE      OFFSET    COMMENT\n");
+    void print_header() {
+        printf("SECTION      ENTRY        VALUE      COMMENT\n");
     }
 
-    void print(const char *section, const char *subsection, uint32_t value, uint32_t offset,
-            const char *fmt, ...)
-    {
+    void print(const char *section, const char *subsection, uint32_t value, const char *fmt, ...) {
         va_list ap;
 
         va_start(ap, fmt);
-        printf("%-12s %-12s 0x%08x 0x%-4x    ", section, subsection, value, offset);
+        printf("%-12s %-12s 0x%08x ", section, subsection, value);
         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, ...)
-    {
+    void print_path(const char *section, const char *subsection, const char *fmt, ...) {
         va_list ap;
 
         va_start(ap, fmt);
-        printf("%-12s %-12s .......... 0x%02x-0x%02x ", section, subsection, offset_start,
-                offset_end);
+        printf("%-12s %-12s .......... ", section, subsection);
         vprintf(fmt, ap);
         printf("\n");
         va_end(ap);
     }
 
-    int resource_metadata(const AssetManager& am, uint32_t res_id,
-            String8 *package, String8 *type, String8 *name)
-    {
+    status_t 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;
+            return UNKNOWN_ERROR;
         }
         if (package) {
             *package = String8(String16(data.package, data.packageLen));
@@ -152,140 +161,150 @@
         if (name) {
             *name = String8(String16(data.name, data.nameLen));
         }
-        return 0;
+        return NO_ERROR;
     }
 
-    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;
+    status_t parse_idmap_header(IdmapBuffer& buf, AssetManager& am) {
+        uint32_t i;
         char path[PATH_LENGTH];
 
-        NEXT(buf, i, o);
+        status_t err = buf.nextUint32(&i);
+        if (err != NO_ERROR) {
+            return err;
+        }
+
         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;
+            return UNKNOWN_ERROR;
         }
+
         print_header();
-        print("IDMAP HEADER", "magic", i, o, "");
+        print("IDMAP HEADER", "magic", i, "");
 
-        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;
+        err = buf.nextUint32(&i);
+        if (err != NO_ERROR) {
+            return err;
         }
-        print_path("", "base path", o, e, "%s", path);
+        print("", "version", i, "");
+
+        err = buf.nextUint32(&i);
+        if (err != NO_ERROR) {
+            return err;
+        }
+        print("", "base crc", i, "");
+
+        err = buf.nextUint32(&i);
+        if (err != NO_ERROR) {
+            return err;
+        }
+        print("", "overlay crc", i, "");
+
+        err = buf.nextPath(path);
+        if (err != NO_ERROR) {
+            // printe done from IdmapBuffer::nextPath
+            return err;
+        }
+        print_path("", "base path", "%s", path);
+
         if (!am.addAssetPath(String8(path), NULL)) {
             printe("failed to add '%s' as asset path\n", path);
-            return -1;
+            return UNKNOWN_ERROR;
         }
 
-        if (buf.nextPath(path, &o, &e) < 0) {
+        err = buf.nextPath(path);
+        if (err != NO_ERROR) {
             // printe done from IdmapBuffer::nextPath
-            return -1;
+            return err;
         }
-        print_path("", "overlay path", o, e, "%s", path);
+        print_path("", "overlay path", "%s", path);
 
-        return 0;
+        return NO_ERROR;
     }
 
-    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);
+    status_t parse_data(IdmapBuffer& buf, const AssetManager& am) {
+        const uint32_t packageId = am.getResources().getBasePackageId(0);
 
-        NEXT(buf, i, o);
-        print("DATA HEADER", "types count", i, o, "");
-        const uint32_t N = i;
+        uint16_t data16;
+        status_t err = buf.nextUint16(&data16);
+        if (err != NO_ERROR) {
+            return err;
+        }
+        print("DATA HEADER", "target pkg", static_cast<uint32_t>(data16), "");
 
-        for (uint32_t j = 0; j < N; ++j) {
-            NEXT(buf, i, o);
-            if (i == 0) {
-                print("", "padding", i, o, "");
-            } else {
+        err = buf.nextUint16(&data16);
+        if (err != NO_ERROR) {
+            return err;
+        }
+        print("", "types count", static_cast<uint32_t>(data16), "");
+
+        uint32_t typeCount = static_cast<uint32_t>(data16);
+        while (typeCount > 0) {
+            typeCount--;
+
+            err = buf.nextUint16(&data16);
+            if (err != NO_ERROR) {
+                return err;
+            }
+            const uint32_t targetTypeId = static_cast<uint32_t>(data16);
+            print("DATA BLOCK", "target type", targetTypeId, "");
+
+            err = buf.nextUint16(&data16);
+            if (err != NO_ERROR) {
+                return err;
+            }
+            print("", "overlay type", static_cast<uint32_t>(data16), "");
+
+            err = buf.nextUint16(&data16);
+            if (err != NO_ERROR) {
+                return err;
+            }
+            const uint32_t entryCount = static_cast<uint32_t>(data16);
+            print("", "entry count", entryCount, "");
+
+            err = buf.nextUint16(&data16);
+            if (err != NO_ERROR) {
+                return err;
+            }
+            const uint32_t entryOffset = static_cast<uint32_t>(data16);
+            print("", "entry offset", entryOffset, "");
+
+            for (uint32_t i = 0; i < entryCount; i++) {
+                uint32_t data32;
+                err = buf.nextUint32(&data32);
+                if (err != NO_ERROR) {
+                    return err;
+                }
+
+                uint32_t resID = (packageId << 24) | (targetTypeId << 16) | (entryOffset + i);
                 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;
+                String8 name;
+                err = resource_metadata(am, resID, NULL, &type, &name);
+                if (err != NO_ERROR) {
+                    return err;
                 }
-                print("", "type offset", i, o, "absolute offset 0x%02x, %s",
-                        i + IDMAP_HEADER_SIZE, type.string());
-                types.add(numeric_type);
+                print("", "entry", data32, "%s/%s", type.string(), name.string());
             }
         }
 
-        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;
+        return NO_ERROR;
     }
 }
 
-int idmap_inspect(const char *idmap_path)
-{
+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) {
+    if (parse_idmap_header(buf, am) != NO_ERROR) {
         // printe done from parse_idmap_header
         return EXIT_FAILURE;
     }
-    Vector<uint32_t> types;
-    if (parse_data_header(buf, am, types) < 0) {
+    if (parse_data(buf, am) != NO_ERROR) {
         // 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/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index f6883e2..9132883 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -929,7 +929,8 @@
 
     /**
      * Same as {@link #onCreate(android.os.Bundle)} but called for those activities created with
-     * the attribute {@link android.R.attr#persistable} set true.
+     * the attribute {@link android.R.attr#persistableMode} set to
+     * <code>persistAcrossReboots</code>.
      *
      * @param savedInstanceState if the activity is being re-initialized after
      *     previously being shut down then this Bundle contains the data it most
@@ -1012,8 +1013,9 @@
 
     /**
      * This is the same as {@link #onRestoreInstanceState(Bundle)} but is called for activities
-     * created with the attribute {@link android.R.attr#persistable}. The {@link
-     * android.os.PersistableBundle} passed came from the restored PersistableBundle first
+     * created with the attribute {@link android.R.attr#persistableMode} set to
+     * <code>persistAcrossReboots</code>. The {@link android.os.PersistableBundle} passed
+     * came from the restored PersistableBundle first
      * saved in {@link #onSaveInstanceState(Bundle, PersistableBundle)}.
      *
      * <p>This method is called between {@link #onStart} and
@@ -1111,7 +1113,8 @@
 
     /**
      * This is the same as {@link #onPostCreate(Bundle)} but is called for activities
-     * created with the attribute {@link android.R.attr#persistable}.
+     * created with the attribute {@link android.R.attr#persistableMode} set to
+     * <code>persistAcrossReboots</code>.
      *
      * @param savedInstanceState The data most recently supplied in {@link #onSaveInstanceState}
      * @param persistentState The data caming from the PersistableBundle first
@@ -1352,10 +1355,10 @@
 
     /**
      * This is the same as {@link #onSaveInstanceState} but is called for activities
-     * created with the attribute {@link android.R.attr#persistable}. The {@link
-     * android.os.PersistableBundle} passed in will be saved and presented in
-     * {@link #onCreate(Bundle, PersistableBundle)} the first time that this activity
-     * is restarted following the next device reboot.
+     * created with the attribute {@link android.R.attr#persistableMode} set to
+     * <code>persistAcrossReboots</code>. The {@link android.os.PersistableBundle} passed
+     * in will be saved and presented in {@link #onCreate(Bundle, PersistableBundle)}
+     * the first time that this activity is restarted following the next device reboot.
      *
      * @param outState Bundle in which to place your saved state.
      * @param outPersistentState State which will be saved across reboots.
@@ -5320,13 +5323,15 @@
      * drawn and it is safe to make this Activity translucent again.
      * @param options activity options delivered to the activity below this one. The options
      * are retrieved using {@link #getActivityOptions}.
+     * @return <code>true</code> if Window was opaque and will become translucent or
+     * <code>false</code> if window was translucent and no change needed to be made.
      *
      * @see #convertFromTranslucent()
      * @see TranslucentConversionListener
      *
      * @hide
      */
-    public void convertToTranslucent(TranslucentConversionListener callback, 
+    public boolean convertToTranslucent(TranslucentConversionListener callback,
             ActivityOptions options) {
         boolean drawComplete;
         try {
@@ -5343,6 +5348,7 @@
             // Window is already translucent.
             mTranslucentCallback.onTranslucentConversionComplete(drawComplete);
         }
+        return mChangeCanvasToTranslucent;
     }
 
     /** @hide */
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index b8f2089..f5514f8 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -319,7 +319,7 @@
         }
 
         public boolean isPersistable() {
-            return (activityInfo.flags & ActivityInfo.FLAG_PERSISTABLE) != 0;
+            return activityInfo.persistableMode == ActivityInfo.PERSIST_ACROSS_REBOOTS;
         }
 
         public String toString() {
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 3e7d9b4..425a140 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -513,6 +513,9 @@
                 public Object createService(ContextImpl ctx) {
                     IBinder b = ServiceManager.getService(POWER_SERVICE);
                     IPowerManager service = IPowerManager.Stub.asInterface(b);
+                    if (service == null) {
+                        Log.wtf(TAG, "Failed to get power manager service.");
+                    }
                     return new PowerManager(ctx.getOuterContext(),
                             service, ctx.mMainThread.getHandler());
                 }});
@@ -694,8 +697,8 @@
 
         registerService(FINGERPRINT_SERVICE, new ServiceFetcher() {
             public Object createService(ContextImpl ctx) {
-                IBinder b = ServiceManager.getService(FINGERPRINT_SERVICE);
-                IFingerprintService service = IFingerprintService.Stub.asInterface(b);
+                IBinder binder = ServiceManager.getService(FINGERPRINT_SERVICE);
+                IFingerprintService service = IFingerprintService.Stub.asInterface(binder);
                 return new FingerprintManager(ctx.getOuterContext(), service);
             }
         });
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index 365cc8e..1d7a0ec 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -55,6 +55,7 @@
     private boolean mIsExitTransitionComplete;
     private boolean mIsReadyForTransition;
     private Bundle mSharedElementsBundle;
+    private boolean mWasOpaque;
 
     public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
             ArrayList<String> sharedElementNames, boolean isReturning) {
@@ -191,7 +192,7 @@
     protected void prepareEnter() {
         mActivity.overridePendingTransition(0, 0);
         if (!mIsReturning) {
-            mActivity.convertToTranslucent(null, null);
+            mWasOpaque = mActivity.convertToTranslucent(null, null);
             Drawable background = getDecor().getBackground();
             if (background != null) {
                 getWindow().setBackgroundDrawable(null);
@@ -376,7 +377,9 @@
 
     private void makeOpaque() {
         if (!mHasStopped && mActivity != null) {
-            mActivity.convertFromTranslucent();
+            if (mWasOpaque) {
+                mActivity.convertFromTranslucent();
+            }
             mActivity = null;
         }
     }
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index 45a2625..ca40436 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -166,6 +166,40 @@
             = "android.app.action.ACTION_PASSWORD_EXPIRING";
 
     /**
+     * Action sent to a device administrator to notify that the device is entering
+     * or exiting lock task mode from an authorized package.  The extra
+     * {@link #EXTRA_LOCK_TASK_ENTERING} will describe whether entering or exiting
+     * the mode.  If entering, the extra {@link #EXTRA_LOCK_TASK_PACKAGE} will describe
+     * the authorized package using lock task mode.
+     *
+     * @see DevicePolicyManager#isLockTaskPermitted
+     *
+     * <p>The calling device admin must be the device owner or profile
+     * owner to receive this broadcast.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_LOCK_TASK_CHANGED
+            = "android.app.action.ACTION_LOCK_TASK_CHANGED";
+
+    /**
+     * A boolean describing whether the device is currently entering or exiting
+     * lock task mode.
+     *
+     * @see #ACTION_LOCK_TASK_CHANGED
+     */
+    public static final String EXTRA_LOCK_TASK_ENTERING =
+            "android.app.extra.LOCK_TASK_ENTERING";
+
+    /**
+     * A boolean describing whether the device is currently entering or exiting
+     * lock task mode.
+     *
+     * @see #ACTION_LOCK_TASK_CHANGED
+     */
+    public static final String EXTRA_LOCK_TASK_PACKAGE =
+            "android.app.extra.LOCK_TASK_PACKAGE";
+
+    /**
      * Broadcast Action: This broadcast is sent to indicate that provisioning of a managed profile
      * or managed device has completed successfully.
      *
@@ -341,6 +375,19 @@
     }
 
     /**
+     * Called when a device is entering or exiting lock task mode by a package
+     * authorized by {@link DevicePolicyManager#isLockTaskPermitted(String)}
+     *
+     * @param context The running context as per {@link #onReceive}.
+     * @param intent The received intent as per {@link #onReceive}.
+     * @param isEnteringLockTask Whether the device is entering or exiting lock task mode.
+     * @param pkg If entering, the authorized package using lock task mode, otherwise null.
+     */
+    public void onLockTaskModeChanged(Context context, Intent intent, boolean isEnteringLockTask,
+            String pkg) {
+    }
+
+    /**
      * Intercept standard device administrator broadcasts.  Implementations
      * should not override this method; it is better to implement the
      * convenience callbacks for each action.
@@ -369,6 +416,10 @@
             onPasswordExpiring(context, intent);
         } else if (ACTION_PROFILE_PROVISIONING_COMPLETE.equals(action)) {
             onProfileProvisioningComplete(context, intent);
+        } else if (ACTION_LOCK_TASK_CHANGED.equals(action)) {
+            boolean isEntering = intent.getBooleanExtra(EXTRA_LOCK_TASK_ENTERING, false);
+            String pkg = intent.getStringExtra(EXTRA_LOCK_TASK_PACKAGE);
+            onLockTaskModeChanged(context, intent, isEntering, pkg);
         }
     }
 }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index e80c761..af653a3 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -18,6 +18,7 @@
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.app.Activity;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -1839,11 +1840,13 @@
      * This function should be used cautiously as once it is called it cannot
      * be undone.  The device owner can only be set as a part of device setup
      * before setup completes.
+     *
+     * @param packageName The package name of the device owner.
      */
-    public void clearDeviceOwnerApp() {
+    public void clearDeviceOwnerApp(String packageName) {
         if (mService != null) {
             try {
-                mService.clearDeviceOwner(mContext.getPackageName());
+                mService.clearDeviceOwner(packageName);
             } catch (RemoteException re) {
                 Log.w(TAG, "Failed to clear device owner");
             }
@@ -2340,15 +2343,20 @@
     }
 
     /**
-     * Sets which components may enter lock task mode.
+     * Sets which packages may enter lock task mode.
+     *
+     * <p>Any packages that shares uid with an allowed package will also be allowed
+     * to activate lock task.
      *
      * This function can only be called by the device owner or the profile owner.
-     * @param components The list of components allowed to enter lock task mode
+     * @param packages The list of packages allowed to enter lock task mode
+     *
+     * @see Activity#startLockTask()
      */
-    public void setLockTaskComponents(ComponentName[] components) throws SecurityException {
+    public void setLockTaskPackages(String[] packages) throws SecurityException {
         if (mService != null) {
             try {
-                mService.setLockTaskComponents(components);
+                mService.setLockTaskPackages(packages);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -2356,13 +2364,13 @@
     }
 
     /**
-     * This function returns the list of components allowed to start the lock task mode.
+     * This function returns the list of packages allowed to start the lock task mode.
      * @hide
      */
-    public ComponentName[] getLockTaskComponents() {
+    public String[] getLockTaskPackages() {
         if (mService != null) {
             try {
-                return mService.getLockTaskComponents();
+                return mService.getLockTaskPackages();
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -2373,12 +2381,12 @@
     /**
      * This function lets the caller know whether the given component is allowed to start the
      * lock task mode.
-     * @param component The component to check
+     * @param pkg The package to check
      */
-    public boolean isLockTaskPermitted(ComponentName component) {
+    public boolean isLockTaskPermitted(String pkg) {
         if (mService != null) {
             try {
-                return mService.isLockTaskPermitted(component);
+                return mService.isLockTaskPermitted(pkg);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index a1caa21..e7b77d8 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -142,13 +142,15 @@
     void setAccountManagementDisabled(in ComponentName who, in String accountType, in boolean disabled);
     String[] getAccountTypesWithManagementDisabled();
 
-    void setLockTaskComponents(in ComponentName[] components);
-    ComponentName[] getLockTaskComponents();
-    boolean isLockTaskPermitted(in ComponentName component);
+    void setLockTaskPackages(in String[] packages);
+    String[] getLockTaskPackages();
+    boolean isLockTaskPermitted(in String pkg);
 
     void setGlobalSetting(in ComponentName who, in String setting, in String value);
     void setSecureSetting(in ComponentName who, in String setting, in String value);
 
     void setMasterVolumeMuted(in ComponentName admin, boolean on);
     boolean isMasterVolumeMuted(in ComponentName admin);
+
+    void notifyLockTaskModeChanged(boolean isEnabled, String pkg, int userId);
 }
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 6e53a6fb..3dfa78b 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -7375,6 +7375,7 @@
             for (int categoryNdx = mCategories.size() - 1; categoryNdx >= 0; --categoryNdx) {
                 out.attribute(null, ATTR_CATEGORY, mCategories.valueAt(categoryNdx));
             }
+            out.endTag(null, TAG_CATEGORIES);
         }
     }
 
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 791e5aa..abc8cde 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -104,6 +104,28 @@
     public int documentLaunchMode;
 
     /**
+     * Constant corresponding to <code>persistRootOnly</code> in
+     * the {@link android.R.attr#persistableMode} attribute.
+     */
+    public static final int PERSIST_ROOT_ONLY = 0;
+    /**
+     * Constant corresponding to <code>doNotPersist</code> in
+     * the {@link android.R.attr#persistableMode} attribute.
+     */
+    public static final int DO_NOT_PERSIST = 1;
+    /**
+     * Constant corresponding to <code>persistAcrossReboots</code> in
+     * the {@link android.R.attr#persistableMode} attribute.
+     */
+    public static final int PERSIST_ACROSS_REBOOTS = 2;
+    /**
+     * Value indicating how this activity is to be persisted across
+     * reboots for restoring in the Recents list.
+     * {@link android.R.attr#persistableMode}
+     */
+    public int persistableMode;
+
+    /**
      * The maximum number of tasks rooted at this activity that can be in the recent task list.
      * Refer to {@link android.R.attr#maxRecents}.
      */
@@ -230,12 +252,6 @@
      */
     public static final int FLAG_IMMERSIVE = 0x0800;
     /**
-     * Bit in {@link #flags} indicating that this activity is to be persisted across
-     * reboots for display in the Recents list.
-     * {@link android.R.attr#persistable}
-     */
-    public static final int FLAG_PERSISTABLE = 0x1000;
-    /**
      * Bit in {@link #flags} indicating that tasks started with this activity are to be
      * removed from the recent list of tasks when the last activity in the task is finished.
      * {@link android.R.attr#autoRemoveFromRecents}
@@ -641,13 +657,23 @@
         return theme != 0 ? theme : applicationInfo.theme;
     }
 
+    private String persistableModeToString() {
+        switch(persistableMode) {
+            case PERSIST_ROOT_ONLY: return "PERSIST_ROOT_ONLY";
+            case DO_NOT_PERSIST: return "DO_NOT_PERSIST";
+            case PERSIST_ACROSS_REBOOTS: return "PERSIST_ACROSS_REBOOTS";
+            default: return "UNKNOWN=" + persistableMode;
+        }
+    }
+
     public void dump(Printer pw, String prefix) {
         super.dumpFront(pw, prefix);
         if (permission != null) {
             pw.println(prefix + "permission=" + permission);
         }
         pw.println(prefix + "taskAffinity=" + taskAffinity
-                + " targetActivity=" + targetActivity);
+                + " targetActivity=" + targetActivity
+                + " persistableMode=" + persistableModeToString());
         if (launchMode != 0 || flags != 0 || theme != 0) {
             pw.println(prefix + "launchMode=" + launchMode
                     + " flags=0x" + Integer.toHexString(flags)
@@ -688,6 +714,7 @@
         dest.writeInt(softInputMode);
         dest.writeInt(uiOptions);
         dest.writeString(parentActivityName);
+        dest.writeInt(persistableMode);
     }
 
     public static final Parcelable.Creator<ActivityInfo> CREATOR
@@ -713,5 +740,6 @@
         softInputMode = source.readInt();
         uiOptions = source.readInt();
         parentActivityName = source.readString();
+        persistableMode = source.readInt();
     }
 }
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 69fa408..6c10bb8 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -57,6 +57,65 @@
 
     private List<OnAppsChangedListener> mListeners
             = new ArrayList<OnAppsChangedListener>();
+    private List<OnAppsChangedCallback> mCallbacks
+            = new ArrayList<OnAppsChangedCallback>();
+
+    /**
+     * Callbacks for package changes to this and related managed profiles.
+     */
+    public static abstract class OnAppsChangedCallback {
+        /**
+         * Indicates that a package was removed from the specified profile.
+         *
+         * @param packageName The name of the package that was removed.
+         * @param user The UserHandle of the profile that generated the change.
+         */
+        abstract public void onPackageRemoved(String packageName, UserHandle user);
+
+        /**
+         * Indicates that a package was added to the specified profile.
+         *
+         * @param packageName The name of the package that was added.
+         * @param user The UserHandle of the profile that generated the change.
+         */
+        abstract public void onPackageAdded(String packageName, UserHandle user);
+
+        /**
+         * Indicates that a package was modified in the specified profile.
+         *
+         * @param packageName The name of the package that has changed.
+         * @param user The UserHandle of the profile that generated the change.
+         */
+        abstract public void onPackageChanged(String packageName, UserHandle user);
+
+        /**
+         * Indicates that one or more packages have become available. For
+         * example, this can happen when a removable storage card has
+         * reappeared.
+         *
+         * @param packageNames The names of the packages that have become
+         *            available.
+         * @param user The UserHandle of the profile that generated the change.
+         * @param replacing Indicates whether these packages are replacing
+         *            existing ones.
+         */
+        abstract public void onPackagesAvailable(String[] packageNames, UserHandle user,
+                boolean replacing);
+
+        /**
+         * Indicates that one or more packages have become unavailable. For
+         * example, this can happen when a removable storage card has been
+         * removed.
+         *
+         * @param packageNames The names of the packages that have become
+         *            unavailable.
+         * @param user The UserHandle of the profile that generated the change.
+         * @param replacing Indicates whether the packages are about to be
+         *            replaced with new versions.
+         */
+        abstract public void onPackagesUnavailable(String[] packageNames, UserHandle user,
+                boolean replacing);
+    }
 
     /**
      * Callbacks for package changes to this and related managed profiles.
@@ -270,7 +329,7 @@
         synchronized (this) {
             if (listener != null && !mListeners.contains(listener)) {
                 mListeners.add(listener);
-                if (mListeners.size() == 1) {
+                if (mListeners.size() == 1 && mCallbacks.size() == 0) {
                     try {
                         mService.addOnAppsChangedListener(mAppsChangedListener);
                     } catch (RemoteException re) {
@@ -289,7 +348,44 @@
     public void removeOnAppsChangedListener(OnAppsChangedListener listener) {
         synchronized (this) {
             mListeners.remove(listener);
-            if (mListeners.size() == 0) {
+            if (mListeners.size() == 0 && mCallbacks.size() == 0) {
+                try {
+                    mService.removeOnAppsChangedListener(mAppsChangedListener);
+                } catch (RemoteException re) {
+                }
+            }
+        }
+    }
+
+    /**
+     * Adds a callback for changes to packages in current and managed profiles.
+     *
+     * @param callback The callback to add.
+     */
+    public void addOnAppsChangedCallback(OnAppsChangedCallback callback) {
+        synchronized (this) {
+            if (callback != null && !mCallbacks.contains(callback)) {
+                mCallbacks.add(callback);
+                if (mCallbacks.size() == 1 && mListeners.size() == 0) {
+                    try {
+                        mService.addOnAppsChangedListener(mAppsChangedListener);
+                    } catch (RemoteException re) {
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Removes a callback that was previously added.
+     *
+     * @param callback The callback to remove.
+     * @see #addOnAppsChangedListener(OnAppsChangedCallback)
+     */
+    public void removeOnAppsChangedCallback(OnAppsChangedCallback callback) {
+        synchronized (this) {
+            mListeners.remove(callback);
+            if (mListeners.size() == 0 && mCallbacks.size() == 0) {
                 try {
                     mService.removeOnAppsChangedListener(mAppsChangedListener);
                 } catch (RemoteException re) {
@@ -309,6 +405,9 @@
                 for (OnAppsChangedListener listener : mListeners) {
                     listener.onPackageRemoved(user, packageName);
                 }
+                for (OnAppsChangedCallback callback : mCallbacks) {
+                    callback.onPackageRemoved(packageName, user);
+                }
             }
         }
 
@@ -321,6 +420,9 @@
                 for (OnAppsChangedListener listener : mListeners) {
                     listener.onPackageChanged(user, packageName);
                 }
+                for (OnAppsChangedCallback callback : mCallbacks) {
+                    callback.onPackageChanged(packageName, user);
+                }
             }
         }
 
@@ -333,6 +435,9 @@
                 for (OnAppsChangedListener listener : mListeners) {
                     listener.onPackageAdded(user, packageName);
                 }
+                for (OnAppsChangedCallback callback : mCallbacks) {
+                    callback.onPackageAdded(packageName, user);
+                }
             }
         }
 
@@ -346,6 +451,9 @@
                 for (OnAppsChangedListener listener : mListeners) {
                     listener.onPackagesAvailable(user, packageNames, replacing);
                 }
+                for (OnAppsChangedCallback callback : mCallbacks) {
+                    callback.onPackagesAvailable(packageNames, user, replacing);
+                }
             }
         }
 
@@ -359,7 +467,10 @@
                 for (OnAppsChangedListener listener : mListeners) {
                     listener.onPackagesUnavailable(user, packageNames, replacing);
                 }
-            }
+                for (OnAppsChangedCallback callback : mCallbacks) {
+                    callback.onPackagesUnavailable(packageNames, user, replacing);
+                }
+           }
         }
     };
 }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 8d9b8d9..5d55b0a1 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2870,15 +2870,12 @@
      *
      */
     public PackageInfo getPackageArchiveInfo(String archiveFilePath, int flags) {
-        PackageParser packageParser = new PackageParser(archiveFilePath);
-        DisplayMetrics metrics = new DisplayMetrics();
-        metrics.setToDefaults();
-        final File sourceFile = new File(archiveFilePath);
+        final PackageParser parser = new PackageParser();
+        final File apkFile = new File(archiveFilePath);
         try {
-            PackageParser.Package pkg = packageParser.parseMonolithicPackage(sourceFile, metrics,
-                    0);
+            PackageParser.Package pkg = parser.parseMonolithicPackage(apkFile, 0);
             if ((flags & GET_SIGNATURES) != 0) {
-                packageParser.collectCertificates(pkg, 0);
+                parser.collectCertificates(pkg, 0);
             }
             PackageUserState state = new PackageUserState();
             return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, state);
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index b25725e..546f3a5 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -16,9 +16,14 @@
 
 package android.content.pm;
 
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
 
 import android.content.ComponentName;
 import android.content.Intent;
@@ -33,6 +38,8 @@
 import android.os.PatternMatcher;
 import android.os.UserHandle;
 import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.AttributeSet;
 import android.util.Base64;
 import android.util.DisplayMetrics;
@@ -41,12 +48,18 @@
 import android.util.Slog;
 import android.util.TypedValue;
 
-import java.io.BufferedInputStream;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.XmlUtils;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
 import java.security.GeneralSecurityException;
 import java.security.KeyFactory;
 import java.security.NoSuchAlgorithmException;
@@ -58,21 +71,19 @@
 import java.security.spec.X509EncodedKeySpec;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
 import java.util.jar.StrictJarFile;
 import java.util.zip.ZipEntry;
 
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.XmlUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
 /**
  * Package archive parsing
  *
@@ -153,17 +164,21 @@
                     android.os.Build.VERSION_CODES.JELLY_BEAN)
     };
 
+    /**
+     * @deprecated callers should move to explicitly passing around source path.
+     */
+    @Deprecated
     private String mArchiveSourcePath;
+
     private String[] mSeparateProcesses;
     private boolean mOnlyCoreApps;
+    private DisplayMetrics mMetrics;
+
     private static final int SDK_VERSION = Build.VERSION.SDK_INT;
     private static final String[] SDK_CODENAMES = Build.VERSION.ACTIVE_CODENAMES;
 
     private int mParseError = PackageManager.INSTALL_SUCCEEDED;
 
-    private static final Object mSync = new Object();
-    private static WeakReference<byte[]> mReadBuffer;
-
     private static boolean sCompatibilityModeEnabled = true;
     private static final int PARSE_DEFAULT_INSTALL_LOCATION =
             PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
@@ -247,12 +262,9 @@
 
     private static final String TAG = "PackageParser";
 
-    public PackageParser(String archiveSourcePath) {
-        mArchiveSourcePath = archiveSourcePath;
-    }
-
-    public PackageParser(File archiveSource) {
-        this(archiveSource.getAbsolutePath());
+    public PackageParser() {
+        mMetrics = new DisplayMetrics();
+        mMetrics.setToDefaults();
     }
 
     public void setSeparateProcesses(String[] procs) {
@@ -263,6 +275,10 @@
         mOnlyCoreApps = onlyCoreApps;
     }
 
+    public void setDisplayMetrics(DisplayMetrics metrics) {
+        mMetrics = metrics;
+    }
+
     private static final boolean isPackageFilename(File file) {
         return isPackageFilename(file.getName());
     }
@@ -480,23 +496,21 @@
         return pi;
     }
 
-    private static Certificate[][] loadCertificates(StrictJarFile jarFile, ZipEntry je,
-            byte[] readBuffer) {
+    private static Certificate[][] loadCertificates(StrictJarFile jarFile, ZipEntry entry)
+            throws PackageParserException {
+        InputStream is = null;
         try {
             // We must read the stream for the JarEntry to retrieve
             // its certificates.
-            InputStream is = new BufferedInputStream(jarFile.getInputStream(je));
-            while (is.read(readBuffer, 0, readBuffer.length) != -1) {
-                // not using
-            }
-            is.close();
-            return je != null ? jarFile.getCertificateChains(je) : null;
-        } catch (IOException e) {
-            Slog.w(TAG, "Exception reading " + je.getName() + " in " + jarFile, e);
-        } catch (RuntimeException e) {
-            Slog.w(TAG, "Exception reading " + je.getName() + " in " + jarFile, e);
+            is = jarFile.getInputStream(entry);
+            readFullyIgnoringContents(is);
+            return jarFile.getCertificateChains(entry);
+        } catch (IOException | RuntimeException e) {
+            throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+                    "Failed reading " + entry.getName() + " in " + jarFile, e);
+        } finally {
+            IoUtils.closeQuietly(is);
         }
-        return null;
     }
 
     public final static int PARSE_IS_SYSTEM = 1<<0;
@@ -508,67 +522,116 @@
     public final static int PARSE_IS_SYSTEM_DIR = 1<<6;
     public final static int PARSE_IS_PRIVILEGED = 1<<7;
     public final static int PARSE_GET_SIGNATURES = 1<<8;
+    public final static int PARSE_TRUSTED_OVERLAY = 1<<9;
+
+    private static final Comparator<String> sSplitNameComparator = new SplitNameComparator();
 
     /**
-     * Parse all APK files under the given directory as a single package.
+     * Used to sort a set of APKs based on their split names, always placing the
+     * base APK (with {@code null} split name) first.
      */
-    public Package parseSplitPackage(File apkDir, DisplayMetrics metrics, int flags,
-            boolean trustedOverlay) throws PackageParserException {
+    private static class SplitNameComparator implements Comparator<String> {
+        @Override
+        public int compare(String lhs, String rhs) {
+            if (lhs == null) {
+                return -1;
+            } else if (rhs == null) {
+                return 1;
+            } else {
+                return lhs.compareTo(rhs);
+            }
+        }
+    }
+
+    /**
+     * Parse all APKs contained in the given directory, treating them as a
+     * single package. This also performs sanity checking, such as requiring
+     * identical package name and version codes, a single base APK, and unique
+     * split names.
+     * <p>
+     * Note that this <em>does not</em> perform signature verification; that
+     * must be done separately in {@link #collectCertificates(Package, int)}.
+     */
+    public Package parseSplitPackage(File apkDir, int flags) throws PackageParserException {
         final File[] files = apkDir.listFiles();
         if (ArrayUtils.isEmpty(files)) {
             throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
                     "No packages found in split");
         }
 
-        File baseFile = null;
+        String packageName = null;
+        int versionCode = 0;
+
+        final ArrayMap<String, File> apks = new ArrayMap<>();
         for (File file : files) {
             if (file.isFile() && isPackageFilename(file)) {
-                ApkLite lite = parseApkLite(file.getAbsolutePath(), 0);
-                if (lite == null) {
-                    throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
-                            "Invalid package file: " + file);
+                final ApkLite lite = parseApkLite(file, 0);
+
+                // Assert that all package names and version codes are
+                // consistent with the first one we encounter.
+                if (packageName == null) {
+                    packageName = lite.packageName;
+                    versionCode = lite.versionCode;
+                } else {
+                    if (!packageName.equals(lite.packageName)) {
+                        throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                                "Inconsistent package " + lite.packageName + " in " + file
+                                + "; expected " + packageName);
+                    }
+                    if (versionCode != lite.versionCode) {
+                        throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                                "Inconsistent version " + lite.versionCode + " in " + file
+                                + "; expected " + versionCode);
+                    }
                 }
 
-                if (TextUtils.isEmpty(lite.splitName)) {
-                    baseFile = file;
-                    break;
+                // Assert that each split is defined only once
+                if (apks.put(lite.splitName, file) != null) {
+                    throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                            "Split name " + lite.splitName
+                            + " defined more than once; most recent was " + file);
                 }
             }
         }
 
+        final File baseFile = apks.remove(null);
         if (baseFile == null) {
-            throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+            throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
                     "Missing base APK in " + apkDir);
         }
 
-        final Package pkg = parseBaseApk(baseFile, metrics, flags, trustedOverlay);
+        // Always apply deterministic ordering based on splitName
+        final int size = apks.size();
+
+        final String[] splitNames = apks.keySet().toArray(new String[size]);
+        Arrays.sort(splitNames, sSplitNameComparator);
+
+        final File[] splitFiles = new File[size];
+        for (int i = 0; i < size; i++) {
+            splitFiles[i] = apks.get(splitNames[i]);
+        }
+
+        final Package pkg = parseBaseApk(baseFile, flags);
         if (pkg == null) {
             throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
                     "Failed to parse base APK: " + baseFile);
         }
 
-        for (File file : files) {
-            if (file.isFile() && isPackageFilename(file) && !file.equals(baseFile)) {
-                parseSplitApk(pkg, file, metrics, flags, trustedOverlay);
-            }
-        }
-
-        // Always use a well-defined sort order
-        if (pkg.splitCodePaths != null) {
-            Arrays.sort(pkg.splitCodePaths);
+        for (File splitFile : splitFiles) {
+            parseSplitApk(pkg, splitFile, flags);
         }
 
         return pkg;
     }
 
-    public Package parseMonolithicPackage(File apkFile, DisplayMetrics metrics, int flags)
-            throws PackageParserException {
-        return parseMonolithicPackage(apkFile, metrics, flags, false);
-    }
-
-    public Package parseMonolithicPackage(File apkFile, DisplayMetrics metrics, int flags,
-            boolean trustedOverlay) throws PackageParserException {
-        final Package pkg = parseBaseApk(apkFile, metrics, flags, trustedOverlay);
+    /**
+     * Parse the given APK file, treating it as as a single monolithic package.
+     * <p>
+     * Note that this <em>does not</em> perform signature verification; that
+     * must be done separately in {@link #collectCertificates(Package, int)}.
+     */
+    public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
+        final Package pkg = parseBaseApk(apkFile, flags);
         if (pkg != null) {
             return pkg;
         } else {
@@ -576,13 +639,15 @@
         }
     }
 
-    private Package parseBaseApk(File apkFile, DisplayMetrics metrics, int flags,
-            boolean trustedOverlay) {
+    private Package parseBaseApk(File apkFile, int flags) {
+        final boolean trustedOverlay = (flags & PARSE_TRUSTED_OVERLAY) != 0;
+
         mParseError = PackageManager.INSTALL_SUCCEEDED;
 
+        final String apkPath = apkFile.getAbsolutePath();
         mArchiveSourcePath = apkFile.getAbsolutePath();
         if (!apkFile.isFile()) {
-            Slog.w(TAG, "Skipping dir: " + mArchiveSourcePath);
+            Slog.w(TAG, "Skipping dir: " + apkPath);
             mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
             return null;
         }
@@ -591,14 +656,14 @@
             if ((flags&PARSE_IS_SYSTEM) == 0) {
                 // We expect to have non-.apk files in the system dir,
                 // so don't warn about them.
-                Slog.w(TAG, "Skipping non-package file: " + mArchiveSourcePath);
+                Slog.w(TAG, "Skipping non-package file: " + apkPath);
             }
             mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
             return null;
         }
 
         if (DEBUG_JAR)
-            Slog.d(TAG, "Scanning package: " + mArchiveSourcePath);
+            Slog.d(TAG, "Scanning package: " + apkPath);
 
         XmlResourceParser parser = null;
         AssetManager assmgr = null;
@@ -606,19 +671,18 @@
         boolean assetError = true;
         try {
             assmgr = new AssetManager();
-            int cookie = assmgr.addAssetPath(mArchiveSourcePath);
+            int cookie = assmgr.addAssetPath(apkPath);
             if (cookie != 0) {
-                res = new Resources(assmgr, metrics, null);
+                res = new Resources(assmgr, mMetrics, null);
                 assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                         Build.VERSION.RESOURCES_SDK_INT);
                 parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
                 assetError = false;
             } else {
-                Slog.w(TAG, "Failed adding asset path:"+mArchiveSourcePath);
+                Slog.w(TAG, "Failed adding asset path:" + apkPath);
             }
         } catch (Exception e) {
-            Slog.w(TAG, "Unable to read AndroidManifest.xml of "
-                    + mArchiveSourcePath, e);
+            Slog.w(TAG, "Unable to read AndroidManifest.xml of " + apkPath, e);
         }
         if (assetError) {
             if (assmgr != null) assmgr.close();
@@ -641,9 +705,9 @@
             // just means to skip this app so don't make a fuss about it.
             if (!mOnlyCoreApps || mParseError != PackageManager.INSTALL_SUCCEEDED) {
                 if (errorException != null) {
-                    Slog.w(TAG, mArchiveSourcePath, errorException);
+                    Slog.w(TAG, apkPath, errorException);
                 } else {
-                    Slog.w(TAG, mArchiveSourcePath + " (at "
+                    Slog.w(TAG, apkPath + " (at "
                             + parser.getPositionDescription()
                             + "): " + errorText[0]);
                 }
@@ -659,14 +723,16 @@
         parser.close();
         assmgr.close();
 
-        pkg.codePath = mArchiveSourcePath;
+        pkg.codePath = apkPath;
         pkg.mSignatures = null;
 
         return pkg;
     }
 
-    private void parseSplitApk(Package pkg, File apkFile, DisplayMetrics metrics, int flags,
-            boolean trustedOverlay) throws PackageParserException {
+    private void parseSplitApk(Package pkg, File apkFile, int flags) throws PackageParserException {
+        final String apkPath = apkFile.getAbsolutePath();
+        mArchiveSourcePath = apkFile.getAbsolutePath();
+
         // TODO: expand split APK parsing
         pkg.splitCodePaths = ArrayUtils.appendElement(String.class, pkg.splitCodePaths,
                 apkFile.getAbsolutePath());
@@ -678,8 +744,9 @@
      * {@code AndroidManifest.xml}, {@code true} is returned.
      */
     public void collectManifestDigest(Package pkg) throws PackageParserException {
+        // TODO: extend to gather digest for split APKs
         try {
-            final StrictJarFile jarFile = new StrictJarFile(mArchiveSourcePath);
+            final StrictJarFile jarFile = new StrictJarFile(pkg.codePath);
             try {
                 final ZipEntry je = jarFile.findEntry(ANDROID_MANIFEST_FILENAME);
                 if (je != null) {
@@ -688,186 +755,127 @@
             } finally {
                 jarFile.close();
             }
-        } catch (IOException e) {
+        } catch (IOException | RuntimeException e) {
             throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                     "Failed to collect manifest digest");
         }
     }
 
+    /**
+     * Collect certificates from all the APKs described in the given package,
+     * populating {@link Package#mSignatures}. This also asserts that all APK
+     * contents are signed correctly and consistently.
+     */
     public void collectCertificates(Package pkg, int flags) throws PackageParserException {
-        if (!collectCertificatesInternal(pkg, flags)) {
-            throw new PackageParserException(mParseError, "Failed to collect certificates");
+        pkg.mCertificates = null;
+        pkg.mSignatures = null;
+        pkg.mSigningKeys = null;
+
+        collectCertificates(pkg, new File(pkg.codePath), flags);
+
+        if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
+            for (String splitCodePath : pkg.splitCodePaths) {
+                collectCertificates(pkg, new File(splitCodePath), flags);
+            }
         }
     }
 
-    private boolean collectCertificatesInternal(Package pkg, int flags) {
-        pkg.mSignatures = null;
+    private static void collectCertificates(Package pkg, File apkFile, int flags)
+            throws PackageParserException {
+        final String apkPath = apkFile.getAbsolutePath();
 
-        WeakReference<byte[]> readBufferRef;
-        byte[] readBuffer = null;
-        synchronized (mSync) {
-            readBufferRef = mReadBuffer;
-            if (readBufferRef != null) {
-                mReadBuffer = null;
-                readBuffer = readBufferRef.get();
-            }
-            if (readBuffer == null) {
-                readBuffer = new byte[8192];
-                readBufferRef = new WeakReference<byte[]>(readBuffer);
-            }
-        }
-
+        StrictJarFile jarFile = null;
         try {
-            StrictJarFile jarFile = new StrictJarFile(mArchiveSourcePath);
+            jarFile = new StrictJarFile(apkPath);
 
-            Certificate[][] certs = null;
+            // Always verify manifest, regardless of source
+            final ZipEntry manifestEntry = jarFile.findEntry(ANDROID_MANIFEST_FILENAME);
+            if (manifestEntry == null) {
+                throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                        "Package " + apkPath + " has no manifest");
+            }
 
-            if ((flags&PARSE_IS_SYSTEM) != 0) {
-                // If this package comes from the system image, then we
-                // can trust it...  we'll just use the AndroidManifest.xml
-                // to retrieve its signatures, not validating all of the
-                // files.
-                ZipEntry jarEntry = jarFile.findEntry(ANDROID_MANIFEST_FILENAME);
-                certs = loadCertificates(jarFile, jarEntry, readBuffer);
-                if (certs == null) {
-                    Slog.e(TAG, "Package " + pkg.packageName
-                            + " has no certificates at entry "
-                            + jarEntry.getName() + "; ignoring!");
-                    jarFile.close();
-                    mParseError = PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
-                    return false;
+            final List<ZipEntry> toVerify = new ArrayList<>();
+            toVerify.add(manifestEntry);
+
+            // If we're parsing an untrusted package, verify all contents
+            if ((flags & PARSE_IS_SYSTEM) == 0) {
+                final Iterator<ZipEntry> i = jarFile.iterator();
+                while (i.hasNext()) {
+                    final ZipEntry entry = i.next();
+
+                    if (entry.isDirectory()) continue;
+                    if (entry.getName().startsWith("META-INF/")) continue;
+                    if (entry.getName().equals(ANDROID_MANIFEST_FILENAME)) continue;
+
+                    toVerify.add(entry);
                 }
-                if (DEBUG_JAR) {
-                    Slog.i(TAG, "File " + mArchiveSourcePath + ": entry=" + jarEntry
-                            + " certs=" + (certs != null ? certs.length : 0));
-                    if (certs != null) {
-                        final int N = certs.length;
-                        for (int i=0; i<N; i++) {
-                            Slog.i(TAG, "  Public key: "
-                                    + certs[i][0].getPublicKey().getEncoded()
-                                    + " " + certs[i][0].getPublicKey());
-                        }
-                    }
+            }
+
+            // Verify that entries are signed consistently with the first entry
+            // we encountered. Note that for splits, certificates may have
+            // already been populated during an earlier parse of a base APK.
+            for (ZipEntry entry : toVerify) {
+                final Certificate[][] entryCerts = loadCertificates(jarFile, entry);
+                if (ArrayUtils.isEmpty(entryCerts)) {
+                    throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+                            "Package " + apkPath + " has no certificates at entry "
+                            + entry.getName());
                 }
-            } else {
-                Iterator<ZipEntry> entries = jarFile.iterator();
-                while (entries.hasNext()) {
-                    final ZipEntry je = entries.next();
-                    if (je.isDirectory()) continue;
 
-                    final String name = je.getName();
-
-                    if (name.startsWith("META-INF/"))
-                        continue;
-
-                    if (ANDROID_MANIFEST_FILENAME.equals(name)) {
-                        pkg.manifestDigest =
-                                ManifestDigest.fromInputStream(jarFile.getInputStream(je));
+                if (pkg.mCertificates == null) {
+                    pkg.mCertificates = entryCerts;
+                    pkg.mSignatures = convertToSignatures(entryCerts);
+                    pkg.mSigningKeys = new ArraySet<>();
+                    for (int i = 0; i < entryCerts.length; i++) {
+                        pkg.mSigningKeys.add(entryCerts[i][0].getPublicKey());
                     }
-
-                    final Certificate[][] localCerts = loadCertificates(jarFile, je, readBuffer);
-                    if (DEBUG_JAR) {
-                        Slog.i(TAG, "File " + mArchiveSourcePath + " entry " + je.getName()
-                                + ": certs=" + certs + " ("
-                                + (certs != null ? certs.length : 0) + ")");
-                    }
-
-                    if (localCerts == null) {
-                        Slog.e(TAG, "Package " + pkg.packageName
-                                + " has no certificates at entry "
-                                + je.getName() + "; ignoring!");
-                        jarFile.close();
-                        mParseError = PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
-                        return false;
-                    } else if (certs == null) {
-                        certs = localCerts;
-                    } else {
-                        // Ensure all certificates match.
-                        for (int i=0; i<certs.length; i++) {
-                            boolean found = false;
-                            for (int j=0; j<localCerts.length; j++) {
-                                if (certs[i] != null &&
-                                        certs[i].equals(localCerts[j])) {
-                                    found = true;
-                                    break;
-                                }
-                            }
-                            if (!found || certs.length != localCerts.length) {
-                                Slog.e(TAG, "Package " + pkg.packageName
+                } else {
+                    final boolean certsMatch = (pkg.mCertificates.length == entryCerts.length)
+                            && ArrayUtils.containsAll(pkg.mCertificates, entryCerts)
+                            && ArrayUtils.containsAll(entryCerts, pkg.mCertificates);
+                    if (!certsMatch) {
+                        throw new PackageParserException(
+                                INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES, "Package " + apkPath
                                         + " has mismatched certificates at entry "
-                                        + je.getName() + "; ignoring!");
-                                jarFile.close();
-                                mParseError = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
-                                return false;
-                            }
-                        }
+                                        + entry.getName());
                     }
                 }
             }
-            jarFile.close();
-
-            synchronized (mSync) {
-                mReadBuffer = readBufferRef;
-            }
-
-            if (!ArrayUtils.isEmpty(certs)) {
-                pkg.mSignatures = convertToSignatures(certs);
-            } else {
-                Slog.e(TAG, "Package " + pkg.packageName
-                        + " has no certificates; ignoring!");
-                mParseError = PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
-                return false;
-            }
-
-            // Add the signing KeySet to the system
-            pkg.mSigningKeys = new HashSet<PublicKey>();
-            for (int i=0; i < certs.length; i++) {
-                pkg.mSigningKeys.add(certs[i][0].getPublicKey());
-            }
-
-        } catch (CertificateEncodingException e) {
-            Slog.w(TAG, "Exception reading " + mArchiveSourcePath, e);
-            mParseError = PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING;
-            return false;
-        } catch (IOException e) {
-            Slog.w(TAG, "Exception reading " + mArchiveSourcePath, e);
-            mParseError = PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING;
-            return false;
-        } catch (SecurityException e) {
-            Slog.w(TAG, "Exception reading " + mArchiveSourcePath, e);
-            mParseError = PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING;
-            return false;
-        } catch (RuntimeException e) {
-            Slog.w(TAG, "Exception reading " + mArchiveSourcePath, e);
-            mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
-            return false;
+        } catch (GeneralSecurityException | IOException | RuntimeException e) {
+            throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING,
+                    "Failed to collect certificates from " + apkPath, e);
+        } finally {
+            closeQuietly(jarFile);
         }
-
-        return true;
     }
 
     /**
      * Only collect certificates on the manifest; does not validate signatures
      * across remainder of package.
      */
-    private static Signature[] collectCertificates(String packageFilePath) {
+    private static Signature[] collectManifestCertificates(File apkFile)
+            throws PackageParserException {
+        final String apkPath = apkFile.getAbsolutePath();
         try {
-            final StrictJarFile jarFile = new StrictJarFile(packageFilePath);
+            final StrictJarFile jarFile = new StrictJarFile(apkPath);
             try {
                 final ZipEntry jarEntry = jarFile.findEntry(ANDROID_MANIFEST_FILENAME);
-                if (jarEntry != null) {
-                    final Certificate[][] certs = loadCertificates(jarFile, jarEntry, null);
-                    return convertToSignatures(certs);
+                if (jarEntry == null) {
+                    throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                            "Package " + apkPath + " has no manifest");
                 }
+
+                final Certificate[][] certs = loadCertificates(jarFile, jarEntry);
+                return convertToSignatures(certs);
+
             } finally {
                 jarFile.close();
             }
-        } catch (GeneralSecurityException e) {
-            Slog.w(TAG, "Failed to collect certs from " + packageFilePath + ": " + e);
-        } catch (IOException e) {
-            Slog.w(TAG, "Failed to collect certs from " + packageFilePath + ": " + e);
+        } catch (GeneralSecurityException | IOException | RuntimeException e) {
+            throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING,
+                    "Failed to collect certificates from " + apkPath, e);
         }
-        return null;
     }
 
     private static Signature[] convertToSignatures(Certificate[][] certs)
@@ -879,67 +887,55 @@
         return res;
     }
 
-    /*
-     * Utility method that retrieves just the package name and install
-     * location from the apk location at the given file path.
-     * @param packageFilePath file location of the apk
-     * @param flags Special parse flags
-     * @return PackageLite object with package information or null on failure.
+    /**
+     * Utility method that retrieves lightweight details about a single APK
+     * file, including package name, split name, and install location.
+     *
+     * @param apkFile path to a single APK
+     * @param flags optional parse flags, such as {@link #PARSE_GET_SIGNATURES}
      */
-    public static ApkLite parseApkLite(String packageFilePath, int flags) {
+    public static ApkLite parseApkLite(File apkFile, int flags)
+            throws PackageParserException {
+        final String apkPath = apkFile.getAbsolutePath();
+
         AssetManager assmgr = null;
-        final XmlResourceParser parser;
-        final Resources res;
+        XmlResourceParser parser = null;
         try {
             assmgr = new AssetManager();
             assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                     Build.VERSION.RESOURCES_SDK_INT);
 
-            int cookie = assmgr.addAssetPath(packageFilePath);
+            int cookie = assmgr.addAssetPath(apkPath);
             if (cookie == 0) {
-                return null;
+                throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+                        "Failed to parse " + apkPath);
             }
 
             final DisplayMetrics metrics = new DisplayMetrics();
             metrics.setToDefaults();
-            res = new Resources(assmgr, metrics, null);
+
+            final Resources res = new Resources(assmgr, metrics, null);
             parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
-        } catch (Exception e) {
-            if (assmgr != null) assmgr.close();
-            Slog.w(TAG, "Unable to read AndroidManifest.xml of "
-                    + packageFilePath, e);
-            return null;
-        }
 
-        // Only collect certificates on the manifest; does not validate
-        // signatures across remainder of package.
-        final Signature[] signatures;
-        if ((flags & PARSE_GET_SIGNATURES) != 0) {
-            signatures = collectCertificates(packageFilePath);
-        } else {
-            signatures = null;
-        }
+            // Only collect certificates on the manifest; does not validate
+            // signatures across remainder of package.
+            final Signature[] signatures;
+            if ((flags & PARSE_GET_SIGNATURES) != 0) {
+                signatures = collectManifestCertificates(apkFile);
+            } else {
+                signatures = null;
+            }
 
-        final AttributeSet attrs = parser;
-        final String errors[] = new String[1];
-        ApkLite packageLite = null;
-        try {
-            packageLite = parseApkLite(res, parser, attrs, flags, signatures, errors);
-        } catch (PackageParserException e) {
-            Slog.w(TAG, packageFilePath, e);
-        } catch (IOException e) {
-            Slog.w(TAG, packageFilePath, e);
-        } catch (XmlPullParserException e) {
-            Slog.w(TAG, packageFilePath, e);
+            final AttributeSet attrs = parser;
+            return parseApkLite(res, parser, attrs, flags, signatures);
+
+        } catch (XmlPullParserException | IOException | RuntimeException e) {
+            throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+                    "Failed to parse " + apkPath, e);
         } finally {
             if (parser != null) parser.close();
             if (assmgr != null) assmgr.close();
         }
-        if (packageLite == null) {
-            Slog.e(TAG, "parsePackageLite error: " + errors[0]);
-            return null;
-        }
-        return packageLite;
     }
 
     private static String validateName(String name, boolean requiresSeparator) {
@@ -995,12 +991,16 @@
             }
         }
 
-        final String splitName = attrs.getAttributeValue(null, "split");
+        String splitName = attrs.getAttributeValue(null, "split");
         if (splitName != null) {
-            final String error = validateName(splitName, true);
-            if (error != null) {
-                throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
-                        "Invalid manifest split: " + error);
+            if (splitName.length() == 0) {
+                splitName = null;
+            } else {
+                final String error = validateName(splitName, true);
+                if (error != null) {
+                    throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
+                            "Invalid manifest split: " + error);
+                }
             }
         }
 
@@ -1009,8 +1009,8 @@
     }
 
     private static ApkLite parseApkLite(Resources res, XmlPullParser parser,
-            AttributeSet attrs, int flags, Signature[] signatures, String[] outError)
-            throws IOException, XmlPullParserException, PackageParserException {
+            AttributeSet attrs, int flags, Signature[] signatures) throws IOException,
+            XmlPullParserException, PackageParserException {
         final Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs, flags);
 
         int installLocation = PARSE_DEFAULT_INSTALL_LOCATION;
@@ -1043,7 +1043,7 @@
             }
 
             if (parser.getDepth() == searchDepth && "package-verifier".equals(parser.getName())) {
-                final VerifierInfo verifier = parseVerifier(res, parser, attrs, flags, outError);
+                final VerifierInfo verifier = parseVerifier(res, parser, attrs, flags);
                 if (verifier != null) {
                     verifiers.add(verifier);
                 }
@@ -1793,7 +1793,7 @@
             }
         }
 
-        owner.mKeySetMapping = new HashMap<String, Set<PublicKey>>();
+        owner.mKeySetMapping = new ArrayMap<String, ArraySet<PublicKey>>();
         for (Map.Entry<PublicKey, Set<String>> e : definedKeySets.entrySet()) {
             PublicKey key = e.getKey();
             Set<String> keySetNames = e.getValue();
@@ -1801,7 +1801,7 @@
                 if (owner.mKeySetMapping.containsKey(alias)) {
                     owner.mKeySetMapping.get(alias).add(key);
                 } else {
-                    Set<PublicKey> keys = new HashSet<PublicKey>();
+                    ArraySet<PublicKey> keys = new ArraySet<PublicKey>();
                     keys.add(key);
                     owner.mKeySetMapping.put(alias, keys);
                 }
@@ -2612,10 +2612,9 @@
                     com.android.internal.R.styleable.AndroidManifestActivity_windowSoftInputMode,
                     0);
 
-            if (sa.getBoolean(
-                    com.android.internal.R.styleable.AndroidManifestActivity_persistable, false)) {
-                a.info.flags |= ActivityInfo.FLAG_PERSISTABLE;
-            }
+            a.info.persistableMode = sa.getInteger(
+                    com.android.internal.R.styleable.AndroidManifestActivity_persistableMode,
+                    ActivityInfo.PERSIST_ROOT_ONLY);
 
             if (sa.getBoolean(
                     com.android.internal.R.styleable.AndroidManifestActivity_allowEmbedded,
@@ -3433,8 +3432,7 @@
     }
 
     private static VerifierInfo parseVerifier(Resources res, XmlPullParser parser,
-            AttributeSet attrs, int flags, String[] outError) throws XmlPullParserException,
-            IOException {
+            AttributeSet attrs, int flags) {
         final TypedArray sa = res.obtainAttributes(attrs,
                 com.android.internal.R.styleable.AndroidManifestPackageVerifier);
 
@@ -3677,7 +3675,10 @@
         public String packageName;
 
         // TODO: work towards making these paths invariant
+
+        /** Base APK */
         public String codePath;
+        /** Split APKs, ordered by parsed splitName */
         public String[] splitCodePaths;
 
         // For now we only support one application per package.
@@ -3723,7 +3724,8 @@
         public int mSharedUserLabel;
 
         // Signatures that were read from the package.
-        public Signature mSignatures[];
+        public Signature[] mSignatures;
+        public Certificate[][] mCertificates;
 
         // For use by package manager service for quick lookup of
         // preferred up order.
@@ -3785,8 +3787,8 @@
         /**
          * Data used to feed the KeySetManager
          */
-        public Set<PublicKey> mSigningKeys;
-        public Map<String, Set<PublicKey>> mKeySetMapping;
+        public ArraySet<PublicKey> mSigningKeys;
+        public ArrayMap<String, ArraySet<PublicKey>> mKeySetMapping;
 
         public Package(String packageName) {
             this.packageName = packageName;
@@ -3794,6 +3796,15 @@
             applicationInfo.uid = -1;
         }
 
+        public Collection<String> getAllCodePaths() {
+            ArrayList<String> paths = new ArrayList<>();
+            paths.add(codePath);
+            if (!ArrayUtils.isEmpty(splitCodePaths)) {
+                Collections.addAll(paths, splitCodePaths);
+            }
+            return paths;
+        }
+
         public void setPackageName(String newName) {
             packageName = newName;
             applicationInfo.packageName = newName;
@@ -4396,6 +4407,33 @@
         sCompatibilityModeEnabled = compatibilityModeEnabled;
     }
 
+    private static AtomicReference<byte[]> sBuffer = new AtomicReference<byte[]>();
+
+    public static long readFullyIgnoringContents(InputStream in) throws IOException {
+        byte[] buffer = sBuffer.getAndSet(null);
+        if (buffer == null) {
+            buffer = new byte[4096];
+        }
+
+        int n = 0;
+        int count = 0;
+        while ((n = in.read(buffer, 0, buffer.length)) != -1) {
+            count += n;
+        }
+
+        sBuffer.set(buffer);
+        return count;
+    }
+
+    public static void closeQuietly(StrictJarFile jarFile) {
+        if (jarFile != null) {
+            try {
+                jarFile.close();
+            } catch (Exception ignored) {
+            }
+        }
+    }
+
     public static class PackageParserException extends Exception {
         public final int error;
 
@@ -4403,5 +4441,10 @@
             super(detailMessage);
             this.error = error;
         }
+
+        public PackageParserException(int error, String detailMessage, Throwable throwable) {
+            super(detailMessage, throwable);
+            this.error = error;
+        }
     }
 }
diff --git a/core/java/android/hardware/hdmi/HdmiCec.java b/core/java/android/hardware/hdmi/HdmiCec.java
index 8ad9463..d86dd5e 100644
--- a/core/java/android/hardware/hdmi/HdmiCec.java
+++ b/core/java/android/hardware/hdmi/HdmiCec.java
@@ -185,6 +185,7 @@
     public static final int RESULT_TARGET_NOT_AVAILABLE = 3;
     public static final int RESULT_ALREADY_IN_PROGRESS = 4;
     public static final int RESULT_EXCEPTION = 5;
+    public static final int RESULT_INCORRECT_MODE = 6;
 
     private static final int[] ADDRESS_TO_TYPE = {
         DEVICE_TV,  // ADDR_TV
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 535bbe24..8142670 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -699,15 +699,15 @@
                 break;
             }
 
-            try {
-                if (enable) {
-                    return mPhoneService.enableApnType(apnType);
-                } else {
-                    return mPhoneService.disableApnType(apnType);
-                }
-            } catch (RemoteException e) {
-                if (retry == 0) getPhoneService(true);
-            }
+//            try {
+//                if (enable) {
+//                    return mPhoneService.enableApnType(apnType);
+//                } else {
+//                    return mPhoneService.disableApnType(apnType);
+//                }
+//            } catch (RemoteException e) {
+//                if (retry == 0) getPhoneService(true);
+//            }
         }
 
         loge("Could not " + (enable ? "enable" : "disable") + " APN type \"" + apnType + "\"");
diff --git a/core/java/android/os/BatteryManagerInternal.java b/core/java/android/os/BatteryManagerInternal.java
new file mode 100644
index 0000000..f3a95b9
--- /dev/null
+++ b/core/java/android/os/BatteryManagerInternal.java
@@ -0,0 +1,49 @@
+/*
+ * 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.os;
+
+/**
+ * Battery manager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class BatteryManagerInternal {
+    /**
+     * Returns true if the device is plugged into any of the specified plug types.
+     */
+    public abstract boolean isPowered(int plugTypeSet);
+
+    /**
+     * Returns the current plug type.
+     */
+    public abstract int getPlugType();
+
+    /**
+     * Returns battery level as a percentage.
+     */
+    public abstract int getBatteryLevel();
+
+    /**
+     * Returns whether we currently consider the battery level to be low.
+     */
+    public abstract boolean getBatteryLevelLow();
+
+    /**
+     * Returns a non-zero value if an unsupported charger is attached.
+     */
+    public abstract int getInvalidCharger();
+}
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index 69b828f..08a15eb 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -16,8 +16,6 @@
 
 package android.os;
 
-import android.view.WindowManagerPolicy;
-
 /**
  * Power manager local system service interface.
  *
@@ -57,12 +55,9 @@
 
     public abstract boolean getLowPowerModeEnabled();
 
+    public abstract void registerLowPowerModeObserver(LowPowerModeListener listener);
+
     public interface LowPowerModeListener {
         public void onLowPowerModeChanged(boolean enabled);
     }
-
-    public abstract void registerLowPowerModeObserver(LowPowerModeListener listener);
-
-    // TODO: Remove this and retrieve as a local service instead.
-    public abstract void setPolicy(WindowManagerPolicy policy);
 }
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 468cfe0..757f38e 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -226,14 +226,14 @@
     public static final String DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks";
 
     /**
-     * Key for user restrictions. Specifies if a user is disallowed from configuring
+     * Key for user restrictions. Specifies if a user is disallowed from controlling
      * applications in Settings. The default value is <code>false</code>.
      * <p>
      * Type: Boolean
      * @see #setUserRestrictions(Bundle)
      * @see #getUserRestrictions()
      */
-    public static final String DISALLOW_CONFIG_APPS = "no_config_apps";
+    public static final String DISALLOW_APPS_CONTROL = "no_control_apps";
 
     /**
      * Key for user restrictions. Specifies if a user is disallowed from mounting
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index 0418049..23b1e2c 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -1194,7 +1194,14 @@
      * @param args Optional arguments to supply to the fragment.
      */
     public void switchToHeader(String fragmentName, Bundle args) {
-        setSelectedHeader(null);
+        Header selectedHeader = null;
+        for (int i = 0; i < mHeaders.size(); i++) {
+            if (fragmentName.equals(mHeaders.get(i).fragment)) {
+                selectedHeader = mHeaders.get(i);
+                break;
+            }
+        }
+        setSelectedHeader(selectedHeader);
         switchToHeaderInner(fragmentName, args);
     }
 
diff --git a/core/java/android/service/fingerprint/FingerprintManager.java b/core/java/android/service/fingerprint/FingerprintManager.java
index b6137d1..5fd597b 100644
--- a/core/java/android/service/fingerprint/FingerprintManager.java
+++ b/core/java/android/service/fingerprint/FingerprintManager.java
@@ -96,6 +96,9 @@
         }
     };
 
+    /**
+     * @hide
+     */
     public FingerprintManager(Context context, IFingerprintService service) {
         mContext = context;
         mService = service;
diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java
index 508769d..e936232 100644
--- a/core/java/android/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -673,7 +673,7 @@
                             startDelays.put(mAnimators.size(), delay);
                             minStartDelay = Math.min(delay, minStartDelay);
                         }
-                        AnimationInfo info = new AnimationInfo(view, getName(),
+                        AnimationInfo info = new AnimationInfo(view, getName(), this,
                                 sceneRoot.getWindowId(), infoValues);
                         runningAnimators.put(animator, info);
                         mAnimators.add(animator);
@@ -1587,30 +1587,10 @@
                 AnimationInfo oldInfo = runningAnimators.get(anim);
                 if (oldInfo != null && oldInfo.view != null &&
                         oldInfo.view.getContext() == sceneRoot.getContext()) {
-                    boolean cancel = false;
                     TransitionValues oldValues = oldInfo.values;
                     View oldView = oldInfo.view;
                     TransitionValues newValues = mEndValues.viewValues.get(oldView);
-                    if (oldValues != null) {
-                        // if oldValues null, then transition didn't care to stash values,
-                        // and won't get canceled
-                        if (newValues != null) {
-                            for (String key : oldValues.values.keySet()) {
-                                Object oldValue = oldValues.values.get(key);
-                                Object newValue = newValues.values.get(key);
-                                if (oldValue != null && newValue != null &&
-                                        !oldValue.equals(newValue)) {
-                                    cancel = true;
-                                    if (DBG) {
-                                        Log.d(LOG_TAG, "Transition.playTransition: " +
-                                                "oldValue != newValue for " + key +
-                                                ": old, new = " + oldValue + ", " + newValue);
-                                    }
-                                    break;
-                                }
-                            }
-                        }
-                    }
+                    boolean cancel = oldInfo.transition.areValuesChanged(oldValues, newValues);
                     if (cancel) {
                         if (anim.isRunning() || anim.isStarted()) {
                             if (DBG) {
@@ -1632,6 +1612,29 @@
         runAnimators();
     }
 
+    boolean areValuesChanged(TransitionValues oldValues, TransitionValues newValues) {
+        boolean valuesChanged = false;
+        // if oldValues null, then transition didn't care to stash values,
+        // and won't get canceled
+        if (oldValues != null && newValues != null) {
+            for (String key : oldValues.values.keySet()) {
+                Object oldValue = oldValues.values.get(key);
+                Object newValue = newValues.values.get(key);
+                if (oldValue != null && newValue != null &&
+                        !oldValue.equals(newValue)) {
+                    valuesChanged = true;
+                    if (DBG) {
+                        Log.d(LOG_TAG, "Transition.playTransition: " +
+                                "oldValue != newValue for " + key +
+                                ": old, new = " + oldValue + ", " + newValue);
+                    }
+                    break;
+                }
+            }
+        }
+        return valuesChanged;
+    }
+
     /**
      * This is a utility method used by subclasses to handle standard parts of
      * setting up and running an Animator: it sets the {@link #getDuration()
@@ -2070,12 +2073,15 @@
         String name;
         TransitionValues values;
         WindowId windowId;
+        Transition transition;
 
-        AnimationInfo(View view, String name, WindowId windowId, TransitionValues values) {
+        AnimationInfo(View view, String name, Transition transition,
+                WindowId windowId, TransitionValues values) {
             this.view = view;
             this.name = name;
             this.values = values;
             this.windowId = windowId;
+            this.transition = transition;
         }
     }
 
diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java
index c6c8337..947e1a7 100644
--- a/core/java/android/transition/Visibility.java
+++ b/core/java/android/transition/Visibility.java
@@ -331,16 +331,6 @@
                     public void onAnimationEnd(Animator animation) {
                         finalSceneRoot.getOverlay().remove(finalOverlayView);
                     }
-
-                    @Override
-                    public void onAnimationPause(Animator animation) {
-                        finalSceneRoot.getOverlay().remove(finalOverlayView);
-                    }
-
-                    @Override
-                    public void onAnimationResume(Animator animation) {
-                        finalSceneRoot.getOverlay().add(finalOverlayView);
-                    }
                 });
             }
             return animator;
@@ -409,6 +399,16 @@
         return overlayView;
     }
 
+    @Override
+    boolean areValuesChanged(TransitionValues oldValues, TransitionValues newValues) {
+        VisibilityInfo changeInfo = getVisibilityChangeInfo(oldValues, newValues);
+        if (oldValues == null && newValues == null) {
+            return false;
+        }
+        return changeInfo.visibilityChange && (changeInfo.startVisibility == View.VISIBLE ||
+            changeInfo.endVisibility == View.VISIBLE);
+    }
+
     /**
      * The default implementation of this method returns a null Animator. Subclasses should
      * override this method to make targets disappear with the desired transition. The
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index d7a913d..181f77e 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -599,6 +599,42 @@
     }
 
     /**
+     * Gets the app VSYNC offset, in nanoseconds.  This is a positive value indicating
+     * the phase offset of the VSYNC events provided by Choreographer relative to the
+     * display refresh.  For example, if Choreographer reports that the refresh occurred
+     * at time N, it actually occurred at (N - appVsyncOffset).
+     * <p>
+     * Apps generally do not need to be aware of this.  It's only useful for fine-grained
+     * A/V synchronization.
+     * @hide
+     */
+    public long getAppVsyncOffsetNanos() {
+        synchronized (this) {
+            updateDisplayInfoLocked();
+            return mDisplayInfo.appVsyncOffsetNanos;
+        }
+    }
+
+    /**
+     * This is how far in advance a buffer must be queued for presentation at
+     * a given time.  If you want a buffer to appear on the screen at
+     * time N, you must submit the buffer before (N - presentationDeadline).
+     * <p>
+     * The desired presentation time for GLES rendering may be set with
+     * {@link android.opengl.EGLExt#eglPresentationTimeANDROID}.  For video decoding, use
+     * {@link android.media.MediaCodec#releaseOutputBuffer(int, long)}.  Times are
+     * expressed in nanoseconds, using the system monotonic clock
+     * ({@link System#nanoTime}).
+     * @hide
+     */
+    public long getPresentationDeadlineNanos() {
+        synchronized (this) {
+            updateDisplayInfoLocked();
+            return mDisplayInfo.presentationDeadlineNanos;
+        }
+    }
+
+    /**
      * Gets display metrics that describe the size and density of this display.
      * <p>
      * The size is adjusted based on the current rotation of the display.
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index b0fe0fa..98696c7 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -180,6 +180,20 @@
     public float physicalYDpi;
 
     /**
+     * This is a positive value indicating the phase offset of the VSYNC events provided by
+     * Choreographer relative to the display refresh.  For example, if Choreographer reports
+     * that the refresh occurred at time N, it actually occurred at (N - appVsyncOffsetNanos).
+     */
+    public long appVsyncOffsetNanos;
+
+    /**
+     * This is how far in advance a buffer must be queued for presentation at
+     * a given time.  If you want a buffer to appear on the screen at
+     * time N, you must submit the buffer before (N - bufferDeadlineNanos).
+     */
+    public long presentationDeadlineNanos;
+
+    /**
      * The state of the display, such as {@link android.view.Display#STATE_ON}.
      */
     public int state;
@@ -253,6 +267,8 @@
                 && logicalDensityDpi == other.logicalDensityDpi
                 && physicalXDpi == other.physicalXDpi
                 && physicalYDpi == other.physicalYDpi
+                && appVsyncOffsetNanos == other.appVsyncOffsetNanos
+                && presentationDeadlineNanos == other.presentationDeadlineNanos
                 && state == other.state
                 && ownerUid == other.ownerUid
                 && Objects.equal(ownerPackageName, other.ownerPackageName);
@@ -286,6 +302,8 @@
         logicalDensityDpi = other.logicalDensityDpi;
         physicalXDpi = other.physicalXDpi;
         physicalYDpi = other.physicalYDpi;
+        appVsyncOffsetNanos = other.appVsyncOffsetNanos;
+        presentationDeadlineNanos = other.presentationDeadlineNanos;
         state = other.state;
         ownerUid = other.ownerUid;
         ownerPackageName = other.ownerPackageName;
@@ -314,6 +332,8 @@
         logicalDensityDpi = source.readInt();
         physicalXDpi = source.readFloat();
         physicalYDpi = source.readFloat();
+        appVsyncOffsetNanos = source.readLong();
+        presentationDeadlineNanos = source.readLong();
         state = source.readInt();
         ownerUid = source.readInt();
         ownerPackageName = source.readString();
@@ -343,6 +363,8 @@
         dest.writeInt(logicalDensityDpi);
         dest.writeFloat(physicalXDpi);
         dest.writeFloat(physicalYDpi);
+        dest.writeLong(appVsyncOffsetNanos);
+        dest.writeLong(presentationDeadlineNanos);
         dest.writeInt(state);
         dest.writeInt(ownerUid);
         dest.writeString(ownerPackageName);
@@ -450,6 +472,10 @@
         sb.append(physicalYDpi);
         sb.append(") dpi, layerStack ");
         sb.append(layerStack);
+        sb.append(", appVsyncOff ");
+        sb.append(appVsyncOffsetNanos);
+        sb.append(", presDeadline ");
+        sb.append(presentationDeadlineNanos);
         sb.append(", type ");
         sb.append(Display.typeToString(type));
         if (address != null) {
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 446bbc9..68e2146 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -804,20 +804,12 @@
 
     @Override
     public void drawPicture(Picture picture) {
-        if (picture.createdFromStream) {
-            return;
-        }
-
         picture.endRecording();
         // TODO: Implement rendering
     }
 
     @Override
     public void drawPicture(Picture picture, Rect dst) {
-        if (picture.createdFromStream) {
-            return;
-        }
-
         save();
         translate(dst.left, dst.top);
         if (picture.getWidth() > 0 && picture.getHeight() > 0) {
@@ -829,10 +821,6 @@
 
     @Override
     public void drawPicture(Picture picture, RectF dst) {
-        if (picture.createdFromStream) {
-            return;
-        }
-
         save();
         translate(dst.left, dst.top);
         if (picture.getWidth() > 0 && picture.getHeight() > 0) {
@@ -981,22 +969,24 @@
         }
 
         nDrawTextOnPath(mRenderer, text, index, count, path.mNativePath, hOffset, vOffset,
-                paint.mBidiFlags, paint.mNativePaint);
+                paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
     }
 
     private static native void nDrawTextOnPath(long renderer, char[] text, int index, int count,
-            long path, float hOffset, float vOffset, int bidiFlags, long nativePaint);
+            long path, float hOffset, float vOffset, int bidiFlags, long nativePaint,
+            long typeface);
 
     @Override
     public void drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint) {
         if (text.length() == 0) return;
 
         nDrawTextOnPath(mRenderer, text, 0, text.length(), path.mNativePath, hOffset, vOffset,
-                paint.mBidiFlags, paint.mNativePaint);
+                paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
     }
 
     private static native void nDrawTextOnPath(long renderer, String text, int start, int end,
-            long path, float hOffset, float vOffset, int bidiFlags, long nativePaint);
+            long path, float hOffset, float vOffset, int bidiFlags, long nativePaint,
+            long typeface);
 
     @Override
     public void drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount,
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 79f19b5..94d8f70 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -458,6 +458,8 @@
         public float xDpi;
         public float yDpi;
         public boolean secure;
+        public long appVsyncOffsetNanos;
+        public long presentationDeadlineNanos;
 
         public PhysicalDisplayInfo() {
         }
@@ -479,7 +481,9 @@
                     && density == other.density
                     && xDpi == other.xDpi
                     && yDpi == other.yDpi
-                    && secure == other.secure;
+                    && secure == other.secure
+                    && appVsyncOffsetNanos == other.appVsyncOffsetNanos
+                    && presentationDeadlineNanos == other.presentationDeadlineNanos;
         }
 
         @Override
@@ -495,6 +499,8 @@
             xDpi = other.xDpi;
             yDpi = other.yDpi;
             secure = other.secure;
+            appVsyncOffsetNanos = other.appVsyncOffsetNanos;
+            presentationDeadlineNanos = other.presentationDeadlineNanos;
         }
 
         // For debugging purposes
@@ -502,7 +508,8 @@
         public String toString() {
             return "PhysicalDisplayInfo{" + width + " x " + height + ", " + refreshRate + " fps, "
                     + "density " + density + ", " + xDpi + " x " + yDpi + " dpi, secure " + secure
-                    + "}";
+                    + ", appVsyncOffset " + appVsyncOffsetNanos
+                    + ", bufferDeadline " + presentationDeadlineNanos + "}";
         }
     }
 
diff --git a/core/java/com/android/internal/policy/IFaceLockInterface.aidl b/core/java/com/android/internal/policy/IFaceLockInterface.aidl
index 017801b..bc1f002 100644
--- a/core/java/com/android/internal/policy/IFaceLockInterface.aidl
+++ b/core/java/com/android/internal/policy/IFaceLockInterface.aidl
@@ -23,6 +23,7 @@
     void startUi(IBinder containingWindowToken, int x, int y, int width, int height,
             boolean useLiveliness);
     void stopUi();
+    void startWithoutUi();
     void registerCallback(IFaceLockCallback cb);
     void unregisterCallback(IFaceLockCallback cb);
 }
diff --git a/core/java/com/android/internal/widget/SwipeDismissLayout.java b/core/java/com/android/internal/widget/SwipeDismissLayout.java
index 002573e..97b1634 100644
--- a/core/java/com/android/internal/widget/SwipeDismissLayout.java
+++ b/core/java/com/android/internal/widget/SwipeDismissLayout.java
@@ -45,10 +45,9 @@
         /**
          * Called when the layout has been swiped and the position of the window should change.
          *
-         * @param progress A number in [-1, 1] representing how far to the left
-         * or right the window has been swiped. Negative values are swipes
-         * left, and positives are right.
-         * @param translate A number in [-w, w], where w is the width of the
+         * @param progress A number in [0, 1] representing how far to the
+         * right the window has been swiped
+         * @param translate A number in [0, w], where w is the width of the
          * layout. This is equivalent to progress * layout.getWidth().
          */
         void onSwipeProgressChanged(SwipeDismissLayout layout, float progress, float translate);
@@ -207,7 +206,7 @@
 
     private void setProgress(float deltaX) {
         mTranslationX = deltaX;
-        if (mProgressListener != null) {
+        if (mProgressListener != null && deltaX >= 0)  {
             mProgressListener.onSwipeProgressChanged(this, deltaX / getWidth(), deltaX);
         }
     }
diff --git a/core/java/com/android/server/SystemServiceManager.java b/core/java/com/android/server/SystemServiceManager.java
index 87a50a9..fda6479 100644
--- a/core/java/com/android/server/SystemServiceManager.java
+++ b/core/java/com/android/server/SystemServiceManager.java
@@ -50,8 +50,19 @@
      * @return The service instance.
      */
     @SuppressWarnings("unchecked")
-    public SystemService startService(String className) throws ClassNotFoundException {
-        return startService((Class<SystemService>) Class.forName(className));
+    public SystemService startService(String className) {
+        final Class<SystemService> serviceClass;
+        try {
+            serviceClass = (Class<SystemService>)Class.forName(className);
+        } catch (ClassNotFoundException ex) {
+            Slog.i(TAG, "Starting " + className);
+            throw new RuntimeException("Failed to create service " + className
+                    + ": service class not found, usually indicates that the caller should "
+                    + "have called PackageManager.hasSystemFeature() to check whether the "
+                    + "feature is available on this device before trying to start the "
+                    + "services that implement it", ex);
+        }
+        return startService(serviceClass);
     }
 
     /**
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 15dfed1..cb00062 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -89,7 +89,7 @@
 	android_util_Process.cpp \
 	android_util_StringBlock.cpp \
 	android_util_XmlBlock.cpp \
-	android/graphics/AndroidPicture.cpp \
+	android_graphics_Picture.cpp \
 	android/graphics/AutoDecodeCancel.cpp \
 	android/graphics/Bitmap.cpp \
 	android/graphics/BitmapFactory.cpp \
diff --git a/core/jni/android/graphics/AndroidPicture.cpp b/core/jni/android/graphics/AndroidPicture.cpp
deleted file mode 100644
index 5977ab2..0000000
--- a/core/jni/android/graphics/AndroidPicture.cpp
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "AndroidPicture.h"
-#include "SkCanvas.h"
-#include "SkStream.h"
-
-AndroidPicture::AndroidPicture(const AndroidPicture* src) {
-    if (NULL != src) {
-        mWidth = src->width();
-        mHeight = src->height();
-        if (NULL != src->mPicture.get()) {
-            mPicture.reset(SkRef(src->mPicture.get()));
-        } if (NULL != src->mRecorder.get()) {
-            mPicture.reset(src->makePartialCopy());
-        }
-    } else {
-        mWidth = 0;
-        mHeight = 0;
-    }
-}
-
-SkCanvas* AndroidPicture::beginRecording(int width, int height) {
-    mPicture.reset(NULL);
-    mRecorder.reset(new SkPictureRecorder);
-    mWidth = width;
-    mHeight = height;
-    return mRecorder->beginRecording(width, height, NULL, 0);
-}
-
-void AndroidPicture::endRecording() {
-    if (NULL != mRecorder.get()) {
-        mPicture.reset(mRecorder->endRecording());
-        mRecorder.reset(NULL);
-    }
-}
-
-int AndroidPicture::width() const {
-    if (NULL != mPicture.get()) {
-        SkASSERT(mPicture->width() == mWidth);
-        SkASSERT(mPicture->height() == mHeight);
-    }
-
-    return mWidth;
-}
-
-int AndroidPicture::height() const {
-    if (NULL != mPicture.get()) {
-        SkASSERT(mPicture->width() == mWidth);
-        SkASSERT(mPicture->height() == mHeight);
-    }
-
-    return mHeight;
-}
-
-AndroidPicture* AndroidPicture::CreateFromStream(SkStream* stream) {
-    AndroidPicture* newPict = new AndroidPicture;
-
-    newPict->mPicture.reset(SkPicture::CreateFromStream(stream));
-    if (NULL != newPict->mPicture.get()) {
-        newPict->mWidth = newPict->mPicture->width();
-        newPict->mHeight = newPict->mPicture->height();
-    }
-
-    return newPict;
-}
-
-void AndroidPicture::serialize(SkWStream* stream) const {
-    if (NULL != mRecorder.get()) {
-        SkAutoTDelete<SkPicture> tempPict(this->makePartialCopy());
-        tempPict->serialize(stream);
-    } else if (NULL != mPicture.get()) {
-        mPicture->serialize(stream);
-    } else {
-        SkPicture empty;
-        empty.serialize(stream);
-    }
-}
-
-void AndroidPicture::draw(SkCanvas* canvas) {
-    if (NULL != mRecorder.get()) {
-        this->endRecording();
-        SkASSERT(NULL != mPicture.get());
-    }
-    if (NULL != mPicture.get()) {
-        // TODO: remove this const_cast once pictures are immutable
-        const_cast<SkPicture*>(mPicture.get())->draw(canvas);
-    }
-}
-
-SkPicture* AndroidPicture::makePartialCopy() const {
-    SkASSERT(NULL != mRecorder.get());
-
-    SkPictureRecorder reRecorder;
-
-    SkCanvas* canvas = reRecorder.beginRecording(mWidth, mHeight, NULL, 0);
-    mRecorder->partialReplay(canvas);
-    return reRecorder.endRecording();
-}
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index 4584c46..69e149e 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -1123,29 +1123,82 @@
         delete[] posPtr;
     }
 
+#ifdef USE_MINIKIN
+    class DrawTextOnPathFunctor {
+    public:
+        DrawTextOnPathFunctor(const Layout& layout, SkCanvas* canvas, float hOffset,
+                    float vOffset, SkPaint* paint, SkPath* path)
+                : layout(layout), canvas(canvas), hOffset(hOffset), vOffset(vOffset),
+                    paint(paint), path(path) {
+        }
+        void operator()(size_t start, size_t end) {
+            uint16_t glyphs[1];
+            for (size_t i = start; i < end; i++) {
+                glyphs[0] = layout.getGlyphId(i);
+                float x = hOffset + layout.getX(i);
+                float y = vOffset + layout.getY(i);
+                canvas->drawTextOnPathHV(glyphs, sizeof(glyphs), *path, x, y, *paint);
+            }
+        }
+    private:
+        const Layout& layout;
+        SkCanvas* canvas;
+        float hOffset;
+        float vOffset;
+        SkPaint* paint;
+        SkPath* path;
+    };
+#endif
+
+    static void doDrawTextOnPath(SkPaint* paint, const jchar* text, int count, int bidiFlags,
+            float hOffset, float vOffset, SkPath* path, SkCanvas* canvas, TypefaceImpl* typeface) {
+#ifdef USE_MINIKIN
+        Layout layout;
+        std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface);
+        layout.doLayout(text, 0, count, count, css);
+        hOffset += MinikinUtils::hOffsetForTextAlign(paint, layout, *path);
+        // Set align to left for drawing, as we don't want individual
+        // glyphs centered or right-aligned; the offset above takes
+        // care of all alignment.
+        SkPaint::Align align = paint->getTextAlign();
+        paint->setTextAlign(SkPaint::kLeft_Align);
+
+        DrawTextOnPathFunctor f(layout, canvas, hOffset, vOffset, paint, path);
+        MinikinUtils::forFontRun(layout, paint, f);
+        paint->setTextAlign(align);
+#else
+        TextLayout::drawTextOnPath(paint, text, count, bidiFlags, hOffset, vOffset, path, canvas);
+#endif
+    }
+
     static void drawTextOnPath___CIIPathFFPaint(JNIEnv* env, jobject,
             jlong canvasHandle, jcharArray text, jint index, jint count,
-            jlong pathHandle, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintHandle) {
+            jlong pathHandle, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintHandle,
+            jlong typefaceHandle) {
         SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
         SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
 
         jchar* textArray = env->GetCharArrayElements(text, NULL);
-        TextLayout::drawTextOnPath(paint, textArray + index, count, bidiFlags, hOffset, vOffset,
-                                   path, canvas);
+        doDrawTextOnPath(paint, textArray + index, count, bidiFlags, hOffset, vOffset,
+                                   path, canvas, typeface);
         env->ReleaseCharArrayElements(text, textArray, 0);
     }
 
     static void drawTextOnPath__StringPathFFPaint(JNIEnv* env, jobject,
             jlong canvasHandle, jstring text, jlong pathHandle,
-            jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintHandle) {
+            jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintHandle,
+            jlong typefaceHandle) {
         SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
         SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+
         const jchar* text_ = env->GetStringChars(text, NULL);
         int count = env->GetStringLength(text);
-        TextLayout::drawTextOnPath(paint, text_, count, bidiFlags, hOffset, vOffset,
-                                   path, canvas);
+        doDrawTextOnPath(paint, text_, count, bidiFlags, hOffset, vOffset,
+                                   path, canvas, typeface);
         env->ReleaseStringChars(text, text_);
     }
 
@@ -1271,9 +1324,9 @@
         (void*) SkCanvasGlue::drawPosText___CII_FPaint},
     {"native_drawPosText","(JLjava/lang/String;[FJ)V",
         (void*) SkCanvasGlue::drawPosText__String_FPaint},
-    {"native_drawTextOnPath","(J[CIIJFFIJ)V",
+    {"native_drawTextOnPath","(J[CIIJFFIJJ)V",
         (void*) SkCanvasGlue::drawTextOnPath___CIIPathFFPaint},
-    {"native_drawTextOnPath","(JLjava/lang/String;JFFIJ)V",
+    {"native_drawTextOnPath","(JLjava/lang/String;JFFIJJ)V",
         (void*) SkCanvasGlue::drawTextOnPath__StringPathFFPaint},
 
     {"freeCaches", "()V", (void*) SkCanvasGlue::freeCaches},
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index a4337ccc..2bd7a28 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -3,7 +3,6 @@
 #include "jni.h"
 #include "JNIHelp.h"
 #include "GraphicsJNI.h"
-#include "AndroidPicture.h"
 
 #include "SkCanvas.h"
 #include "SkDevice.h"
@@ -346,17 +345,6 @@
     return p;
 }
 
-AndroidPicture* GraphicsJNI::getNativePicture(JNIEnv* env, jobject picture)
-{
-    SkASSERT(env);
-    SkASSERT(picture);
-    SkASSERT(env->IsInstanceOf(picture, gPicture_class));
-    jlong pictureHandle = env->GetLongField(picture, gPicture_nativeInstanceID);
-    AndroidPicture* p = reinterpret_cast<AndroidPicture*>(pictureHandle);
-    SkASSERT(p);
-    return p;
-}
-
 SkRegion* GraphicsJNI::getNativeRegion(JNIEnv* env, jobject region)
 {
     SkASSERT(env);
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 2e2f920..ad174f7 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -14,7 +14,6 @@
 class SkBitmapRegionDecoder;
 class SkCanvas;
 class SkPaint;
-class AndroidPicture;
 
 class GraphicsJNI {
 public:
@@ -50,7 +49,6 @@
     static SkPaint*  getNativePaint(JNIEnv*, jobject paint);
     static android::TypefaceImpl* getNativeTypeface(JNIEnv*, jobject paint);
     static SkBitmap* getNativeBitmap(JNIEnv*, jobject bitmap);
-    static AndroidPicture* getNativePicture(JNIEnv*, jobject picture);
     static SkRegion* getNativeRegion(JNIEnv*, jobject region);
 
     // Given the 'native' long held by the Rasterizer.java object, return a
diff --git a/core/jni/android/graphics/MinikinUtils.cpp b/core/jni/android/graphics/MinikinUtils.cpp
index a9360ea..fc92d53 100644
--- a/core/jni/android/graphics/MinikinUtils.cpp
+++ b/core/jni/android/graphics/MinikinUtils.cpp
@@ -19,6 +19,7 @@
 #include <string>
 
 #include "SkPaint.h"
+#include "SkPathMeasure.h"
 #include "minikin/Layout.h"
 #include "TypefaceImpl.h"
 #include "MinikinSkia.h"
@@ -76,4 +77,20 @@
     return 0;
 }
 
+float MinikinUtils::hOffsetForTextAlign(SkPaint* paint, const Layout& layout, const SkPath& path) {
+    float align = 0;
+    switch (paint->getTextAlign()) {
+        case SkPaint::kCenter_Align:
+            align = -0.5f;
+            break;
+        case SkPaint::kRight_Align:
+            align = -1;
+            break;
+        default:
+            return 0;
+    }
+    SkPathMeasure measure(path, false);
+    return align * (layout.getAdvance() - measure.getLength());
+}
+
 }
diff --git a/core/jni/android/graphics/MinikinUtils.h b/core/jni/android/graphics/MinikinUtils.h
index a96c6b1..b2662a1 100644
--- a/core/jni/android/graphics/MinikinUtils.h
+++ b/core/jni/android/graphics/MinikinUtils.h
@@ -36,6 +36,7 @@
 
     static float xOffsetForTextAlign(SkPaint* paint, const Layout& layout);
 
+    static float hOffsetForTextAlign(SkPaint* paint, const Layout& layout, const SkPath& path);
     // f is a functor of type void f(size_t start, size_t end);
     template <typename F>
     static void forFontRun(const Layout& layout, SkPaint* paint, F& f) {
diff --git a/core/jni/android/graphics/Picture.cpp b/core/jni/android/graphics/Picture.cpp
index 0683f73..bc0c25f 100644
--- a/core/jni/android/graphics/Picture.cpp
+++ b/core/jni/android/graphics/Picture.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * 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.
@@ -14,123 +14,104 @@
  * limitations under the License.
  */
 
-#include "jni.h"
-#include "GraphicsJNI.h"
-#include <android_runtime/AndroidRuntime.h>
-#include "AndroidPicture.h"
+#include "Picture.h"
 
 #include "SkCanvas.h"
 #include "SkStream.h"
-#include "SkTemplates.h"
-#include "CreateJavaOutputStreamAdaptor.h"
 
 namespace android {
 
-class SkPictureGlue {
-public:
-    static jlong newPicture(JNIEnv* env, jobject, jlong srcHandle) {
-        const AndroidPicture* src = reinterpret_cast<AndroidPicture*>(srcHandle);
-        return reinterpret_cast<jlong>(new AndroidPicture(src));
-    }
-
-    static jlong deserialize(JNIEnv* env, jobject, jobject jstream,
-                             jbyteArray jstorage) {
-        AndroidPicture* picture = NULL;
-        SkStream* strm = CreateJavaInputStreamAdaptor(env, jstream, jstorage);
-        if (strm) {
-            picture = AndroidPicture::CreateFromStream(strm);
-            delete strm;
+Picture::Picture(const Picture* src) {
+    if (NULL != src) {
+        mWidth = src->width();
+        mHeight = src->height();
+        if (NULL != src->mPicture.get()) {
+            mPicture.reset(SkRef(src->mPicture.get()));
+        } if (NULL != src->mRecorder.get()) {
+            mPicture.reset(src->makePartialCopy());
         }
-        return reinterpret_cast<jlong>(picture);
+    } else {
+        mWidth = 0;
+        mHeight = 0;
     }
-
-    static void killPicture(JNIEnv* env, jobject, jlong pictureHandle) {
-        AndroidPicture* picture = reinterpret_cast<AndroidPicture*>(pictureHandle);
-        SkASSERT(picture);
-        delete picture;
-    }
-
-    static void draw(JNIEnv* env, jobject, jlong canvasHandle,
-                            jlong pictureHandle) {
-        SkCanvas* canvas = GraphicsJNI::getNativeCanvas(canvasHandle);
-        AndroidPicture* picture = reinterpret_cast<AndroidPicture*>(pictureHandle);
-        SkASSERT(canvas);
-        SkASSERT(picture);
-        picture->draw(canvas);
-    }
-
-    static jboolean serialize(JNIEnv* env, jobject, jlong pictureHandle,
-                              jobject jstream, jbyteArray jstorage) {
-        AndroidPicture* picture = reinterpret_cast<AndroidPicture*>(pictureHandle);
-        SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
-
-        if (NULL != strm) {
-            picture->serialize(strm);
-            delete strm;
-            return JNI_TRUE;
-        }
-        return JNI_FALSE;
-    }
-
-    static jint getWidth(JNIEnv* env, jobject jpic) {
-        NPE_CHECK_RETURN_ZERO(env, jpic);
-        AndroidPicture* pict = GraphicsJNI::getNativePicture(env, jpic);
-        int width = pict->width();
-        return static_cast<jint>(width);
-    }
-
-    static jint getHeight(JNIEnv* env, jobject jpic) {
-        NPE_CHECK_RETURN_ZERO(env, jpic);
-        AndroidPicture* pict = GraphicsJNI::getNativePicture(env, jpic);
-        int height = pict->height();
-        return static_cast<jint>(height);
-    }
-
-    static jlong beginRecording(JNIEnv* env, jobject, jlong pictHandle,
-                                jint w, jint h) {
-        AndroidPicture* pict = reinterpret_cast<AndroidPicture*>(pictHandle);
-        // beginRecording does not ref its return value, it just returns it.
-        SkCanvas* canvas = pict->beginRecording(w, h);
-        // the java side will wrap this guy in a Canvas.java, which will call
-        // unref in its finalizer, so we have to ref it here, so that both that
-        // Canvas.java and our picture can both be owners
-        canvas->ref();
-        return reinterpret_cast<jlong>(canvas);
-    }
-
-    static void endRecording(JNIEnv* env, jobject, jlong pictHandle) {
-        AndroidPicture* pict = reinterpret_cast<AndroidPicture*>(pictHandle);
-        pict->endRecording();
-    }
-};
-
-static JNINativeMethod gPictureMethods[] = {
-    {"getWidth", "()I", (void*) SkPictureGlue::getWidth},
-    {"getHeight", "()I", (void*) SkPictureGlue::getHeight},
-    {"nativeConstructor", "(J)J", (void*) SkPictureGlue::newPicture},
-    {"nativeCreateFromStream", "(Ljava/io/InputStream;[B)J", (void*)SkPictureGlue::deserialize},
-    {"nativeBeginRecording", "(JII)J", (void*) SkPictureGlue::beginRecording},
-    {"nativeEndRecording", "(J)V", (void*) SkPictureGlue::endRecording},
-    {"nativeDraw", "(JJ)V", (void*) SkPictureGlue::draw},
-    {"nativeWriteToStream", "(JLjava/io/OutputStream;[B)Z", (void*)SkPictureGlue::serialize},
-    {"nativeDestructor","(J)V", (void*) SkPictureGlue::killPicture}
-};
-
-#include <android_runtime/AndroidRuntime.h>
-
-#define REG(env, name, array) \
-    result = android::AndroidRuntime::registerNativeMethods(env, name, array, \
-    SK_ARRAY_COUNT(array));  \
-    if (result < 0) return result
-
-int register_android_graphics_Picture(JNIEnv* env) {
-    int result;
-
-    REG(env, "android/graphics/Picture", gPictureMethods);
-
-    return result;
 }
 
+SkCanvas* Picture::beginRecording(int width, int height) {
+    mPicture.reset(NULL);
+    mRecorder.reset(new SkPictureRecorder);
+    mWidth = width;
+    mHeight = height;
+    return mRecorder->beginRecording(width, height, NULL, 0);
 }
 
+void Picture::endRecording() {
+    if (NULL != mRecorder.get()) {
+        mPicture.reset(mRecorder->endRecording());
+        mRecorder.reset(NULL);
+    }
+}
 
+int Picture::width() const {
+    if (NULL != mPicture.get()) {
+        SkASSERT(mPicture->width() == mWidth);
+        SkASSERT(mPicture->height() == mHeight);
+    }
+
+    return mWidth;
+}
+
+int Picture::height() const {
+    if (NULL != mPicture.get()) {
+        SkASSERT(mPicture->width() == mWidth);
+        SkASSERT(mPicture->height() == mHeight);
+    }
+
+    return mHeight;
+}
+
+Picture* Picture::CreateFromStream(SkStream* stream) {
+    Picture* newPict = new Picture;
+
+    newPict->mPicture.reset(SkPicture::CreateFromStream(stream));
+    if (NULL != newPict->mPicture.get()) {
+        newPict->mWidth = newPict->mPicture->width();
+        newPict->mHeight = newPict->mPicture->height();
+    }
+
+    return newPict;
+}
+
+void Picture::serialize(SkWStream* stream) const {
+    if (NULL != mRecorder.get()) {
+        SkAutoTDelete<SkPicture> tempPict(this->makePartialCopy());
+        tempPict->serialize(stream);
+    } else if (NULL != mPicture.get()) {
+        mPicture->serialize(stream);
+    } else {
+        SkPicture empty;
+        empty.serialize(stream);
+    }
+}
+
+void Picture::draw(SkCanvas* canvas) {
+    if (NULL != mRecorder.get()) {
+        this->endRecording();
+        SkASSERT(NULL != mPicture.get());
+    }
+    if (NULL != mPicture.get()) {
+        // TODO: remove this const_cast once pictures are immutable
+        const_cast<SkPicture*>(mPicture.get())->draw(canvas);
+    }
+}
+
+SkPicture* Picture::makePartialCopy() const {
+    SkASSERT(NULL != mRecorder.get());
+
+    SkPictureRecorder reRecorder;
+
+    SkCanvas* canvas = reRecorder.beginRecording(mWidth, mHeight, NULL, 0);
+    mRecorder->partialReplay(canvas);
+    return reRecorder.endRecording();
+}
+
+}; // namespace android
diff --git a/core/jni/android/graphics/AndroidPicture.h b/core/jni/android/graphics/Picture.h
similarity index 85%
rename from core/jni/android/graphics/AndroidPicture.h
rename to core/jni/android/graphics/Picture.h
index f434941..abb0403 100644
--- a/core/jni/android/graphics/AndroidPicture.h
+++ b/core/jni/android/graphics/Picture.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_PICTURE_H
-#define ANDROID_PICTURE_H
+#ifndef ANDROID_GRAPHICS_PICTURE_H
+#define ANDROID_GRAPHICS_PICTURE_H
 
 #include "SkPicture.h"
 #include "SkPictureRecorder.h"
@@ -28,13 +28,15 @@
 class SkStream;
 class SkWStream;
 
+namespace android {
+
 // Skia's SkPicture class has been split into an SkPictureRecorder
 // and an SkPicture. AndroidPicture recreates the functionality
 // of the old SkPicture interface by flip-flopping between the two
 // new classes.
-class AndroidPicture {
+class Picture {
 public:
-    explicit AndroidPicture(const AndroidPicture* src = NULL);
+    explicit Picture(const Picture* src = NULL);
 
     SkCanvas* beginRecording(int width, int height);
 
@@ -44,7 +46,7 @@
 
     int height() const;
 
-    static AndroidPicture* CreateFromStream(SkStream* stream);
+    static Picture* CreateFromStream(SkStream* stream);
 
     void serialize(SkWStream* stream) const;
 
@@ -60,4 +62,6 @@
     // resulting picture will have balanced saves and restores.
     SkPicture* makePartialCopy() const;
 };
-#endif // ANDROID_PICTURE_H
+
+}; // namespace android
+#endif // ANDROID_GRAPHICS_PICTURE_H
diff --git a/core/jni/android_graphics_Picture.cpp b/core/jni/android_graphics_Picture.cpp
new file mode 100644
index 0000000..f827907
--- /dev/null
+++ b/core/jni/android_graphics_Picture.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jni.h"
+#include "GraphicsJNI.h"
+#include <android_runtime/AndroidRuntime.h>
+
+#include "Picture.h"
+
+#include "SkCanvas.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "CreateJavaOutputStreamAdaptor.h"
+
+namespace android {
+
+static jlong android_graphics_Picture_newPicture(JNIEnv* env, jobject, jlong srcHandle) {
+    const Picture* src = reinterpret_cast<Picture*>(srcHandle);
+    return reinterpret_cast<jlong>(new Picture(src));
+}
+
+static jlong android_graphics_Picture_deserialize(JNIEnv* env, jobject, jobject jstream,
+                                                  jbyteArray jstorage) {
+    Picture* picture = NULL;
+    SkStream* strm = CreateJavaInputStreamAdaptor(env, jstream, jstorage);
+    if (strm) {
+        picture = Picture::CreateFromStream(strm);
+        delete strm;
+    }
+    return reinterpret_cast<jlong>(picture);
+}
+
+static void android_graphics_Picture_killPicture(JNIEnv* env, jobject, jlong pictureHandle) {
+    Picture* picture = reinterpret_cast<Picture*>(pictureHandle);
+    SkASSERT(picture);
+    delete picture;
+}
+
+static void android_graphics_Picture_draw(JNIEnv* env, jobject, jlong canvasHandle,
+                                          jlong pictureHandle) {
+    SkCanvas* canvas = GraphicsJNI::getNativeCanvas(canvasHandle);
+    Picture* picture = reinterpret_cast<Picture*>(pictureHandle);
+    SkASSERT(canvas);
+    SkASSERT(picture);
+    picture->draw(canvas);
+}
+
+static jboolean android_graphics_Picture_serialize(JNIEnv* env, jobject, jlong pictureHandle,
+                                                   jobject jstream, jbyteArray jstorage) {
+    Picture* picture = reinterpret_cast<Picture*>(pictureHandle);
+    SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
+
+    if (NULL != strm) {
+        picture->serialize(strm);
+        delete strm;
+        return JNI_TRUE;
+    }
+    return JNI_FALSE;
+}
+
+static jint android_graphics_Picture_getWidth(JNIEnv* env, jobject, jlong pictureHandle) {
+    Picture* pict = reinterpret_cast<Picture*>(pictureHandle);
+    return static_cast<jint>(pict->width());
+}
+
+static jint android_graphics_Picture_getHeight(JNIEnv* env, jobject, jlong pictureHandle) {
+    Picture* pict = reinterpret_cast<Picture*>(pictureHandle);
+    return static_cast<jint>(pict->height());
+}
+
+static jlong android_graphics_Picture_beginRecording(JNIEnv* env, jobject, jlong pictHandle,
+                                                     jint w, jint h) {
+    Picture* pict = reinterpret_cast<Picture*>(pictHandle);
+    // beginRecording does not ref its return value, it just returns it.
+    SkCanvas* canvas = pict->beginRecording(w, h);
+    // the java side will wrap this guy in a Canvas.java, which will call
+    // unref in its finalizer, so we have to ref it here, so that both that
+    // Canvas.java and our picture can both be owners
+    canvas->ref();
+    return reinterpret_cast<jlong>(canvas);
+}
+
+static void android_graphics_Picture_endRecording(JNIEnv* env, jobject, jlong pictHandle) {
+    Picture* pict = reinterpret_cast<Picture*>(pictHandle);
+    pict->endRecording();
+}
+
+static JNINativeMethod gMethods[] = {
+    {"nativeGetWidth", "(J)I", (void*) android_graphics_Picture_getWidth},
+    {"nativeGetHeight", "(J)I", (void*) android_graphics_Picture_getHeight},
+    {"nativeConstructor", "(J)J", (void*) android_graphics_Picture_newPicture},
+    {"nativeCreateFromStream", "(Ljava/io/InputStream;[B)J", (void*)android_graphics_Picture_deserialize},
+    {"nativeBeginRecording", "(JII)J", (void*) android_graphics_Picture_beginRecording},
+    {"nativeEndRecording", "(J)V", (void*) android_graphics_Picture_endRecording},
+    {"nativeDraw", "(JJ)V", (void*) android_graphics_Picture_draw},
+    {"nativeWriteToStream", "(JLjava/io/OutputStream;[B)Z", (void*)android_graphics_Picture_serialize},
+    {"nativeDestructor","(J)V", (void*) android_graphics_Picture_killPicture}
+};
+
+int register_android_graphics_Picture(JNIEnv* env) {
+    return AndroidRuntime::registerNativeMethods(env, "android/graphics/Picture", gMethods, NELEM(gMethods));
+}
+
+}; // namespace android
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 4a6e117..de00e59 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -669,8 +669,48 @@
 #endif
 }
 
+#ifdef USE_MINIKIN
+class RenderTextOnPathFunctor {
+public:
+    RenderTextOnPathFunctor(const Layout& layout, OpenGLRenderer* renderer, float hOffset,
+                float vOffset, SkPaint* paint, SkPath* path)
+            : layout(layout), renderer(renderer), hOffset(hOffset), vOffset(vOffset),
+                paint(paint), path(path) {
+    }
+    void operator()(size_t start, size_t end) {
+        uint16_t glyphs[1];
+        for (size_t i = start; i < end; i++) {
+            glyphs[0] = layout.getGlyphId(i);
+            float x = hOffset + layout.getX(i);
+            float y = vOffset + layout.getY(i);
+            renderer->drawTextOnPath((const char*) glyphs, sizeof(glyphs), 1, path, x, y, paint);
+        }
+    }
+private:
+    const Layout& layout;
+    OpenGLRenderer* renderer;
+    float hOffset;
+    float vOffset;
+    SkPaint* paint;
+    SkPath* path;
+};
+#endif
+
 static void renderTextOnPath(OpenGLRenderer* renderer, const jchar* text, int count,
-        SkPath* path, jfloat hOffset, jfloat vOffset, int bidiFlags, SkPaint* paint) {
+        SkPath* path, jfloat hOffset, jfloat vOffset, int bidiFlags, SkPaint* paint,
+        TypefaceImpl* typeface) {
+#ifdef USE_MINIKIN
+    Layout layout;
+    std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface);
+    layout.doLayout(text, 0, count, count, css);
+    hOffset += MinikinUtils::hOffsetForTextAlign(paint, layout, *path);
+    SkPaint::Align align = paint->getTextAlign();
+    paint->setTextAlign(SkPaint::kLeft_Align);
+
+    RenderTextOnPathFunctor f(layout, renderer, hOffset, vOffset, paint, path);
+    MinikinUtils::forFontRun(layout, paint, f);
+    paint->setTextAlign(align);
+#else
     sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint,
             text, 0, count, count, bidiFlags);
     if (value == NULL) {
@@ -681,6 +721,7 @@
     int bytesCount = glyphsCount * sizeof(jchar);
     renderer->drawTextOnPath((const char*) glyphs, bytesCount, glyphsCount, path,
             hOffset, vOffset, paint);
+#endif
 }
 
 static void renderTextRun(OpenGLRenderer* renderer, const jchar* text,
@@ -739,27 +780,31 @@
 
 static void android_view_GLES20Canvas_drawTextArrayOnPath(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jcharArray text, jint index, jint count,
-        jlong pathPtr, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintPtr) {
+        jlong pathPtr, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintPtr,
+        jlong typefacePtr) {
     OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
     jchar* textArray = env->GetCharArrayElements(text, NULL);
     SkPath* path = reinterpret_cast<SkPath*>(pathPtr);
     SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
+    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefacePtr);
 
     renderTextOnPath(renderer, textArray + index, count, path,
-            hOffset, vOffset, bidiFlags, paint);
+            hOffset, vOffset, bidiFlags, paint, typeface);
     env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
 }
 
 static void android_view_GLES20Canvas_drawTextOnPath(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jstring text, jint start, jint end,
-        jlong pathPtr, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintPtr) {
+        jlong pathPtr, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintPtr,
+        jlong typefacePtr) {
     OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
     const jchar* textArray = env->GetStringChars(text, NULL);
     SkPath* path = reinterpret_cast<SkPath*>(pathPtr);
     SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
+    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefacePtr);
 
     renderTextOnPath(renderer, textArray + start, end - start, path,
-            hOffset, vOffset, bidiFlags, paint);
+            hOffset, vOffset, bidiFlags, paint, typeface);
     env->ReleaseStringChars(text, textArray);
 }
 
@@ -986,8 +1031,8 @@
     { "nDrawText",          "(JLjava/lang/String;IIFFIJJ)V",
             (void*) android_view_GLES20Canvas_drawText },
 
-    { "nDrawTextOnPath",    "(J[CIIJFFIJ)V",   (void*) android_view_GLES20Canvas_drawTextArrayOnPath },
-    { "nDrawTextOnPath",    "(JLjava/lang/String;IIJFFIJ)V",
+    { "nDrawTextOnPath",    "(J[CIIJFFIJJ)V",  (void*) android_view_GLES20Canvas_drawTextArrayOnPath },
+    { "nDrawTextOnPath",    "(JLjava/lang/String;IIJFFIJJ)V",
             (void*) android_view_GLES20Canvas_drawTextOnPath },
 
     { "nDrawTextRun",       "(J[CIIIIFFZJJ)V",  (void*) android_view_GLES20Canvas_drawTextRunArray },
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index c0d5221..cfc8eb8 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -61,6 +61,8 @@
     jfieldID xDpi;
     jfieldID yDpi;
     jfieldID secure;
+    jfieldID appVsyncOffsetNanos;
+    jfieldID presentationDeadlineNanos;
 } gPhysicalDisplayInfoClassInfo;
 
 static struct {
@@ -392,6 +394,10 @@
         env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.xDpi, info.xdpi);
         env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.yDpi, info.ydpi);
         env->SetBooleanField(infoObj, gPhysicalDisplayInfoClassInfo.secure, info.secure);
+        env->SetLongField(infoObj, gPhysicalDisplayInfoClassInfo.appVsyncOffsetNanos,
+                info.appVsyncOffset);
+        env->SetLongField(infoObj, gPhysicalDisplayInfoClassInfo.presentationDeadlineNanos,
+                info.presentationDeadline);
         env->SetObjectArrayElement(configArray, static_cast<jsize>(c), infoObj);
         env->DeleteLocalRef(infoObj);
     }
@@ -648,6 +654,10 @@
     gPhysicalDisplayInfoClassInfo.xDpi = env->GetFieldID(clazz, "xDpi", "F");
     gPhysicalDisplayInfoClassInfo.yDpi = env->GetFieldID(clazz, "yDpi", "F");
     gPhysicalDisplayInfoClassInfo.secure = env->GetFieldID(clazz, "secure", "Z");
+    gPhysicalDisplayInfoClassInfo.appVsyncOffsetNanos = env->GetFieldID(clazz,
+            "appVsyncOffsetNanos", "J");
+    gPhysicalDisplayInfoClassInfo.presentationDeadlineNanos = env->GetFieldID(clazz,
+            "presentationDeadlineNanos", "J");
 
     jclass rectClazz = env->FindClass("android/graphics/Rect");
     gRectClassInfo.bottom = env->GetFieldID(rectClazz, "bottom", "I");
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 815c4a7..2b94b65 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -49,6 +49,14 @@
 
 static jmethodID gRunnableMethod;
 
+static JNIEnv* getenv(JavaVM* vm) {
+    JNIEnv* env;
+    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+        LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", vm);
+    }
+    return env;
+}
+
 class JavaTask : public RenderTask {
 public:
     JavaTask(JNIEnv* env, jobject jrunnable) {
@@ -57,20 +65,13 @@
     }
 
     virtual void run() {
-        env()->CallVoidMethod(mRunnable, gRunnableMethod);
-        env()->DeleteGlobalRef(mRunnable);
+        JNIEnv* env = getenv(mVm);
+        env->CallVoidMethod(mRunnable, gRunnableMethod);
+        env->DeleteGlobalRef(mRunnable);
         delete this;
     };
 
 private:
-    JNIEnv* env() {
-        JNIEnv* env;
-        if (mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
-            return 0;
-        }
-        return env;
-    }
-
     JavaVM* mVm;
     jobject mRunnable;
 };
@@ -122,12 +123,34 @@
     std::vector<OnFinishedEvent> mOnFinishedEvents;
 };
 
-class RootRenderNode : public RenderNode, public AnimationHook {
+class RenderingException : public MessageHandler {
 public:
-    RootRenderNode() : RenderNode() {
+    RenderingException(JavaVM* vm, const std::string& message)
+            : mVm(vm)
+            , mMessage(message) {
+    }
+
+    virtual void handleMessage(const Message&) {
+        throwException(mVm, mMessage);
+    }
+
+    static void throwException(JavaVM* vm, const std::string& message) {
+        JNIEnv* env = getenv(vm);
+        jniThrowException(env, "java/lang/IllegalStateException", message.c_str());
+    }
+
+private:
+    JavaVM* mVm;
+    std::string mMessage;
+};
+
+class RootRenderNode : public RenderNode, AnimationHook, ErrorHandler {
+public:
+    RootRenderNode(JNIEnv* env) : RenderNode() {
         mLooper = Looper::getForThread();
         LOG_ALWAYS_FATAL_IF(!mLooper.get(),
                 "Must create RootRenderNode on a thread with a looper!");
+        env->GetJavaVM(&mVm);
     }
 
     virtual ~RootRenderNode() {}
@@ -137,10 +160,16 @@
         mOnFinishedEvents.push_back(event);
     }
 
+    virtual void onError(const std::string& message) {
+        mLooper->sendMessage(new RenderingException(mVm, message), 0);
+    }
+
     virtual void prepareTree(TreeInfo& info) {
         info.animationHook = this;
+        info.errorHandler = this;
         RenderNode::prepareTree(info);
         info.animationHook = NULL;
+        info.errorHandler = NULL;
 
         // post all the finished stuff
         if (mOnFinishedEvents.size()) {
@@ -160,6 +189,7 @@
 private:
     sp<Looper> mLooper;
     std::vector<OnFinishedEvent> mOnFinishedEvents;
+    JavaVM* mVm;
 };
 
 static void android_view_ThreadedRenderer_setAtlas(JNIEnv* env, jobject clazz,
@@ -178,7 +208,7 @@
 }
 
 static jlong android_view_ThreadedRenderer_createRootRenderNode(JNIEnv* env, jobject clazz) {
-    RootRenderNode* node = new RootRenderNode();
+    RootRenderNode* node = new RootRenderNode(env);
     node->incStrong(0);
     node->setName("RootRenderNode");
     return reinterpret_cast<jlong>(node);
diff --git a/core/res/res/drawable-hdpi/ic_corp_badge.png b/core/res/res/drawable-hdpi/ic_corp_badge.png
new file mode 100644
index 0000000..c79ce92
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_corp_badge.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_corp_icon_badge.png b/core/res/res/drawable-hdpi/ic_corp_icon_badge.png
new file mode 100644
index 0000000..0059e09
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_corp_icon_badge.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_corp_badge.png b/core/res/res/drawable-mdpi/ic_corp_badge.png
new file mode 100644
index 0000000..c1447fe
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_corp_badge.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_corp_icon_badge.png b/core/res/res/drawable-mdpi/ic_corp_icon_badge.png
new file mode 100644
index 0000000..5ff8c5d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_corp_icon_badge.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_corp_badge.png b/core/res/res/drawable-xhdpi/ic_corp_badge.png
new file mode 100644
index 0000000..2d3d748
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_corp_badge.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_corp_icon_badge.png b/core/res/res/drawable-xhdpi/ic_corp_icon_badge.png
new file mode 100644
index 0000000..dc5716d
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_corp_icon_badge.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_corp_badge.png b/core/res/res/drawable-xxhdpi/ic_corp_badge.png
new file mode 100644
index 0000000..430e63b
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_corp_badge.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_corp_icon_badge.png b/core/res/res/drawable-xxhdpi/ic_corp_icon_badge.png
new file mode 100644
index 0000000..cc00dd8
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_corp_icon_badge.png
Binary files differ
diff --git a/core/res/res/drawable/ic_corp_badge.xml b/core/res/res/drawable/ic_corp_badge.xml
deleted file mode 100644
index 5325712..0000000
--- a/core/res/res/drawable/ic_corp_badge.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<!--
-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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
-    <size
-        android:width="19.0dp"
-        android:height="19.0dp"/>
-
-    <viewport
-        android:viewportWidth="19.0"
-        android:viewportHeight="19.0"/>
-
-    <path
-        android:pathData="M9.5,9.5m-9.5,0.0a9.5,9.5 0.0,1.0 1.0,19.0 0.0a9.5,9.5 0.0,1.0 1.0,-19.0 0.0"
-        android:fill="#FF5722"/>
-    <path
-        android:pathData="M12.667,7.125l-1.583,0.0L11.084,6.333l-0.792,-0.792L8.708,5.5410004L7.917,6.333l0.0,0.792L6.333,7.125c-0.438,0.0 -0.788,0.354 -0.788,0.792l-0.004,4.354c0.0,0.438 0.354,0.792 0.792,0.792l6.333,0.0c0.438,0.0 0.792,-0.354 0.792,-0.792L13.458,7.917C13.458,7.479 13.104,7.125 12.667,7.125zM10.094,10.687L8.906,10.687L8.906,9.5l1.188,0.0L10.094,10.687zM10.292,7.125L8.708,7.125L8.708,6.333l1.583,0.0L10.291,7.125z"
-        android:fill="#FFFFFF"/>
-    <path
-        android:pathData="M4.75,4.75 h9.5 v9.5 h-9.5z"
-        android:fill="#00000000"/>
-</vector>
diff --git a/core/res/res/drawable/ic_corp_icon_badge.xml b/core/res/res/drawable/ic_corp_icon_badge.xml
deleted file mode 100644
index 7bfab4c..0000000
--- a/core/res/res/drawable/ic_corp_icon_badge.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<!--
-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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
-    <size
-        android:width="64.0dp"
-        android:height="64.0dp"/>
-
-    <viewport
-        android:viewportWidth="64.0"
-        android:viewportHeight="64.0"/>
-
-    <path
-        android:fill="#FF000000"
-        android:pathData="M49.062,50.0m-14.0,0.0a14.0,14.0 0.0,1.0 1.0,28.0 0.0a14.0,14.0 0.0,1.0 1.0,-28.0 0.0"/>
-    <path
-        android:fill="#FF000000"
-        android:pathData="M49.0,49.5m-14.0,0.0a14.0,14.0 0.0,1.0 1.0,28.0 0.0a14.0,14.0 0.0,1.0 1.0,-28.0 0.0"/>
-    <path
-        android:pathData="M49.0,49.0m-14.0,0.0a14.0,14.0 0.0,1.0 1.0,28.0 0.0a14.0,14.0 0.0,1.0 1.0,-28.0 0.0"
-        android:fill="#FF5722"/>
-    <path
-        android:pathData="M53.667,45.5l-2.333,0.0l0.0,-1.167l-1.167,-1.167l-2.333,0.0l-1.167,1.167L46.667,45.5l-2.333,0.0c-0.645,0.0 -1.161,0.522 -1.161,1.167l-0.006,6.417c0.0,0.645 0.522,1.167 1.167,1.167l9.333,0.0c0.645,0.0 1.167,-0.522 1.167,-1.167l0.0,-6.417C54.833,46.022 54.311,45.5 53.667,45.5zM49.875,50.75l-1.75,0.0L48.125,49.0l1.75,0.0L49.875,50.75zM50.167,45.5l-2.333,0.0l0.0,-1.167l2.333,0.0L50.167,45.5z"
-        android:fill="#FFFFFF"/>
-    <path
-        android:pathData="M42.0,42.0 h14.0 v14.0 h-14.0z"
-        android:fill="#00000000"/>
-</vector>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 48b9311..4e06d9a 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -881,17 +881,33 @@
     <!-- The name of the logical parent of the activity as it appears in the manifest. -->
     <attr name="parentActivityName" format="string" />
 
-    <!-- Define an activity that will persist across reboots. If such an activity is in the
-         Recents list when the device is shut off it will appear in the Recents list when
-         the device is next powered on. To be persisted all activities in the task from the
-         root activity up to the last activity before a <em>break</em> must be declared with
-         the persistable attribute. A <em>break</em> is the first activity after the root
-         started with Intent.FLAG_CLEAR_TASK_WHEN_RESET.
+    <!-- Define how an activity persist across reboots. Activities defined as "never" will not
+         be persisted. Those defined as "always" will be persisted. Those defined as "taskOnly"
+         will persist the root activity of the task only. See below for more detail as to
+         what gets persisted. -->
+    <attr name="persistableMode">
+        <!-- The default. If this activity forms the root of a task then that task will be
+             persisted across reboots but only the launching intent will be used. All
+             activities above this activity in the task will not be persisted. In addition
+             this activity will not be passed a PersistableBundle into which it could have
+             stored its state. -->
+        <enum name="persistRootOnly" value="0" />
+        <!-- If this activity forms the root of a task then that task will not be persisted
+             across reboots -->
+        <enum name="doNotPersist" value="1" />
+        <!-- If this activity forms the root of a task then the task and this activity will
+             be persisted across reboots. If the activity above this activity is also
+             tagged with the attribute <code>"persist"</code> then it will be persisted as well.
+             And so on up the task stack until either an activity without the
+             <code>persistableMode="persistAcrossReboots"</code> attribute or one that was launched
+             with the flag Intent.FLAG_CLEAR_TASK_WHEN_RESET is encountered.
 
-         <p>Activities that are declared with the persistable attribute will be provided with a
-         forced-persistable Bundle in onCreate() and onSavedInstanceState(), and must only
-         be passed a persistable Bundle in their Intent.extras. -->
-    <attr name="persistable" format="boolean" />
+             <p>Activities that are declared with the persistAcrossReboots attribute will be
+             provided with a PersistableBundle in onSavedInstanceState(), These activities may
+             use this PeristableBundle to save their state. Then, following a reboot, that
+             PersistableBundle will be provided back to the activity in its onCreate() method. -->
+        <enum name="persistAcrossReboots" value="2" />
+    </attr>
 
     <!-- This attribute specifies that an activity shall become the root activity of a
          new task each time it is launched. Using this attribute permits the user to
@@ -1632,7 +1648,7 @@
         <!-- @hide This broacast receiver will only receive broadcasts for the
              primary user.  Can only be used with receivers. -->
         <attr name="primaryUserOnly" format="boolean" />
-        <attr name="persistable" />
+        <attr name="persistableMode" />
         <attr name="allowEmbedded" />
         <attr name="documentLaunchMode" />
         <attr name="maxRecents" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index a4f78bd..471eece 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1464,7 +1464,7 @@
 
     <!-- Name of the CustomDialog that is used for VPN -->
     <string name="config_customVpnConfirmDialogComponent"
-            >com.android.vpndialogs/com.android.vpndialogs.CustomDialog</string>
+            >com.android.vpndialogs/com.android.vpndialogs.ConfirmDialog</string>
 
     <!-- Apps that are authorized to access shared accounts, overridden by product overlays -->
     <string name="config_appsAuthorizedForSharedAccounts">;com.android.settings;</string>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 1bdb3cf..f6ad78b 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2136,7 +2136,7 @@
   <public type="attr" name="colorControlActivated" />
   <public type="attr" name="colorButtonNormal" />
   <public type="attr" name="colorControlHighlight" />
-  <public type="attr" name="persistable" />
+  <public type="attr" name="persistableMode" />
   <public type="attr" name="titleTextAppearance" />
   <public type="attr" name="subtitleTextAppearance" />
   <public type="attr" name="slideEdge" />
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index 7251e7c..7f41ac1c 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -305,11 +305,9 @@
 
     private PackageParser.Package parsePackage(Uri packageURI) throws PackageParserException {
         final String archiveFilePath = packageURI.getPath();
-        PackageParser packageParser = new PackageParser(archiveFilePath);
+        PackageParser packageParser = new PackageParser();
         File sourceFile = new File(archiveFilePath);
-        DisplayMetrics metrics = new DisplayMetrics();
-        metrics.setToDefaults();
-        PackageParser.Package pkg = packageParser.parseMonolithicPackage(sourceFile, metrics, 0);
+        PackageParser.Package pkg = packageParser.parseMonolithicPackage(sourceFile, 0);
         packageParser = null;
         return pkg;
     }
diff --git a/data/fonts/fallback_fonts.xml b/data/fonts/fallback_fonts.xml
index 1eaae65..37e9b157 100644
--- a/data/fonts/fallback_fonts.xml
+++ b/data/fonts/fallback_fonts.xml
@@ -219,6 +219,21 @@
     </family>
     <family>
         <fileset>
+            <file>NotoSansCherokee-Regular.ttf</file>
+        </fileset>
+    </family>
+    <family>
+        <fileset>
+            <file>NotoSansCanadianAboriginal-Regular.ttf</file>
+        </fileset>
+    </family>
+    <family>
+        <fileset>
+            <file>NotoSansYi-Regular.ttf</file>
+        </fileset>
+    </family>
+    <family>
+        <fileset>
             <file lang="zh-CN">NotoSansHans-Regular.otf</file>
         </fileset>
     </family>
diff --git a/docs/html/google/play-services/games.jd b/docs/html/google/play-services/games.jd
index 94f6715..a73f688 100644
--- a/docs/html/google/play-services/games.jd
+++ b/docs/html/google/play-services/games.jd
@@ -1,4 +1,5 @@
 page.title=Google Play Game Services
+page.tags="games"
 header.hide=1
 
 @jd:body
diff --git a/docs/html/guide/topics/manifest/activity-element.jd b/docs/html/guide/topics/manifest/activity-element.jd
index b648d48..c4d5083 100644
--- a/docs/html/guide/topics/manifest/activity-element.jd
+++ b/docs/html/guide/topics/manifest/activity-element.jd
@@ -5,7 +5,8 @@
 
 <dl class="xml">
 <dt>syntax:</dt>
-<dd><pre class="stx">&lt;activity android:<a href="#reparent">allowTaskReparenting</a>=["true" | "false"]
+<dd><pre class="stx">&lt;activity android:<a href="#embedded">allowEmbedded</a>=["true" | "false"]
+          android:<a href="#reparent">allowTaskReparenting</a>=["true" | "false"]
           android:<a href="#always">alwaysRetainTaskState</a>=["true" | "false"]
           android:<a href="#clear">clearTaskOnLaunch</a>=["true" | "false"]
           android:<a href="#config">configChanges</a>=["mcc", "mnc", "locale",
@@ -62,6 +63,17 @@
 
 <dt>attributes:</dt>
 <dd><dl class="attr">
+<dt><a name="embedded"></a>{@code android:allowEmbedded}</dt>
+<dd>
+    Indicate that the activity can be launched as the embedded child of another
+    activity. Particularly in the case where the child lives in a container
+    such as a Display owned by another activity. For example, activities
+    that are used for Wear custom notifications must declare this so
+    Wear can display the activity in it's context stream, which resides
+    in another process.
+
+    <p>The default value of this attribute is <code>false</code>.
+</dd>
 <dt><a name="reparent"></a>{@code android:allowTaskReparenting}</dt>
 <dd>Whether or not the activity can move from the task that started it to
 the task it has an affinity for when that task is next brought to the
diff --git a/docs/html/preview/api-overview.jd b/docs/html/preview/api-overview.jd
index 40618a3..2fa029f 100644
--- a/docs/html/preview/api-overview.jd
+++ b/docs/html/preview/api-overview.jd
@@ -15,7 +15,9 @@
 <ol id="toc44" class="hide-nested">
   <li><a href="#Behaviors">Important Behavior Changes</a>
     <ol>
+      <li><a href="#ART">New Android Runtime (ART)</a></li>
       <li><a href="#BehaviorNotifications">If your app implements notifications...</a></li>
+      <li><a href="#BehaviorMediaControl">If your app uses RemoteControlClient...</a></li>
       <li><a href="#BehaviorFullscreen">If your app uses fullScreenIntent...</a></li>
       <li><a href="#BehaviorGetRecentTasks">If your app uses ActivityManager.getRecentTasks()...</a></li>
     </ol>
@@ -23,9 +25,10 @@
   <li><a href="#UI">User Interface</a>
     <ol>
       <li><a href="#MaterialDesign">Material design support</a></li>
+      <li><a href="#DoNotDisturb">Do Not Disturb mode</a></li>
       <li><a href="#LockscreenNotifications">Lockscreen notifications</a></li>
       <li><a href="#NotificationsMetadata">Notifications metadata</a></li>
-      <li><a href="#Recents">Concurrent documents and activities in Recents screen</a></li>
+      <li><a href="#Recents">Concurrent documents and activities in the Recents screen</a></li>
       <li><a href="#WebView">WebView updates</a></li>
     </ol>
   </li>
@@ -41,7 +44,7 @@
   </li>
   <li><a href="#Multimedia">Multimedia</a>
     <ol>
-      <li><a href="#Camera-v2">Camera V2</a></li>
+      <li><a href="#Camera-v2">Camera v2 API</a></li>
       <li><a href="#AudioPlayback">Audio playback</a></li>
       <li><a href="#MediaPlaybackControl">Media playback control</a></li>
     </ol>
@@ -55,7 +58,7 @@
     <ol>
       <li><a href="#Multinetwork">Dynamic network selection and seamless handoff</a></li>
       <li><a href="#BluetoothBroadcasting">Bluetooth broadcasting</a></li>
-      <li><a href="#NFCEnhancements">NFC enhancements for payments</a></li>
+      <li><a href="#NFCEnhancements">NFC enhancements</a></li>
     </ol>
   </li>
   <li><a href="#Power">Power Efficiency</a>
@@ -71,7 +74,7 @@
   </li>
   <li><a href="#Printing">Printing Framework</a>
     <ol>
-      <li><a href="#PDFRender">PDF rendering</a></li>
+      <li><a href="#PDFRender">Render PDF as bitmap</a></li>
     </ol>
   </li>
   <li><a href="#TestingA11y">Testing &amp; Accessibility</a>
@@ -96,73 +99,130 @@
 </div>
 </div>
 
-<p>L is an upcoming release for the Android platform
-that offers new features for users and app developers. This document provides
-an introduction to the most notable new APIs.</p>
+<p>The L Developer Preview gives you an advance look at the upcoming release for
+the Android platform,
+which offers new features for users and app developers. This document provides
+an introduction to the most notable APIs.</p>
 
-<p>L is currently available as a <strong>developer preview</strong> intended
-for early adopters and testers. If you are interested in influencing the
-direction of the Android framework,
-<a href="{@docRoot}preview/setup-sdk.html">give the L Developer Preview a
-try</a> and send us your feedback!</p>
+<p>The L Developer Preview is intended for <strong>developer early adopters</strong> and
+<strong>testers</strong>. If you are interested in influencing the direction of the
+Android framework, <a href="{@docRoot}preview/setup-sdk.html">give the L
+Developer Preview a try</a> and send us your feedback!</p>
 
-<p class="caution"><strong>Caution:</strong>You should not publish apps
-using L Developer Preview to the Google Play store.</p>
+<p class="caution"><strong>Caution:</strong> Do not not publish apps
+that use the L Developer Preview to the Google Play store.</p>
+
+<p class="note"><strong>Note:</strong> This document often refers to classes and
+methods that do not yet have reference material available on <a
+href="{@docRoot}">developer.android.com</a>. These API elements are
+formatted in {@code code style} in this document (without hyperlinks). For the
+preliminary API documentation for these elements, download the <a
+href="{@docRoot}preview/l-developer-preview-reference.zip">preview
+reference</a>.</p>
 
 <h2 id="Behaviors">Important Behavior Changes</h2>
 
 <p>If you have previously published an app for Android, be aware that your app
-  might be affected by changes in L.</p>
+  might be affected by changes in the upcoming release.</p>
+
+<h3 id="ART">New Android Runtime (ART)</h3>
+
+<p>The 4.4 release introduced a new, experimental Android runtime, ART. Under
+4.4, ART was optional, and the default runtime remained Dalvik. With the L Developer Preview, ART is
+now the default runtime.</p>
+
+<p>For an overview of ART's new features, see
+<a href="https://source.android.com/devices/tech/dalvik/art.html">Introducing
+ART</a>. Some of the major new features are:</p>
+
+<ul>
+  <li>Ahead-of-Time (AOT) compilation</li>
+  <li>Improved garbage collection (GC)</li>
+  <li>Improved debugging support</li>
+</ul>
+
+<p>Most Android apps should just work without change under ART. However, some
+techniques that work on Dalvik do not work on ART. For information about the
+most important issues, see
+<a href="{@docRoot}guide/practices/verifying-apps-art.html">Verifying App
+Behavior on the Android Runtime (ART)</a>. Pay particular attention if:</p>
+
+<ul>
+  <li>Your app uses Java Native Interface (JNI) to run C/C++ code.</li>
+  <li>You use development tools that generate non-standard code (such as some
+      obfuscators).</li>
+  <li>You use techniques that are incompatible with compacting garbage
+      collection. (ART does not currently implement compacting GC, but
+      compacting GC is under development in the Android Open-Source
+      Project.)</li>
+</ul>
 
 <h3 id="BehaviorNotifications">If your app implements notifications...</h3>
 
-<p>Notifications will be drawn with dark text atop white (or very light)
+<p>Notifications are drawn with dark text atop white (or very light)
 backgrounds to match the new material design widgets. Make sure that all your
-notifications look right with the new color scheme. You should remove or update
-assets and text styles that involve color. The system will automatically invert
-action icons in notifications. Use
-{@code android.app.Notification.Builder.setColor()} to set an accent color
-in a circle behind your {@code Notification.icon} image.</p>
+notifications look right with the new color scheme:</p>
 
-<p>The system will ignore all non-alpha channels in action icons and the main
-notification icon, so you should assume that these icons will be alpha-only.
-</p>
+<ul>
+
+  <li>Update or remove assets that involve color.</li>
+
+  <li>The system automatically inverts action icons in notifications. Use
+  {@code android.app.Notification.Builder.setColor()} to set an accent color
+  in a circle behind your {@link android.app.Notification#icon} image.</li>
+
+  <li>The system ignores all non-alpha channels in action icons and the main
+  notification icon. You should assume that these icons are alpha-only.</li>
+
+</ul>
 
 <p>If you are currently adding sounds and vibrations to your notifications by
 using the {@link android.media.Ringtone}, {@link android.media.MediaPlayer},
-or {@link android.os.Vibrator} classes, make sure to remove this code so that
-the system can present notifications correctly in Do not disturb mode. You
-should use the {@link android.app.Notification.Builder} methods instead to add
-sounds and vibration.
-</p>
+or {@link android.os.Vibrator} classes, remove this code so that
+the system can present notifications correctly in <a href="#DoNotDisturb">Do Not Disturb</a> mode.
+Instead, use the {@link android.app.Notification.Builder} methods instead to add
+sounds and vibration.</p>
 
 <h3 id="BehaviorMediaControl">If your app uses RemoteControlClient...</h3>
 
-<p>Lockscreens in L will not show transport controls for your
+<p>Lockscreens in the L Developer Preview do not show transport controls for your
 {@link android.media.RemoteControlClient}. Instead, your app can provide
 media playback control from the lockscreen through a media notification. This
 gives your app more control over the presentation of media buttons, while
 providing a consistent experience for users across the lockscreen and
 unlocked device.</p>
 
-<p>You must call {@code Notification.Builder.setVisibility(Notification.VISIBILITY_PUBLIC)} to mark your media notification as safe to reveal, even when the lockscreen is secured
-with a PIN, pattern, or password.</p>
+<p>Call {@code
+Notification.Builder.setVisibility(Notification.VISIBILITY_PUBLIC)} to mark a
+notification as safe to display on the lockscreen (even when the lockscreen is
+secured with a PIN, pattern, or password). For more information, see
+<a href="#LockscreenNotifications">Lockscreen Notifications</a>.</p>
 
 <h3 id="BehaviorFullscreen">If your app uses fullScreenIntent...</h3>
 
 <p>Notifications now appear in a small floating window if all these conditions
-are met: the user’s activity is in fullscreen mode, the screen is on, and the
-device is unlocked. If your app implements fullscreen activities, make sure that
+are met:</p>
+
+<ul>
+  <li>The user’s activity is in fullscreen mode,</li>
+  <li>The screen is on, and</li>
+  <li>The device is unlocked</li>
+</ul>
+
+<p>If your app implements fullscreen activities, make sure that
 these heads-up notifications are presented correctly.</p>
 
 <h3 id="BehaviorGetRecentTasks">If your app uses ActivityManager.getRecentTasks()...</h3>
 
-<p>With the introduction of the new document tasks feature in L (see below),
-the {@code android.app.ActivityManager.getRecentTasks()} method is now
-deprecated to improve user privacy. For backwards
-compatibility, it will still return a small subset of its data including the
+<p>With the introduction of the new <em>concurrent documents and activities tasks</em> feature in the upcoming
+release (see <a href="#Recents">Concurrent documents and activities in Recents
+screen</a> below),
+the {@link android.app.ActivityManager#getRecentTasks
+ActivityManager.getRecentTasks()} method is now
+deprecated to improve user privacy. For backward
+compatibility, this method still returns a small subset of its data, including the
 calling application’s own tasks and possibly some other non-sensitive tasks
-such as home. If your app is using this method to retrieve its own tasks,
+(such as Home). If your app is using this method to retrieve its own tasks,
 use {@code android.app.ActivityManager.getAppTasks()} instead to retrieve that
 information.</p>
 
@@ -170,11 +230,15 @@
 
 <h3 id="MaterialDesign">Material design support</h3>
 
-<p>The L Developer Preview adds support for the material design style. You can create
-material design apps that are visually dynamic and have UI element transitions
-which feel natural and delightful to users. This support includes:</p>
+
+<p>The upcoming release adds support for Android's new <em>material</em> design
+style. You can create
+apps with material design that are visually dynamic and have UI element transitions
+that feel natural to users. This support includes:</p>
+
 <ul>
-  <li>The Material theme</li>
+
+  <li>The material theme</li>
   <li>View shadows</li>
   <li>The {@code RecyclerView} widget</li>
   <li>Drawable animation and styling effects</li>
@@ -182,8 +246,9 @@
   <li>Animators for view properties based on the state of a view</li>
   <li>Customizable UI widgets and app bars with color palettes that you control</li>
 </ul>
+
 <p>To learn more about adding material design functionality to your app, see
-<a href="{@docRoot}preview/material/index.html">Material design on Android</a>.</p>
+<a href="{@docRoot}preview/material/index.html">Material Design</a>.</p>
 
 <h3 id="LockscreenNotifications">Lockscreen notifications</h3>
 <p>Lockscreens in the L Developer Preview have the ability to present notifications.
@@ -194,29 +259,57 @@
 displayed over the secure lockscreen. To control the visibility level, call
 {@code android.app.Notification.Builder.setVisibility()} and specify one of these
 values:</p>
+
 <ul>
 <li>{@code VISIBILITY_PRIVATE}. Shows basic information, such as the
 notification’s icon, but hides the notification’s full content. If you want to
 provide a redacted public version of your notification for the system to display
-on a secure lockscreen, set the public notification object in the <code>publicVersion</code>
-field.</li>
+on a secure lockscreen, create a public notification object and put a reference
+to it in the private notification's {@code publicVersion} field.</li>
 <li>{@code VISIBILITY_PUBLIC}. Shows the notification’s full content. This is
   the system default if visibility is left unspecified.</li>
 <li>{@code VISIBILITY_SECRET}. Shows only the most minimal information,
 excluding even the notification’s icon.</li>
 </ul>
 
-<h3 id="NotificationsMetadata">Notifications metadata</h3>
-<p>The L Developer Preview uses metadata associated with your app notifications
-to more intelligently sort your notifications. The metadata you set also
-controls how the system presents your app notifications when the user is in <em>Do
-not disturb</em> mode. When constructing your notification, you can call the
-following methods in {@code android.app.Notification.Builder}:</p>
+<h3 id="DoNotDisturb">Do Not Disturb mode</h3>
+
+<p>The L Developer Preview introduces a new <em>Do Not Disturb</em> mode. When
+the user puts the device in <em>Do Not Disturb</em> mode, the device limits
+the frequency of the notifications it shows the user (when the user
+wants to avoid distractions). The user can
+customize the feature in a number of ways, such as:</p>
 
 <ul>
-<li>{@code setCategory()}. Allows the system to handle your app notifications
-in <em>Do not disturb mode</em> (for example, if your notification represents an
-incoming call, instant message, or alarm).</li>
+  <li>Specifying important people, whose calls should go through even when
+    the device is in <em>Do Not Disturb</em> mode.</li>
+  <li>Setting custom categories to allow notifications when the device is in
+    <em>Do Not Disturb</em> mode. Examples of such categories include phone
+    calls and direct communications (like Hangouts and Skype calls).</li>
+  <li>Setting rules so <em>Do Not Disturb</em> automatically goes into effect in
+    certain conditions (like at particular times of day).</li>
+</ul>
+
+<p>You should add the appropriate metadata to your app notifications to help
+make sure <em>Do Not Disturb</em> mode handles them properly. For example, if
+your app is an alarm clock,
+you can tag the notification as an alarm so it will wake the user up even if the
+device is in <em>Do Not Disturb</em> mode. For more information, see <a
+href="NotificationsMetadata">Notifications metadata</a>.</p>
+
+<h3 id="NotificationsMetadata">Notifications metadata</h3>
+<p>The L Developer Preview uses metadata associated with your app notifications
+to sort the notifications more intelligently. The metadata you set also
+controls how the system presents your app notifications when the user is in <em>Do
+Not Disturb</em> mode. To set the metadata, call the following methods in
+{@code android.app.Notification.Builder} when you construct the
+notification:</p>
+
+<ul>
+<li>{@code setCategory()}. Depending on the message category, this tells
+the system how to handle your app notifications when the device is
+in <em>Do Not Disturb</em> mode (for example, if your notification represents an
+incoming call, instant message, or alarm).
 <li>{@code setPriority()}. Notifications with the priority field set to
 {@code PRIORITY_MAX} or {@code PRIORITY_HIGH} will appear in a small floating
 window if the notification also has sound or vibration.</li>
@@ -231,30 +324,35 @@
 <p>In previous releases, the
 <a href="{@docRoot}design/get-started/ui-overview.html">Recents screen</a>
 could only display a single task for each app that the user interacted with
-most recently. The L Developer Preview allows your app to open additional tasks
-for concurrent activities or documents. This feature facilitates multitasking
+most recently. The L Developer Preview enables your app to open more tasks as
+needed for additional concurrent activities for documents.
+This feature facilitates multitasking
 by letting users quickly switch between individual activities and documents
-from the Recents screen. Examples of such concurrent tasks might include web
-pages in a browser app, documents in a productivity app, concurrent matches in
+from the Recents screen, with a consistent switching experience across all apps.
+Examples of such concurrent tasks might include open tabs in a web
+browser app, documents in a productivity app, concurrent matches in
 a game, or chats in a messaging app. Your app can manage its tasks
 through the {@code android.app.ActivityManager.AppTask} class.</p>
 
 <p>To insert a logical break so that the system treats your activity as a new
-document, use {@code android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT} when
+task, use {@code android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT} when
 launching the activity with {@link android.app.Activity#startActivity(android.content.Intent) startActivity()}. You can also get this behavior by declaring the
 <a href="{@docRoot}guide/topics/manifest/activity-element.html">&lt;activity&gt;</a>
 attribute {@code documentLaunchMode="intoExisting"} or {@code ="always"} in your
 manifest.</p>
 
 <p>You can also mark that a task should be removed from the Recents screen
-when all its activities are closed by using {@code android.content.Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS} when starting the root activity for
+when all its activities are closed. To do this, use {@code
+android.content.Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS} when starting the
+root activity for
 the task. You can also set this behavior for an activity by declaring the
 <a href="{@docRoot}guide/topics/manifest/activity-element.html">&lt;activity&gt;</a>
 attribute {@code autoRemoveFromRecents=“true”} in your manifest.</p>
 
 <p>To avoid cluttering the Recents screen, you can set the maximum number of
-tasks from your app that can appear in the Recents screen through the
-<a href="{@docRoot}guide/topics/manifest/application-element.html">&lt;application&gt;</a> attribute {@code android:maxRecent}. The current maximum that can be specified
+tasks from your app that can appear in that screen. To do this, set the
+<a href="{@docRoot}guide/topics/manifest/application-element.html">&lt;application&gt;</a>
+attribute {@code android:maxRecent}. The current maximum that can be specified
 is 100 tasks per user.</a></p>
 
 <h3 id="WebView">WebView updates</h3>
@@ -274,16 +372,20 @@
 <h3 id="IME">IME bug fixes and improvements</h3>
 
 <p>Beginning in the L Developer Preview, users can more easily switch between
-all input method editors (IME) <a href="{@docRoot}guide/topics/text/creating-input-method.html">supported by the platform</a>. Performing the designated
+all <a href="{@docRoot}guide/topics/text/creating-input-method.html">input
+method editors (IME)</a> supported by the platform. Performing the designated
 switching action (usually touching a Globe icon on the soft keyboard) will cycle
 among all such IMEs. This change takes place in
-{@code android.view.inputmethod.InputMethodManager.shouldOfferSwitchingToNextInputMethod()}.</p>
+{@link android.view.inputmethod.InputMethodManager#shouldOfferSwitchingToNextInputMethod
+InputMethodManager.shouldOfferSwitchingToNextInputMethod()}.</p>
 
-<p>In addition, the framework will now check whether the next IME includes a
-switching mechanism at all, thus supporting switching to the IME after it. An
+<p>In addition, the framework now checks whether the next IME includes a
+switching mechanism at all (and, thus, whether that IME supports switching to
+the IME after it). An
 IME with a switching mechanism will not cycle to an IME without one. This
 change takes place in
-{@code android.view.inputmethod.InputMethodManager.switchToNextInputMethod()}.
+{@link android.view.inputmethod.InputMethodManager#switchToNextInputMethod
+InputMethodManager.switchToNextInputMethod}.
 
 <p>To see an example of how to use the updated IME-switching APIs, refer to the
 updated soft-keyboard implementation sample in this release.</p>
@@ -314,17 +416,20 @@
 &lt;/manifest&gt;
 </pre>
 
-<p>For more information about using OpenGL ES, including how to check the device’s supported OpenGL ES version at runtime, see the <a href="{@docRoot}/guide/topics/graphics/opengl.html">OpenGL ES API guide</a>.</p>
+<p>For more information about using OpenGL ES, including how to check the device’s supported OpenGL ES version at runtime, see the <a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL ES API guide</a>.</p>
 
 <h2 id="Multimedia">Multimedia</h2>
 
-<h3 id="Camera=v2">Camera v2 API</h3>
+<h3 id="Camera-v2">Camera v2 API</h3>
 
 <p>The L Developer Preview introduces the new {@code android.hardware.camera2}
-API to facilitate fine grain photo capture and image processing. You can now programmatically access the camera devices available to the system with {@code CameraManager.getCameraIdList()} and connect to a specific device with {@code CameraManager.openCamera()}. To start capturing images, you
-need to create a {@code CameraCaptureSession} and specify the
-{@link android.view.Surface} objects to send the captured images. The {@code CameraCaptureSession} can be configured to take single shots or multiple images
-in a burst.</p>
+API to facilitate fine-grain photo capture and image processing. You can now
+programmatically access the camera devices available to the system with {@code
+CameraManager.getCameraIdList()} and connect to a specific device with {@code
+CameraManager.openCamera()}. To start capturing images, create a {@code
+CameraCaptureSession} and specify the {@link android.view.Surface} objects for
+the captured images. The {@code CameraCaptureSession} can be configured to take
+single shots or multiple images in a burst.</p>
 
 <p>To be notified when new images are captured, implement the
 {@code CameraCaptureSession.CaptureListener()} interface and set it in your
@@ -334,16 +439,17 @@
 {@code CaptureResult}.</p>
 
 <h3 id="AudioPlayback">Audio playback</h3>
-<p>This release includes the following changes for
-  {@code android.media.AudioTrack}:</p>
+<p>This release includes the following changes to
+  {@link android.media.AudioTrack}:</p>
 <ul>
   <li>Your app can now supply audio data in floating-point format
 ({@code android.media.AudioFormat.ENCODING_PCM_FLOAT}). This permits greater
 dynamic range, more consistent precision, and greater headroom. Floating-point arithmetic is especially useful during intermediate calculations. Playback
-end-points use integer format for audio data, and with lower bit-depth. In L
-Developer Preview, portions of the internal pipeline are not yet floating-point.
-  <li>Your app can now supply audio data as a {@code ByteBuffer}, in the same
-format as provided by {@code MediaCodec}.
+end-points use integer format for audio data, and with lower bit-depth. (In the
+L Developer Preview, portions of the internal pipeline are not yet
+floating-point.)
+  <li>Your app can now supply audio data as a {@link java.nio.ByteBuffer}, in the same
+format as provided by {@link android.media.MediaCodec}.
   <li>The {@code WRITE_NON_BLOCKING} option can simplify buffering and
     multithreading for some apps.
 </ul>
@@ -352,8 +458,8 @@
 <p>You can now build your own media controller app with the new
 {@code android.media.session.MediaController} class, which provides
 simplified transport controls APIs that replace those in
-{@code android.media.RemoteControlClient}. The {@code MediaController} class
-allows thread-safe control of playback from a non UI process, making it easier
+{@link android.media.RemoteControlClient}. The {@code MediaController} class
+allows thread-safe control of playback from a non-UI process, making it easier
 to control your media playback service from your app’s user interface.
 
 <p>You can also create multiple controllers to send playback commands,
@@ -362,14 +468,16 @@
 call {@code MediaSession.getSessionToken()} to request an access
 token in order for your app to interact with the session.</p>
 
-<p>Send transport commands such as "play", "stop", "skip", and
+<p>You can now send transport commands such as "play", "stop", "skip", and
 "set rating" by using {@code MediaController.TransportControls}. To handle
-in-bound media transport commands from controllers attached to the session, you
-should override the callback methods in
+in-bound media transport commands from controllers attached to the session,
+override the callback methods in
 {@code MediaSession.TransportControlsCallback}.</p>
 
 <p>You can also create rich notifications that allow playback control tied to a
-media session with the new {@code android.app.Notification.MediaStyle} class.</p>
+media session with the new {@code android.app.Notification.MediaStyle} class. By
+using the new notification and media APIs, you will ensure that the System UI
+knows about your playback and can extract and show album art.</p>
 
 <h2 id="Storage">Storage</h2>
 
@@ -381,46 +489,58 @@
 has access to all its child directories and content.</p>
 
 <p>To get the absolute paths to directories on external storage devices where
-applications can store media files, call the
-{@code android.content.Context.getExternalMediaDirs()} method. No additional
+applications can store media files, call the new
+{@code android.content.Context.getExternalMediaDirs()} method. No
+additional
 permissions are needed by your app to read or write to the returned paths.
-External storage devices here are those considered by the system to be a
+In this context, "external storage devices" are those devices which the system
+considers to be a
 permanent part of the device, and includes emulated external storage and
 physical media slots such as SD cards in battery compartments.</p>
 
 <p>If you want to access a document in an existing directory, call the
-{@code android.provider.DocumentsContract.buildDocumentViaUri()} method and pass
-in a Uri representing the path to the parent directory and the target document
-ID. The method returns a new {@link android.net.Uri} with which your app can
+{@code android.provider.DocumentsContract.buildDocumentViaUri()} method.
+Pass the method a URI representing the path to the parent directory, and the
+target document
+ID. The method returns a new {@link android.net.Uri} which your app can
 use to write media content with {@code DocumentsContract.createDocument()}.
 
 <h2 id="Wireless">Wireless &amp; Connectivity</h2>
 
 <h3 id="Multinetwork">Dynamic network selection and seamless handoff</h3>
-<p>The L Developer Preview provides new multi-networking APIs for your app to
+<p>The L Developer Preview provides new multi-networking APIs. These let your app
 dynamically scan for available networks with specific capabilities, and
 establish a connection to them. This is useful when your app requires a
 specialized network, such as an SUPL, MMS, or carrier-billing network, or if
 you want to send data using a particular type of transport protocol.</p>
 
-<p>To select and connect to a network dynamically from your app, first
-instantiate a {@code android.net.ConnectivityManager}. Next, create a
-{@code android.net.NetworkRequest} to specify the network features and transport
-type your app is interested in. To start scanning for suitable networks, call
-{@code ConnectivityManager.requestNetwork()} or
-{@code ConnectivityManager.registerNetworkCallback(), and pass in the
-{@code NetworkRequest} object and an implementation of
-{@code ConnectivityManager.NetworkCallbackListener}.</p>
+<p>To select and connect to a network dynamically from your app follow these
+steps:</p>
+
+<ol>
+ <li>Create a {@link android.net.ConnectivityManager}.</li>
+ <li>Create a
+  {@code android.net.NetworkRequest} to specify the network features and transport
+  type your app is interested in.</li>
+  <li>To scan for suitable networks, call
+  {@code ConnectivityManager.requestNetwork()} or
+  {@code ConnectivityManager.registerNetworkCallback()}, and pass in the
+  {@code NetworkRequest} object and an implementation of
+  {@code ConnectivityManager.NetworkCallbackListener}.</li>
+
+</ol>
 
 <p>When the system detects a suitable network, it connects to the network and
 invokes the {@code NetworkCallbackListener.onAvailable()} callback. You can use
 the {@code android.net.Network} object from the callback to get additional
-information about the network, or to establish a socket connection.</p>
+information about the network, or to direct traffic to use the selected
+network.</p>
 
 <h3 id="BluetoothBroadcasting">Bluetooth broadcasting</h3>
 <p>Android 4.3 introduced platform support for <a href="{@docRoot}guide/topics/connectivity/bluetooth-le.html">Bluetooth Low Energy</a>
 (BLE) in the central role. In the L Developer Preview, an Android device can now
-act as a Bluetooth LE <em>peripheral device</em> and make its presence known to
+act as a Bluetooth LE <em>peripheral device</em>. Apps can use this capability
+to make their presence known to
 nearby devices. For instance, you can build apps that allow a device to
 function as a pedometer or health monitor and communicate its data with another
 BLE device.</p>
@@ -429,16 +549,19 @@
 You must add the {@code android.permission.BLUETOOTH_ADMIN} permission in your
 manifest in order for your app to use the new advertising and scanning features.</a>
 
-<p>To begin Bluetooth LE advertising so that other devices can discover the
-device running your app, call {@code android.bluetooth.le.BluetoothAdvertiser.startAdvisertising()} and pass in an implementation of the
-{@code android.bluetooth.le.AdvertiseCallback} class to report the success
-or failure of the advertising operation.</p>
+<p>To begin Bluetooth LE advertising so that other devices can discover
+your app, call {@code android.bluetooth.le.BluetoothAdvertiser.startAdvisertising()}
+and pass in an implementation of the
+{@code android.bluetooth.le.AdvertiseCallback} class. The callback object
+receives a report of the success or failure of the advertising operation.</p>
 
-<p>Conversely, if you want to scan for Bluetooth LE devices nearby, call
-{@code android.bluetooth.le.BluetoothLeScanner.startScan()} and pass in an
+<p> The L Developer Preview introduces the {@code
+android.bluetooth.le.ScanFilter} class so that your app can scan for only the
+specific types of devices it is interested in. To begin scanning for Bluetooth
+LE devices, call {@code android.bluetooth.le.BluetoothLeScanner.startScan()} and
+pass in a list of filters. In the method call, you must also provide an
 implementation of {@code android.bluetooth.le.ScanCallback} to report if a
-Bluetooth LE advertisement is found. Optionally, you can pass in filters to scan
-for a specific type of device.</p>
+Bluetooth LE advertisement is found. </p>
 
 <h3 id="NFCEnhancements">NFC enhancements</h3>
 <p>The L Developer Preview adds these enhancements to enable wider and more
@@ -446,13 +569,12 @@
 
 <ul>
 <li>Android Beam is now available in the share menu.
-<li>Support for the <a href="http://www.wi-fi.org/discover-wi-fi/wi-fi-direct">Wi-fi Direct standard</a>.
 <li>Your app can invoke the Android Beam on the user’s device to share data by
 calling {@code android.nfc.NfcAdapter.invokeBeam()}. This avoids the need for
 the user to manually tap the device against another NFC-capable device to
 complete the data transfer.
-<li>Use the new {@code android.nfc.NdefRecord.createTextRecord()} method if
-  you want to create an NDEF record containing UTF-8 text data.
+<li>You can use the new {@code android.nfc.NdefRecord.createTextRecord()} method
+to create an NDEF record containing UTF-8 text data.
 <li>If you are developing a payment app, you now have the ability to
 register an NFC application ID (AID) dynamically by calling
 {@code android.nfc.cardemulation.CardEmulation.registerAidsForService()}.
@@ -466,18 +588,31 @@
 <h3 id="JobScheduler">Scheduling jobs</h3>
 <p>The L Developer Preview provides a new {@code android.app.job.JobScheduler}
 API that lets you optimize battery life by defining jobs for the system to run
-asynchronously at a later time, such as when the device is charging. This is
-useful when you want to defer non user-facing units of work, have application
-code that accesses the network, or want to run a number of tasks as a batch on
-a regular schedule.</p>
+asynchronously at a later time or under specified conditions (such as when the
+device is charging). This is useful in such situations as:</p>
+<ul>
+  <li>The app has non-user-facing work that you want to defer until the unit is
+    plugged in.</li>
+  <li>The app has a task that requires network access (or requires a wifi
+    connection).</li>
+  <li>The app has a number of tasks that you want to run as a batch on a regular
+   schedule.</li>
 
-<p>A {@code android.app.job.JobInfo} object encapsulates such a unit of work,
-and provides an exact description of the criteria you are scheduling.</p>
+</ul>
+
+<p>A unit of work is encapsulated by a {@code android.app.job.JobInfo} object.
+This object provides an exact description of the criteria to be used for
+scheduling.</p>
 
 <p>Use the {@code android.app.job.JobInfo.Builder} to configure how the
 scheduled task should run. You can schedule the task to run under specific
-conditions such as only while the device is charging, when connected to an
-unmetered network, or when the system deems the device is idle.</p>
+conditions, such as:</p>
+
+<ul>
+  <li>The device is charging</li>
+  <li>The device is connected to an unmetered network</li>
+  <li>The system deems the device to be idle</li>
+</ul>
 
 <p>For example, you can add code like this to run your task on an
 unmetered network:</p>
@@ -513,23 +648,33 @@
 </ul>
 
 <p>Use the {@code --help} option to learn about the various options for
-tailoring the output. For example, to run the tool to print battery usage
-statistics since the device was last charged for a given app package, run this
+tailoring the output. For example, to print battery usage
+statistics for a given app package since the device was last charged, run this
 command:
 <pre>
-$ adb shell dumpsys batterystats --charged <package-name>
+$ adb shell dumpsys batterystats --charged &lt;package-name&gt;
 </pre>
 </dd>
 
 <dt><strong>Battery Historian</strong></dt>
 <dd>
-<p>The Battery Historian tool ({@code historian.par}) analyzes L-based Android
-bug reports and creates an HTML visualization of power-related events. It can
-also visualize power consumption data from a power monitor, and will attempt to
-map power usage to the wakelocks seen. You can find the Battery Historian tool
+<p>The Battery Historian tool ({@code historian.par}) analyzes Android
+bug reports from the L Developer Preview and creates an HTML visualization of
+power-related events. It can
+also visualize power consumption data from a power monitor, and attempts to
+map power usage to the wake locks seen. You can find the Battery Historian tool
 in {@code &lt;sdk&gt;/tools}.</p>
 
-<p>For best results, you should first enable full wakelock reporting to allow
+<img src="images/battery_historian.png"
+     srcset="images/battery_historian@2x.png 2x"
+    alt="" width="440" height="240"
+    id="figure1" />
+<p class="img-caption">
+  <strong>Figure 1.</strong>HTML visualization generated by the Battery
+      Historian tool.
+</p>
+
+<p>For best results, you should first enable full wake lock reporting, to allow
 the Battery Historian tool to monitor uninterrupted over an extended period of
 time:</p>
 <pre>
@@ -548,93 +693,70 @@
 </pre>
 </dd>
 
-<dt><strong>On-device power management</strong></dt>
-<dd>
-<p>You can use the {@code android.os.BatteryManager} API to obtain power
-consumption information based on the battery fuel gauge included in Android
-phones and tablets. This is useful in cases when it is not convenient to
-connect external measurement equipment to the Android device.</p>
-<p>To retrieve the battery properties, call {@code BatteryManager.getIntProperty()}
-or {@code BatteryManager.getLongProperty()}. The properties available, the
-exact resolution of the values of each, and other characteristics such as
-update frequency depend on the particular device being tested.</p>
-
-<p>The following properties can be inspected on all Android devices:</p>
-
-<table>
-  <tr>
-     <th>Property</th>
-     <th>Description</th>
-  </tr>
-  <tr>
-     <td>{@code BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER}</td>
-     <td>Remaining battery capacity in microampere-hours.</td>
-  </tr>
-  <tr>
-     <td>{@code BatteryManager.BATTERY_PROPERTY_CURRENT_NOW}</td>
-     <td>Instantaneous battery current in microamperes.</td>
-  </tr>
-  <tr>
-     <td>{@code BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE}</td>
-     <td>Average battery current in microamperes</td>
-  </tr>
-  <tr>
-     <td>{@code BatteryManager.BATTERY_PROPERTY_CAPACITY}</td>
-     <td>Remaining battery capacity as an integer percentage.</td>
-  </tr>
-  <tr>
-     <td>{@code BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER}</td>
-     <td>Remaining energy in nanowatt-hours.</td>
-  </tr>
-</table>
-<dd>
 </dl>
 
 <h2 id="Enterprise">Enterprise</h2>
 <h3 id="ManagedProvisioning">Managed provisioning</h3>
 
+<div class="figure" style="width:360px">
+  <img src="images/managed_apps_launcher.png"
+    srcset="images/managed_apps_launcher@2x.png 2x"
+    alt="" width="360" height="572" id="figure2" />
+  <p class="img-caption">
+    <strong>Figure 2.</strong> Launcher screen showing managed apps (marked with
+    a lock badge)
+  </p>
+</div>
+
 <p>The L Developer Preview provides new functionality for running apps within
 an enterprise environment:</p>
 <ul>
 <li><strong>Create managed user profiles</strong>. A device administrator can
-initiate a managed provisioning process to enroll a user device with an
-existing personal account into a co-present but separate managed profile that
-the administrator controls.
-<li><strong>Set device owner scope</strong>. Device administrators can also
-apply managed provisioning to configure a device that has no previous user
-accounts installed, so that they have full control over the device.
+initiate a managed provisioning process to add a co-present but separate managed
+profile to a device with an existing personal account. The administrator has
+control over the managed profile.</li>
+<li><strong>Set device owner</strong>. Device administrators can also initiate a
+managed provisioning process to automatically provision a
+currently-unprovisioned device such that they have full control over the
+device.</li>
 </ul>
 
-<p>To start the manged provisioning process, send
-{@code ACTION_PROVISION_MANAGED_PROFILE} in an {@link android.content.Intent}. A
-user may be associated with more than one managed profile. To get a list of the
-managed profiles associated with the user, call
-{@code android.os.UserManager.getUserProfiles()}.</p>
+<p>To start the managed provisioning process, send {@code
+ACTION_PROVISION_MANAGED_PROFILE} in an {@link android.content.Intent}. If the
+call is successful, the system triggers the {@code
+android.app.admin.DeviceAdminReceiver. onProfileProvisioningComplete()} callback.
+You can then call {@code app.admin.DevicePolicyManager. setProfileEnabled()} to
+set this profile to the enabled state.</p>
+
+<p>A user may be associated with more than one managed profile. To get a list of
+the managed profiles associated with the user, call
+{@code android.os.UserManager. getUserProfiles()}.</p>
 
 <p>Once a managed profile is created for a user, apps that are managed by the
 device administrator will appear alongside non-managed apps in the user’s
-Launcher, Recent apps screen, and notifications. A device policy management app
-can make the managed apps visually prominent by appending a “work” badge to the
-icon drawable with {@code android.os.UserManager.getBadgeDrawableForUser()}.</p>
+Launcher, Recent apps screen, and notifications.</p>
 
-<p>If you are developing a Launcher app, you can use the new {@code android.content.pm.LauncherApps} class to get a list of launchable activities for the current user
-and any associated managed profiles.</p>
+<p>If you are developing a Launcher app, you can use the new {@code
+android.content.pm.LauncherApps} class to get a list of launchable activities
+for the current user and any associated managed profiles. Your Launcher can make
+the managed apps visually prominent by appending a “work” badge to the icon
+drawable with {@code android.os.UserManager.getBadgeDrawableForUser()}.</p>
 
 <h2 id="Printing">Printing Framework</h2>
 
 <h3 id="PDFRender">Render PDF as bitmap</h3>
 <p>You can now render PDF document pages into bitmap images for printing by
 using the new {@code android.graphics.pdf.PdfRenderer} class. You must specify a
-{@code ParcelFileDescriptor} that is seekable (that is, the file can be randomly
+{@link android.os.ParcelFileDescriptor} that is seekable (that is, the content can be randomly
 accessed) on which the system writes the the printable content. Your app can
 obtain a page for rendering with {@code openPage()}, then call {@code render()}
 to turn the opened {@code PdfRenderer.Page} into a bitmap. You can also set
-additional parameters if you only wan to convert a portion of the document into
+additional parameters if you only want to convert a portion of the document into
 a bitmap image (for example, to implement <a href="http://en.wikipedia.org/wiki/Tiled_rendering">tile rendering</a> in order to zoom in on the document).</p>
 
 <h2 id="TestingA11y">Testing &amp; Accessibility </h2>
 
-<h3 id="Testing A11yImprovements">Testing and accessibility improvements</h3>
+<h3 id="TestingA11yImprovements">Testing and accessibility improvements</h3>
 <p>The L Developer Preview adds the following support for testing and
 accessibility:</p>
 
@@ -644,44 +766,45 @@
 capture frame statistics for window animations and content. This lets you
 write instrumentation tests to evaluate if the app under test is rendering
 frames at a sufficient refresh frequency to provide a smooth user experience.
+
 <li>You can execute shell commands from your instrumentation test with the new
 {@code android.app.UiAutomation.executeShellCommand()}. The command execution
-is similar to running 'adb shell' from a host connected to the device. This
+is similar to running {@code adb shell} from a host connected to the device. This
 allows you to use shell based tools such as {@code dumpsys}, {@code am},
 {@code content}, and {@code pm}.
+
 <li>Accessibility services and test tools that use the accessibility APIs
-(such as <a href="{@docRoot}tools/help/uiautomator/index.html">UiAutomator</a>)
+(such as <a href="{@docRoot}tools/help/uiautomator/index.html">uiautomator</a>)
 can now retrieve detailed information about the properties of windows on the
 screen that sighted users can interact with. To retrieve a list of
-{@code android.view.accessibility.AccessibilityWindowInfo} representing the
+{@code android.view.accessibility.AccessibilityWindowInfo} objects
+representing the
 windows information, call the new
 {@code android.accessibilityservice.AccessibilityService.getWindows()} method.
 <li>You can use the new {@code android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction} to define standard or customized
-actions to perform on an {@code android.view.accessibility.AccessibilityNodeInfo}.
+actions to perform on an {@link android.view.accessibility.AccessibilityNodeInfo}.
 The new {@code AccessibilityAction} class replaces the actions-related APIs
 previously found in {@code AccessibilityNodeInfo}.
 </ul>
 
-<h2 id="manifest">Manifest Declarations</h2>
+<h2 id="Manifest">Manifest Declarations</h2>
 
 <h3 id="ManifestFeatures">Declarable required features</h3>
-<p>The following values are now supported in the <a href="{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code &lt;uses-feature&gt;}</a> element so you
+<p>The following values are now supported in the <a href="{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code &lt;uses-feature&gt;}</a> element, so you
 can ensure that your app is installed only on devices that provide the features
 your app needs.</p>
 
 <ul>
-<li>{@code FEATURE_LEANBACK}. Declares that your app must be installed only on devices that support the <a href="{@docRoot}tv}">Android TV</a> user interface. Example:
+<li>{@code FEATURE_LEANBACK}. Declares that your app must be installed only on
+devices that support the <a href="{@docRoot}training/tv}">Android TV</a> user
+interface. Example:
 <pre>
 &lt;uses-feature android:name="android.software.leanback"
               android:required="true" /&gt;
 </pre>
 
-<li>{@code FEATURE_MANAGEDPROFILES}. Declares that your app must only be installed on devices that support managed profiles for enterprise users. Example:
-<pre>
-&lt;uses-feature android:name="android.software.managedprofiles"
-              android:required="true" /&gt;
-</pre>
-<li>{@code FEATURE_WEBVIEW}. Declares that your app must only be installed on devices that fully implement the android.webkit.* APIs. Example:
+<li>{@code FEATURE_WEBVIEW}. Declares that your app must only be installed on
+devices that fully implement the {@code android.webkit.*} APIs. Example:
 <pre>
 &lt;uses-feature android:name="android.software.webview"
               android:required="true" /&gt;
diff --git a/docs/html/preview/images/battery_historian.png b/docs/html/preview/images/battery_historian.png
new file mode 100644
index 0000000..5b0db74
--- /dev/null
+++ b/docs/html/preview/images/battery_historian.png
Binary files differ
diff --git a/docs/html/preview/images/battery_historian@2x.png b/docs/html/preview/images/battery_historian@2x.png
new file mode 100644
index 0000000..dbb5d5e
--- /dev/null
+++ b/docs/html/preview/images/battery_historian@2x.png
Binary files differ
diff --git a/docs/html/preview/images/managed_apps_launcher.png b/docs/html/preview/images/managed_apps_launcher.png
new file mode 100644
index 0000000..983d904
--- /dev/null
+++ b/docs/html/preview/images/managed_apps_launcher.png
Binary files differ
diff --git a/docs/html/preview/images/managed_apps_launcher@2.png b/docs/html/preview/images/managed_apps_launcher@2.png
new file mode 100644
index 0000000..d298fd2
--- /dev/null
+++ b/docs/html/preview/images/managed_apps_launcher@2.png
Binary files differ
diff --git a/docs/html/tv/images/hero.jpg b/docs/html/tv/images/hero.jpg
index c42a436..e951167 100644
--- a/docs/html/tv/images/hero.jpg
+++ b/docs/html/tv/images/hero.jpg
Binary files differ
diff --git a/docs/html/tv/index.jd b/docs/html/tv/index.jd
index e1cae8c..3e7652c 100644
--- a/docs/html/tv/index.jd
+++ b/docs/html/tv/index.jd
@@ -4,7 +4,6 @@
 no_footer_links=true
 page.type=about
 
-
 @jd:body
 
 <style>
@@ -14,17 +13,9 @@
 }
 </style>
 
-<style>
-#footer {
-    display: none;
-}
-.content-footer {
-  display: none;
-}
-</style>
-
 
 <div class="landing-body-content">
+
   <div class="landing-hero-container">
 
     <div class="landing-section tv-hero">
@@ -42,9 +33,11 @@
                     Put your app on TV and bring everyone into
                     the action.</p>
                 </div>
+              </div>
 
               <div class="landing-body">
-                <a href="{@docRoot}preview/tv/index.html" class="landing-button landing-primary" style="margin-top: 40px;">
+                <a href="{@docRoot}preview/tv/start/index.html" class="landing-button
+                  landing-primary" style="margin-top: 40px;">
                   Get Started
                 </a>
               </div>
@@ -58,11 +51,10 @@
         </a>
       </div>
     </div> <!-- end .landing-section .landing-hero -->
-  </div> <!-- end .landing-hero-container -->
 
     <div class="landing-rest-of-page">
 
-      <div class="landing-section landing-gray-background" id="reimagine-your-app">
+      <div class="landing-section" style="background-color:#f5f5f5" id="reimagine-your-app">
         <div class="wrap">
           <div class="landing-section-header">
             <div class="landing-h1">Reimagine Your App</div>
@@ -71,7 +63,6 @@
             </div>
           </div>
 
-
           <div class="landing-body">
             <div class="landing-breakout cols">
 
@@ -119,13 +110,13 @@
         </div>  <!-- end .wrap -->
       </div>  <!-- end .landing-section -->
 
-      <div class="landing-section" style="background-color:#f5f5f5">
+      <div class="landing-section landing-gray-background">
         <div class="wrap">
           <div class="landing-section-header">
             <div class="landing-h1">Build to Entertain</div>
             <div class="landing-subhead">
-              Android TV let's you engage your users in a new, shared environment.<br>
-              Find out how to get your app ready for it's big screen debut.
+              Android TV lets you engage your users in a new, shared environment.<br>
+              Find out how to get your app ready for its big-screen debut.
             </div>
           </div>
 
@@ -142,7 +133,7 @@
                   catalogs.
                 </p>
                 <p class="landing-small">
-                  <a href="{@docRoot}design/tv/index.html">Learn pre-built fragments</a>
+                  <a href="{@docRoot}preview/tv/ui/browse.html">Learn pre-built fragments</a>
                 </p>
               </div>
 
@@ -151,11 +142,10 @@
 
                 <p>Get Found</p>
                 <p class="landing-small">
-                  Give your content the attention it deserves by including it in Android TV's global
-                  search results.
+                  Help users find your content quickly with in-app searching.
                 </p>
                 <p class="landing-small">
-                  <a href="{@docRoot}design/tv/index.html">Learn about TV design</a>
+                  <a href="{@docRoot}preview/tv/ui/in-app-search.html">Learn about app search</a>
                 </p>
               </div>
 
@@ -167,7 +157,8 @@
                   Suggest content from your app to keep your users coming back.
                 </p>
                 <p class="landing-small">
-                  <a href="{@docRoot}design/tv/index.html">Learn about design for TV</a>
+                  <a href="{@docRoot}preview/tv/ui/recommendations.html">Learn about
+                    recommendations</a>
                 </p>
               </div>
 
@@ -182,28 +173,26 @@
           <div class="landing-section-header">
             <div class="landing-h1 landing-align-left">Get Started with Android TV</div>
             <div class="landing-body">
-              <p>You can begin building apps right away using these developer resources.</p>
+              <p>Begin building TV apps right away using these developer resources:</p>
             </div>
           </div>
 
           <div class="landing-body">
             <div class="landing-breakout cols">
-              <div class="col-8">
-                <p>Preview SDK</p>
+              <div class="col-8" style="margin-left: -8px;">
+                <p style="font-size: 24px;">L-Preview SDK</p>
                 <p>
-                  Get started building for Android TV using the Android L-preview SDK. The preview
-                  SDK includes the Android TV emulator so you can start building your TV app right
-                  away.
+                  The preview SDK includes all the tools you need to build and test apps for TV.
+                  Download it and start creating your big-screen app.
                 </p>
 
               </div>
 
               <div class="col-8">
-                <p>ADT-1 Developer Kit</p>
+                <p style="font-size: 24px;">ADT-1 Developer Kit</p>
                 <p>
-                  While supplies last, developers can request an ADT-1 Developer Kit, a compact and
-                  powerful streaming media player and gamepad, ideal for developing apps for Android
-                  TV.
+                  Request an ADT-1 Developer Kit, a compact and powerful streaming media player
+                  and gamepad, ideal for developing and testing apps for TV.
                 </p>
 
               </div>
@@ -215,15 +204,16 @@
             <div class="landing-breakout cols">
 
               <div class="col-8">
-                <a href="{@docRoot}preview/download.html" class="landing-button landing-secondary">
+                <a href="{@docRoot}preview/setup-sdk.html" class="landing-button landing-secondary">
                   Download the Preview SDK
                 </a>
               </div>
 
               <div class="col-8">
-                <a href="{@docRoot}tv/adt-1/request.html" class="landing-button landing-secondary">
+                <a href="{@docRoot}preview/tv/adt-1/request.html" class="landing-button landing-secondary">
                   Request ADT-1 Developer Kit
                 </a>
+              </div>
             </div>
           </div>
 
@@ -232,31 +222,33 @@
 
     </div> <!-- end .landing-rest-of-page -->
 
-
-    <div class="content-footer wrap" itemscope="" itemtype="http://schema.org/SiteNavigationElement">
-      <div class="layout-content-col col-16" style="padding-top:4px">
-        <style>#___plusone_0 {float:right !important;}</style>
-        <div class="g-plusone" data-size="medium"></div>
+      <div class="content-footer wrap" itemscope="" itemtype="http://schema.org/SiteNavigationElement"
+        style="border-top: none;">
+        <div class="layout-content-col col-16" style="padding-top:4px">
+          <style>#___plusone_0 {float:right !important;}</style>
+          <div class="g-plusone" data-size="medium"></div>
+        </div>
       </div>
-    </div>
-    <div id="footer" class="wrap" style="width:940px;position:relative;top:-35px;z-index:-1">
-      <div id="copyright">
-        Except as noted, this content is
-        licensed under <a href="http://creativecommons.org/licenses/by/2.5/">
-        Creative Commons Attribution 2.5</a>. For details and
-        restrictions, see the <a href="/license.html">Content
-        License</a>.
+      <div id="footer" class="wrap" style="width:940px;position:relative;top:-35px;z-index:-1">
+        <div id="copyright">
+          Except as noted, this content is
+          licensed under <a href="http://creativecommons.org/licenses/by/2.5/">
+          Creative Commons Attribution 2.5</a>. For details and
+          restrictions, see the <a href="/license.html">Content
+          License</a>.
+        </div>
       </div>
-    </div>
 
-
-  </div> <!-- end landing-body-content -->
+  </div> <!-- end .landing-hero-container -->
 
   <script>
   $("a.landing-down-arrow").on("click", function(e) {
     $("body").animate({
-      scrollTop: $(".wear-hero").height() + 76
+      scrollTop: $(".tv-hero").height() + 120
     }, 1000, "easeOutQuint");
     e.preventDefault();
   });
   </script>
+
+</div> <!-- end landing-body-content -->
+
diff --git a/docs/html/wear/index.jd b/docs/html/wear/index.jd
index 59d5506..0d9325d 100644
--- a/docs/html/wear/index.jd
+++ b/docs/html/wear/index.jd
@@ -66,7 +66,7 @@
         </a>
       </div>
     </div> <!-- end .landing-section .landing-hero -->
-  </div> <!-- end .landing-hero-container -->
+
 
     <div class="landing-rest-of-page">
       <div class="landing-section" id="extending-android-to-wearables">
@@ -264,7 +264,7 @@
     <div class="content-footer wrap" itemscope="" itemtype="http://schema.org/SiteNavigationElement">
       <div class="layout-content-col col-16" style="padding-top:4px">
         <style>#___plusone_0 {float:right !important;}</style>
-        <div id="___plusone_0" style="text-indent: 0px; margin: 0px; padding: 0px; border-style: none; float: none; line-height: normal; font-size: 1px; vertical-align: baseline; display: inline-block; width: 90px; height: 20px; background: transparent;"><iframe frameborder="0" hspace="0" marginheight="0" marginwidth="0" scrolling="no" style="position: static; top: 0px; width: 90px; margin: 0px; border-style: none; left: 0px; visibility: visible; height: 20px;" tabindex="0" vspace="0" width="100%" id="I0_1402525433965" name="I0_1402525433965" src="https://apis.google.com/u/0/_/+1/fastbutton?usegapi=1&amp;size=medium&amp;origin=http%3A%2F%2Frobertly.mtv%3A8080&amp;url=http%3A%2F%2Frobertly.mtv%3A8080%2Fwear%2Findex.html&amp;gsrc=3p&amp;jsh=m%3B%2F_%2Fscs%2Fapps-static%2F_%2Fjs%2Fk%3Doz.gapi.en.QxHQHBkhz7M.O%2Fm%3D__features__%2Fam%3DUQ%2Frt%3Dj%2Fd%3D1%2Fz%3Dzcms%2Frs%3DAItRSTMLrMyRVKsu2FQoRingre3w1MT49A#_methods=onPlusOne%2C_ready%2C_close%2C_open%2C_resizeMe%2C_renderstart%2Concircled%2Cdrefresh%2Cerefresh%2Conload&amp;id=I0_1402525433965&amp;parent=http%3A%2F%2Frobertly.mtv%3A8080&amp;pfname=&amp;rpctoken=32453860" data-gapiattached="true" title="+1"></iframe></div>
+        <div class="g-plusone" data-size="medium"></div>
       </div>
     </div>
     <div id="footer" class="wrap" style="width:940px;position:relative;top:-35px;z-index:-1">
@@ -276,5 +276,16 @@
         License</a>.
       </div>
     </div>
-  </div> <!-- end landing-body-content -->
+  </div> <!-- end .landing-hero-container -->
+
+  <script>
+  $("a.landing-down-arrow").on("click", function(e) {
+    $("body").animate({
+      scrollTop: $(".wear-hero").height() + 120
+    }, 1000, "easeOutQuint");
+    e.preventDefault();
+  });
+  </script>
+
+</div> <!-- end landing-body-content -->
 
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 158801c..13421aa 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -1770,7 +1770,7 @@
         }
         native_drawTextOnPath(mNativeCanvasWrapper, text, index, count,
                 path.ni(), hOffset, vOffset,
-                paint.mBidiFlags, paint.mNativePaint);
+                paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
     }
 
     /**
@@ -1790,7 +1790,7 @@
             float vOffset, @NonNull Paint paint) {
         if (text.length() > 0) {
             native_drawTextOnPath(mNativeCanvasWrapper, text, path.ni(), hOffset, vOffset,
-                    paint.mBidiFlags, paint.mNativePaint);
+                    paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
         }
     }
 
@@ -2021,11 +2021,11 @@
                                                      int count, long nativePath,
                                                      float hOffset,
                                                      float vOffset, int bidiFlags,
-                                                     long nativePaint);
+                                                     long nativePaint, long nativeTypeface);
     private static native void native_drawTextOnPath(long nativeCanvas,
                                                      String text, long nativePath,
                                                      float hOffset,
                                                      float vOffset,
-                                                     int flags, long nativePaint);
+                                                     int flags, long nativePaint, long nativeTypeface);
     private static native void finalizer(long nativeCanvas);
 }
diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java
index a021165..5aa7c6a 100644
--- a/graphics/java/android/graphics/Picture.java
+++ b/graphics/java/android/graphics/Picture.java
@@ -31,18 +31,13 @@
     private Canvas mRecordingCanvas;
     private final long mNativePicture;
 
-    /**
-     * @hide
-     */
-    public final boolean createdFromStream;
-
     private static final int WORKING_STREAM_STORAGE = 16 * 1024;
 
     /**
      * Creates an empty picture that is ready to record.
      */
     public Picture() {
-        this(nativeConstructor(0), false);
+        this(nativeConstructor(0));
     }
 
     /**
@@ -51,7 +46,23 @@
      * changes will not be reflected in this picture.
      */
     public Picture(Picture src) {
-        this(nativeConstructor(src != null ? src.mNativePicture : 0), false);
+        this(nativeConstructor(src != null ? src.mNativePicture : 0));
+    }
+
+    private Picture(long nativePicture) {
+        if (nativePicture == 0) {
+            throw new RuntimeException();
+        }
+        mNativePicture = nativePicture;
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            nativeDestructor(mNativePicture);
+        } finally {
+            super.finalize();
+        }
     }
 
     /**
@@ -85,13 +96,17 @@
      * Get the width of the picture as passed to beginRecording. This
      * does not reflect (per se) the content of the picture.
      */
-    public native int getWidth();
+    public int getWidth() {
+      return nativeGetWidth(mNativePicture);
+    }
 
     /**
      * Get the height of the picture as passed to beginRecording. This
      * does not reflect (per se) the content of the picture.
      */
-    public native int getHeight();
+    public int getHeight() {
+      return nativeGetHeight(mNativePicture);
+    }
 
     /**
      * Draw this picture on the canvas.
@@ -130,7 +145,7 @@
      */
     @Deprecated
     public static Picture createFromStream(InputStream stream) {
-        return new Picture(nativeCreateFromStream(stream, new byte[WORKING_STREAM_STORAGE]), true);
+        return new Picture(nativeCreateFromStream(stream, new byte[WORKING_STREAM_STORAGE]));
     }
 
     /**
@@ -159,32 +174,12 @@
         }
     }
 
-    protected void finalize() throws Throwable {
-        try {
-            nativeDestructor(mNativePicture);
-        } finally {
-            super.finalize();
-        }
-    }
-
-    final long ni() {
-        return mNativePicture;
-    }
-
-    private Picture(long nativePicture, boolean fromStream) {
-        if (nativePicture == 0) {
-            throw new RuntimeException();
-        }
-        mNativePicture = nativePicture;
-        createdFromStream = fromStream;
-    }
-
     // return empty picture if src is 0, or a copy of the native src
     private static native long nativeConstructor(long nativeSrcOr0);
-    private static native long nativeCreateFromStream(InputStream stream,
-                                                byte[] storage);
-    private static native long nativeBeginRecording(long nativeCanvas,
-                                                    int w, int h);
+    private static native long nativeCreateFromStream(InputStream stream, byte[] storage);
+    private static native int nativeGetWidth(long nativePicture);
+    private static native int nativeGetHeight(long nativePicture);
+    private static native long nativeBeginRecording(long nativeCanvas, int w, int h);
     private static native void nativeEndRecording(long nativeCanvas);
     private static native void nativeDraw(long nativeCanvas, long nativePicture);
     private static native boolean nativeWriteToStream(long nativePicture,
@@ -201,18 +196,15 @@
 
         @Override
         public void setBitmap(Bitmap bitmap) {
-            throw new RuntimeException(
-                                "Cannot call setBitmap on a picture canvas");
+            throw new RuntimeException("Cannot call setBitmap on a picture canvas");
         }
 
         @Override
         public void drawPicture(Picture picture) {
             if (mPicture == picture) {
-                throw new RuntimeException(
-                            "Cannot draw a picture into its recording canvas");
+                throw new RuntimeException("Cannot draw a picture into its recording canvas");
             }
             super.drawPicture(picture);
         }
     }
 }
-
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index be940df..0a394d5 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -928,7 +928,7 @@
             mTargetDensity = state.mTargetDensity;
         }
 
-        updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
+        mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
         computeBitmapSize();
     }
 }
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index c3c1bca..a1e1f76 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -147,10 +147,12 @@
     }
 
     private VectorDrawable(VectorDrawableState state, Resources res, Theme theme) {
-        mVectorState = new VectorDrawableState(state);
-
-        if (theme != null && canApplyTheme()) {
+        if (theme != null && state.canApplyTheme()) {
+            // If we need to apply a theme, implicitly mutate.
+            mVectorState = new VectorDrawableState(state);
             applyTheme(theme);
+        } else {
+            mVectorState = state;
         }
 
         mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
diff --git a/include/androidfw/ByteBucketArray.h b/include/androidfw/ByteBucketArray.h
new file mode 100644
index 0000000..87c6b12
--- /dev/null
+++ b/include/androidfw/ByteBucketArray.h
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+#ifndef __BYTE_BUCKET_ARRAY_H
+#define __BYTE_BUCKET_ARRAY_H
+
+#include <utils/Log.h>
+#include <stdint.h>
+#include <string.h>
+
+namespace android {
+
+/**
+ * Stores a sparsely populated array. Has a fixed size of 256
+ * (number of entries that a byte can represent).
+ */
+template<typename T>
+class ByteBucketArray {
+public:
+    ByteBucketArray() : mDefault() {
+        memset(mBuckets, 0, sizeof(mBuckets));
+    }
+
+    ~ByteBucketArray() {
+        for (size_t i = 0; i < NUM_BUCKETS; i++) {
+            if (mBuckets[i] != NULL) {
+                delete [] mBuckets[i];
+            }
+        }
+        memset(mBuckets, 0, sizeof(mBuckets));
+    }
+
+    inline size_t size() const {
+        return NUM_BUCKETS * BUCKET_SIZE;
+    }
+
+    inline const T& get(size_t index) const {
+        return (*this)[index];
+    }
+
+    const T& operator[](size_t index) const {
+        if (index >= size()) {
+            return mDefault;
+        }
+
+        uint8_t bucketIndex = static_cast<uint8_t>(index) >> 4;
+        T* bucket = mBuckets[bucketIndex];
+        if (bucket == NULL) {
+            return mDefault;
+        }
+        return bucket[0x0f & static_cast<uint8_t>(index)];
+    }
+
+    T& editItemAt(size_t index) {
+        ALOG_ASSERT(index < size(), "ByteBucketArray.getOrCreate(index=%u) with size=%u",
+                (uint32_t) index, (uint32_t) size());
+
+        uint8_t bucketIndex = static_cast<uint8_t>(index) >> 4;
+        T* bucket = mBuckets[bucketIndex];
+        if (bucket == NULL) {
+            bucket = mBuckets[bucketIndex] = new T[BUCKET_SIZE]();
+        }
+        return bucket[0x0f & static_cast<uint8_t>(index)];
+    }
+
+    bool set(size_t index, const T& value) {
+        if (index >= size()) {
+            return false;
+        }
+
+        editItemAt(index) = value;
+        return true;
+    }
+
+private:
+    enum { NUM_BUCKETS = 16, BUCKET_SIZE = 16 };
+
+    T*  mBuckets[NUM_BUCKETS];
+    T   mDefault;
+};
+
+} // namespace android
+
+#endif // __BYTE_BUCKET_ARRAY_H
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index 4d8e512..e612c0a 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -237,6 +237,7 @@
 #define Res_MAKEARRAY(entry) (0x02000000 | (entry&0xFFFF))
 
 #define Res_MAXPACKAGE 255
+#define Res_MAXTYPE 255
 
 /**
  * Representation of a value in a resource, supplying type
@@ -510,6 +511,23 @@
     uint32_t                    mStylePoolSize;    // number of uint32_t
 };
 
+/**
+ * Wrapper class that allows the caller to retrieve a string from
+ * a string pool without knowing which string pool to look.
+ */
+class StringPoolRef {
+public:
+    StringPoolRef();
+    StringPoolRef(const ResStringPool* pool, uint32_t index);
+
+    const char* string8(size_t* outLen) const;
+    const char16_t* string16(size_t* outLen) const;
+
+private:
+    const ResStringPool*        mPool;
+    uint32_t                    mIndex;
+};
+
 /** ********************************************************************
  *  XML Tree
  *
@@ -835,6 +853,8 @@
 
     // Last index into keyStrings that is for public use by others.
     uint32_t lastPublicKey;
+
+    uint32_t typeIdOffset;
 };
 
 // The most specific locale can consist of:
@@ -1469,9 +1489,13 @@
              bool copyData=false);
     ~ResTable();
 
-    status_t add(Asset* asset, const int32_t cookie, bool copyData,
-                 const void* idmap = NULL);
-    status_t add(const void *data, size_t size);
+    status_t add(const void* data, size_t size, const int32_t cookie=-1, bool copyData=false);
+    status_t add(const void* data, size_t size, const void* idmapData, size_t idmapDataSize,
+            const int32_t cookie=-1, bool copyData=false);
+
+    status_t add(Asset* asset, const int32_t cookie=-1, bool copyData=false);
+    status_t add(Asset* asset, Asset* idmapAsset, const int32_t cookie=-1, bool copyData=false);
+
     status_t add(ResTable* src);
     status_t addEmpty(const int32_t cookie);
 
@@ -1610,13 +1634,14 @@
             uint32_t typeSpecFlags;
             Res_value value;
         };
+
         struct type_info {
             size_t numEntries;
             theme_entry* entries;
         };
+
         struct package_info {
-            size_t numTypes;
-            type_info types[];
+            type_info types[Res_MAXTYPE + 1];
         };
 
         void free_package(package_info* pi);
@@ -1711,6 +1736,7 @@
     size_t getBasePackageCount() const;
     const String16 getBasePackageName(size_t idx) const;
     uint32_t getBasePackageId(size_t idx) const;
+    uint32_t getLastTypeIdForPackage(size_t idx) const;
 
     // Return the number of resource tables that the object contains.
     size_t getTableCount() const;
@@ -1740,13 +1766,15 @@
             void** outData, size_t* outSize) const;
 
     enum {
-        IDMAP_HEADER_SIZE_BYTES = 3 * sizeof(uint32_t) + 2 * 256,
+        IDMAP_HEADER_SIZE_BYTES = 4 * sizeof(uint32_t) + 2 * 256,
     };
+
     // Retrieve idmap meta-data.
     //
     // This function only requires the idmap header (the first
     // IDMAP_HEADER_SIZE_BYTES) bytes of an idmap file.
     static bool getIdmapInfo(const void* idmap, size_t size,
+            uint32_t* pVersion,
             uint32_t* pTargetCrc, uint32_t* pOverlayCrc,
             String8* pTargetPath, String8* pOverlayPath);
 
@@ -1756,21 +1784,24 @@
 private:
     struct Header;
     struct Type;
+    struct Entry;
     struct Package;
     struct PackageGroup;
     struct bag_set;
+    typedef Vector<Type*> TypeList;
 
-    status_t addInternal(const void* data, size_t size, const int32_t cookie,
-                 bool copyData, const Asset* idmap);
+    status_t addInternal(const void* data, size_t size, const void* idmapData, size_t idmapDataSize,
+            const int32_t cookie, bool copyData);
 
     ssize_t getResourcePackageIndex(uint32_t resID) const;
-    ssize_t getEntry(
-        const Package* package, int typeIndex, int entryIndex,
+
+    status_t getEntry(
+        const PackageGroup* packageGroup, int typeIndex, int entryIndex,
         const ResTable_config* config,
-        const ResTable_type** outType, const ResTable_entry** outEntry,
-        const Type** outTypeClass) const;
+        Entry* outEntry) const;
+
     status_t parsePackage(
-        const ResTable_package* const pkg, const Header* const header, uint32_t idmap_id);
+        const ResTable_package* const pkg, const Header* const header);
 
     void print_value(const Package* pkg, const Res_value& value) const;
     
diff --git a/include/androidfw/TypeWrappers.h b/include/androidfw/TypeWrappers.h
new file mode 100644
index 0000000..7bdf8af
--- /dev/null
+++ b/include/androidfw/TypeWrappers.h
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+#ifndef __TYPE_WRAPPERS_H
+#define __TYPE_WRAPPERS_H
+
+#include <androidfw/ResourceTypes.h>
+
+namespace android {
+
+struct TypeVariant {
+    TypeVariant(const ResTable_type* data)
+        : data(data) {}
+
+    class iterator {
+    public:
+        iterator& operator=(const iterator& rhs) {
+            mTypeVariant = rhs.mTypeVariant;
+            mIndex = rhs.mIndex;
+        }
+
+        bool operator==(const iterator& rhs) const {
+            return mTypeVariant == rhs.mTypeVariant && mIndex == rhs.mIndex;
+        }
+
+        bool operator!=(const iterator& rhs) const {
+            return mTypeVariant != rhs.mTypeVariant || mIndex != rhs.mIndex;
+        }
+
+        iterator operator++(int) {
+            uint32_t prevIndex = mIndex;
+            operator++();
+            return iterator(mTypeVariant, prevIndex);
+        }
+
+        const ResTable_entry* operator->() const {
+            return operator*();
+        }
+
+        uint32_t index() const {
+            return mIndex;
+        }
+
+        iterator& operator++();
+        const ResTable_entry* operator*() const;
+
+    private:
+        friend struct TypeVariant;
+        iterator(const TypeVariant* tv, uint32_t index)
+            : mTypeVariant(tv), mIndex(index) {}
+        const TypeVariant* mTypeVariant;
+        uint32_t mIndex;
+    };
+
+    iterator beginEntries() const {
+        return iterator(this, 0);
+    }
+
+    iterator endEntries() const {
+        return iterator(this, dtohl(data->entryCount));
+    }
+
+    const ResTable_type* data;
+};
+
+} // namespace android
+
+#endif // __TYPE_WRAPPERS_H
diff --git a/libs/androidfw/Android.mk b/libs/androidfw/Android.mk
index d21197e..957809d 100644
--- a/libs/androidfw/Android.mk
+++ b/libs/androidfw/Android.mk
@@ -25,6 +25,7 @@
     ObbFile.cpp \
     ResourceTypes.cpp \
     StreamingZipInflater.cpp \
+    TypeWrappers.cpp \
     ZipFileRO.cpp \
     ZipUtils.cpp
 
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 1b3f1fd..0340928 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -256,7 +256,7 @@
     String8 targetPath;
     String8 overlayPath;
     if (!ResTable::getIdmapInfo(idmap->getBuffer(false), idmap->getLength(),
-                NULL, NULL, &targetPath, &overlayPath)) {
+                NULL, NULL, NULL, &targetPath, &overlayPath)) {
         ALOGW("failed to read idmap file %s\n", idmapPath.string());
         delete idmap;
         return false;
@@ -311,7 +311,7 @@
             ALOGW("failed to find resources.arsc in %s\n", ap.path.string());
             return false;
         }
-        tables[i].add(ass, 1, false /* copyData */, NULL /* idMap */);
+        tables[i].add(ass);
     }
 
     return tables[0].createIdmap(tables[1], targetCrc, overlayCrc,
@@ -617,7 +617,7 @@
                     // can quickly copy it out for others.
                     ALOGV("Creating shared resources for %s", ap.path.string());
                     sharedRes = new ResTable();
-                    sharedRes->add(ass, i + 1, false, idmap);
+                    sharedRes->add(ass, idmap, i + 1, false);
 #ifdef HAVE_ANDROID_OS
                     const char* data = getenv("ANDROID_DATA");
                     LOG_ALWAYS_FATAL_IF(data == NULL, "ANDROID_DATA not set");
@@ -646,7 +646,7 @@
                 mResources->add(sharedRes);
             } else {
                 ALOGV("Parsing resources for %s", ap.path.string());
-                mResources->add(ass, i + 1, !shared, idmap);
+                mResources->add(ass, idmap, i + 1, !shared);
             }
             onlyEmptyResources = false;
 
@@ -654,7 +654,7 @@
                 delete ass;
             }
         } else {
-            ALOGW("Installing empty resources in to table %p\n", mResources);
+            ALOGV("Installing empty resources in to table %p\n", mResources);
             mResources->addEmpty(i + 1);
         }
 
@@ -736,7 +736,7 @@
         if (oass != NULL) {
             Asset* oidmap = openIdmapLocked(oap);
             offset++;
-            sharedRes->add(oass, offset + 1, false, oidmap);
+            sharedRes->add(oass, oidmap, offset + 1, false);
             const_cast<AssetManager*>(this)->mAssetPaths.add(oap);
             const_cast<AssetManager*>(this)->mZipSet.addOverlay(targetPackagePath, oap);
         }
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index a4b78a6..2e3abb5 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -17,7 +17,9 @@
 #define LOG_TAG "ResourceType"
 //#define LOG_NDEBUG 0
 
+#include <androidfw/ByteBucketArray.h>
 #include <androidfw/ResourceTypes.h>
+#include <androidfw/TypeWrappers.h>
 #include <utils/Atomic.h>
 #include <utils/ByteOrder.h>
 #include <utils/Debug.h>
@@ -30,6 +32,7 @@
 #include <memory.h>
 #include <ctype.h>
 #include <stdint.h>
+#include <stddef.h>
 
 #ifndef INT32_MAX
 #define INT32_MAX ((int32_t)(2147483647))
@@ -42,7 +45,7 @@
 #define TABLE_SUPER_NOISY(x) //x
 #define LOAD_TABLE_NOISY(x) //x
 #define TABLE_THEME(x) //x
-#define LIB_NOISY(x) x
+#define LIB_NOISY(x) //x
 
 namespace android {
 
@@ -63,9 +66,8 @@
 #endif
 #endif
 
-#define IDMAP_MAGIC         0x706d6469
-// size measured in sizeof(uint32_t)
-#define IDMAP_HEADER_SIZE (ResTable::IDMAP_HEADER_SIZE_BYTES / sizeof(uint32_t))
+#define IDMAP_MAGIC             0x504D4449
+#define IDMAP_CURRENT_VERSION   0x00000001
 
 #define APP_PACKAGE_ID      0x7f
 #define SYS_PACKAGE_ID      0x01
@@ -77,6 +79,11 @@
     return (c < 0x0080 && isspace(c));
 }
 
+template<typename T>
+inline static T max(T a, T b) {
+    return a > b ? a : b;
+}
+
 // range checked; guaranteed to NUL-terminate within the stated number of available slots
 // NOTE: if this truncates the dst string due to running out of space, no attempt is
 // made to avoid splitting surrogate pairs.
@@ -215,104 +222,179 @@
     fill9patchOffsets(reinterpret_cast<Res_png_9patch*>(outData));
 }
 
-static bool assertIdmapHeader(const uint32_t* map, size_t sizeBytes)
-{
-    if (sizeBytes < ResTable::IDMAP_HEADER_SIZE_BYTES) {
-        ALOGW("idmap assertion failed: size=%d bytes\n", (int)sizeBytes);
+static bool assertIdmapHeader(const void* idmap, size_t size) {
+    if (reinterpret_cast<uintptr_t>(idmap) & 0x03) {
+        ALOGE("idmap: header is not word aligned");
         return false;
     }
-    if (*map != htodl(IDMAP_MAGIC)) { // htodl: map data expected to be in correct endianess
-        ALOGW("idmap assertion failed: invalid magic found (is 0x%08x, expected 0x%08x)\n",
-             *map, htodl(IDMAP_MAGIC));
+
+    if (size < ResTable::IDMAP_HEADER_SIZE_BYTES) {
+        ALOGW("idmap: header too small (%d bytes)", (uint32_t) size);
+        return false;
+    }
+
+    const uint32_t magic = htodl(*reinterpret_cast<const uint32_t*>(idmap));
+    if (magic != IDMAP_MAGIC) {
+        ALOGW("idmap: no magic found in header (is 0x%08x, expected 0x%08x)",
+             magic, IDMAP_MAGIC);
+        return false;
+    }
+
+    const uint32_t version = htodl(*(reinterpret_cast<const uint32_t*>(idmap) + 1));
+    if (version != IDMAP_CURRENT_VERSION) {
+        // We are strict about versions because files with this format are
+        // auto-generated and don't need backwards compatibility.
+        ALOGW("idmap: version mismatch in header (is 0x%08x, expected 0x%08x)",
+                version, IDMAP_CURRENT_VERSION);
         return false;
     }
     return true;
 }
 
-static status_t idmapLookup(const uint32_t* map, size_t sizeBytes, uint32_t key, uint32_t* outValue)
-{
-    // see README for details on the format of map
-    if (!assertIdmapHeader(map, sizeBytes)) {
-        return UNKNOWN_ERROR;
-    }
-    map = map + IDMAP_HEADER_SIZE; // skip ahead to data segment
-    // size of data block, in uint32_t
-    const size_t size = (sizeBytes - ResTable::IDMAP_HEADER_SIZE_BYTES) / sizeof(uint32_t);
-    const uint32_t type = Res_GETTYPE(key) + 1; // add one, idmap stores "public" type id
-    const uint32_t entry = Res_GETENTRY(key);
-    const uint32_t typeCount = *map;
+class IdmapEntries {
+public:
+    IdmapEntries() : mData(NULL) {}
 
-    if (type > typeCount) {
-        ALOGW("Resource ID map: type=%d exceeds number of types=%d\n", type, typeCount);
-        return UNKNOWN_ERROR;
-    }
-    if (typeCount > size) {
-        ALOGW("Resource ID map: number of types=%d exceeds size of map=%d\n", typeCount, (int)size);
-        return UNKNOWN_ERROR;
-    }
-    const uint32_t typeOffset = map[type];
-    if (typeOffset == 0) {
-        *outValue = 0;
-        return NO_ERROR;
-    }
-    if (typeOffset + 1 > size) {
-        ALOGW("Resource ID map: type offset=%d exceeds reasonable value, size of map=%d\n",
-             typeOffset, (int)size);
-        return UNKNOWN_ERROR;
-    }
-    const uint32_t entryCount = map[typeOffset];
-    const uint32_t entryOffset = map[typeOffset + 1];
-    if (entryCount == 0 || entry < entryOffset || entry - entryOffset > entryCount - 1) {
-        *outValue = 0;
-        return NO_ERROR;
-    }
-    const uint32_t index = typeOffset + 2 + entry - entryOffset;
-    if (index > size) {
-        ALOGW("Resource ID map: entry index=%u exceeds size of map=%d\n", index, (int)size);
-        *outValue = 0;
-        return NO_ERROR;
-    }
-    *outValue = map[index];
+    bool hasEntries() const {
+        if (mData == NULL) {
+            return false;
+        }
 
-    return NO_ERROR;
-}
+        return (dtohs(*mData) > 0);
+    }
 
-static status_t getIdmapPackageId(const uint32_t* map, size_t mapSize, uint32_t *outId)
-{
-    if (!assertIdmapHeader(map, mapSize)) {
-        return UNKNOWN_ERROR;
+    size_t byteSize() const {
+        if (mData == NULL) {
+            return 0;
+        }
+        uint16_t entryCount = dtohs(mData[2]);
+        return (sizeof(uint16_t) * 4) + (sizeof(uint32_t) * static_cast<size_t>(entryCount));
     }
-    if (mapSize <= IDMAP_HEADER_SIZE + 1) {
-        ALOGW("corrupt idmap: map size %d too short\n", (int)mapSize);
-        return UNKNOWN_ERROR;
+
+    uint8_t targetTypeId() const {
+        if (mData == NULL) {
+            return 0;
+        }
+        return dtohs(mData[0]);
     }
-    uint32_t typeCount = *(map + IDMAP_HEADER_SIZE);
-    if (typeCount == 0) {
-        ALOGW("corrupt idmap: no types\n");
-        return UNKNOWN_ERROR;
+
+    uint8_t overlayTypeId() const {
+        if (mData == NULL) {
+            return 0;
+        }
+        return dtohs(mData[1]);
     }
-    if (IDMAP_HEADER_SIZE + 1 + typeCount > mapSize) {
-        ALOGW("corrupt idmap: number of types %u extends past idmap size %d\n", typeCount, (int)mapSize);
-        return UNKNOWN_ERROR;
-    }
-    const uint32_t* p = map + IDMAP_HEADER_SIZE + 1;
-    // find first defined type
-    while (*p == 0) {
-        ++p;
-        if (--typeCount == 0) {
-            ALOGW("corrupt idmap: types declared, none found\n");
+
+    status_t setTo(const void* entryHeader, size_t size) {
+        if (reinterpret_cast<uintptr_t>(entryHeader) & 0x03) {
+            ALOGE("idmap: entry header is not word aligned");
             return UNKNOWN_ERROR;
         }
+
+        if (size < sizeof(uint16_t) * 4) {
+            ALOGE("idmap: entry header is too small (%u bytes)", (uint32_t) size);
+            return UNKNOWN_ERROR;
+        }
+
+        const uint16_t* header = reinterpret_cast<const uint16_t*>(entryHeader);
+        const uint16_t targetTypeId = dtohs(header[0]);
+        const uint16_t overlayTypeId = dtohs(header[1]);
+        if (targetTypeId == 0 || overlayTypeId == 0 || targetTypeId > 255 || overlayTypeId > 255) {
+            ALOGE("idmap: invalid type map (%u -> %u)", targetTypeId, overlayTypeId);
+            return UNKNOWN_ERROR;
+        }
+
+        uint16_t entryCount = dtohs(header[2]);
+        if (size < sizeof(uint32_t) * (entryCount + 2)) {
+            ALOGE("idmap: too small (%u bytes) for the number of entries (%u)",
+                    (uint32_t) size, (uint32_t) entryCount);
+            return UNKNOWN_ERROR;
+        }
+        mData = header;
+        return NO_ERROR;
     }
 
-    // determine package id from first entry of first type
-    const uint32_t offset = *p + IDMAP_HEADER_SIZE + 2;
-    if (offset > mapSize) {
-        ALOGW("corrupt idmap: entry offset %u points outside map size %d\n", offset, (int)mapSize);
+    status_t lookup(uint16_t entryId, uint16_t* outEntryId) const {
+        uint16_t entryCount = dtohs(mData[2]);
+        uint16_t offset = dtohs(mData[3]);
+
+        if (entryId < offset) {
+            // The entry is not present in this idmap
+            return BAD_INDEX;
+        }
+
+        entryId -= offset;
+
+        if (entryId >= entryCount) {
+            // The entry is not present in this idmap
+            return BAD_INDEX;
+        }
+
+        // It is safe to access the type here without checking the size because
+        // we have checked this when it was first loaded.
+        const uint32_t* entries = reinterpret_cast<const uint32_t*>(mData) + 2;
+        uint32_t mappedEntry = dtohl(entries[entryId]);
+        if (mappedEntry == 0xffffffff) {
+            // This entry is not present in this idmap
+            return BAD_INDEX;
+        }
+        *outEntryId = static_cast<uint16_t>(mappedEntry);
+        return NO_ERROR;
+    }
+
+private:
+    const uint16_t* mData;
+};
+
+status_t parseIdmap(const void* idmap, size_t size, uint8_t* outPackageId, KeyedVector<uint8_t, IdmapEntries>* outMap) {
+    if (!assertIdmapHeader(idmap, size)) {
         return UNKNOWN_ERROR;
     }
-    *outId = (map[offset] >> 24) & 0x000000ff;
 
+    size -= ResTable::IDMAP_HEADER_SIZE_BYTES;
+    if (size < sizeof(uint16_t) * 2) {
+        ALOGE("idmap: too small to contain any mapping");
+        return UNKNOWN_ERROR;
+    }
+
+    const uint16_t* data = reinterpret_cast<const uint16_t*>(
+            reinterpret_cast<const uint8_t*>(idmap) + ResTable::IDMAP_HEADER_SIZE_BYTES);
+
+    uint16_t targetPackageId = dtohs(*(data++));
+    if (targetPackageId == 0 || targetPackageId > 255) {
+        ALOGE("idmap: target package ID is invalid (%02x)", targetPackageId);
+        return UNKNOWN_ERROR;
+    }
+
+    uint16_t mapCount = dtohs(*(data++));
+    if (mapCount == 0) {
+        ALOGE("idmap: no mappings");
+        return UNKNOWN_ERROR;
+    }
+
+    if (mapCount > 255) {
+        ALOGW("idmap: too many mappings. Only 255 are possible but %u are present", (uint32_t) mapCount);
+    }
+
+    while (size > sizeof(uint16_t) * 4) {
+        IdmapEntries entries;
+        status_t err = entries.setTo(data, size);
+        if (err != NO_ERROR) {
+            return err;
+        }
+
+        ssize_t index = outMap->add(entries.overlayTypeId(), entries);
+        if (index < 0) {
+            return NO_MEMORY;
+        }
+
+        data += entries.byteSize() / sizeof(uint16_t);
+        size -= entries.byteSize();
+    }
+
+    if (outPackageId != NULL) {
+        *outPackageId = static_cast<uint8_t>(targetPackageId);
+    }
     return NO_ERROR;
 }
 
@@ -2726,7 +2808,7 @@
         free(resourceIDMap);
     }
 
-    ResTable* const                 owner;
+    const ResTable* const           owner;
     void*                           ownedData;
     const ResTable_header*          header;
     size_t                          size;
@@ -2739,6 +2821,17 @@
     size_t                          resourceIDMapSize;
 };
 
+struct ResTable::Entry {
+    ResTable_config config;
+    const ResTable_entry* entry;
+    const ResTable_type* type;
+    uint32_t specFlags;
+    const Package* package;
+
+    StringPoolRef typeStr;
+    StringPoolRef keyStr;
+};
+
 struct ResTable::Type
 {
     Type(const Header* _header, const Package* _package, size_t count)
@@ -2749,33 +2842,29 @@
     const size_t                    entryCount;
     const ResTable_typeSpec*        typeSpec;
     const uint32_t*                 typeSpecFlags;
+    IdmapEntries                    idmapEntries;
     Vector<const ResTable_type*>    configs;
 };
 
 struct ResTable::Package
 {
     Package(ResTable* _owner, const Header* _header, const ResTable_package* _package)
-        : owner(_owner), header(_header), package(_package) { }
-    ~Package()
-    {
-        size_t i = types.size();
-        while (i > 0) {
-            i--;
-            delete types[i];
+        : owner(_owner), header(_header), package(_package), typeIdOffset(0) {
+        if (dtohs(package->header.headerSize) == sizeof(package)) {
+            // The package structure is the same size as the definition.
+            // This means it contains the typeIdOffset field.
+            typeIdOffset = package->typeIdOffset;
         }
     }
 
-    ResTable* const                 owner;
+    const ResTable* const           owner;
     const Header* const             header;
     const ResTable_package* const   package;
-    Vector<Type*>                   types;
 
     ResStringPool                   typeStrings;
     ResStringPool                   keyStrings;
 
-    const Type* getType(size_t idx) const {
-        return idx < types.size() ? types[idx] : NULL;
-    }
+    size_t                          typeIdOffset;
 };
 
 // A group of objects describing a particular resource package.
@@ -2787,13 +2876,24 @@
         : owner(_owner)
         , name(_name)
         , id(_id)
-        , typeCount(0)
+        , largestTypeId(0)
         , bags(NULL)
         , dynamicRefTable(static_cast<uint8_t>(_id))
     { }
 
     ~PackageGroup() {
         clearBagCache();
+        const size_t numTypes = types.size();
+        for (size_t i = 0; i < numTypes; i++) {
+            const TypeList& typeList = types[i];
+            const size_t numInnerTypes = typeList.size();
+            for (size_t j = 0; j < numInnerTypes; j++) {
+                if (typeList[j]->package->owner == owner) {
+                    delete typeList[j];
+                }
+            }
+        }
+
         const size_t N = packages.size();
         for (size_t i=0; i<N; i++) {
             Package* pkg = packages[i];
@@ -2806,17 +2906,15 @@
     void clearBagCache() {
         if (bags) {
             TABLE_NOISY(printf("bags=%p\n", bags));
-            Package* pkg = packages[0];
-            TABLE_NOISY(printf("typeCount=%x\n", typeCount));
-            for (size_t i=0; i<typeCount; i++) {
+            for (size_t i = 0; i < bags->size(); i++) {
                 TABLE_NOISY(printf("type=%d\n", i));
-                const Type* type = pkg->getType(i);
-                if (type != NULL) {
-                    bag_set** typeBags = bags[i];
+                const TypeList& typeList = types[i];
+                if (typeList.isEmpty()) {
+                    bag_set** typeBags = bags->get(i);
                     TABLE_NOISY(printf("typeBags=%p\n", typeBags));
                     if (typeBags) {
-                        TABLE_NOISY(printf("type->entryCount=%x\n", type->entryCount));
-                        const size_t N = type->entryCount;
+                        const size_t N = typeList[0]->entryCount;
+                        TABLE_NOISY(printf("type->entryCount=%x\n", N));
                         for (size_t j=0; j<N; j++) {
                             if (typeBags[j] && typeBags[j] != (bag_set*)0xFFFFFFFF)
                                 free(typeBags[j]);
@@ -2825,25 +2923,38 @@
                     }
                 }
             }
-            free(bags);
+            delete bags;
             bags = NULL;
         }
     }
 
-    ResTable* const                 owner;
+    ssize_t findType16(const char16_t* type, size_t len) const {
+        const size_t N = packages.size();
+        for (size_t i = 0; i < N; i++) {
+            ssize_t index = packages[i]->typeStrings.indexOfString(type, len);
+            if (index >= 0) {
+                return index + packages[i]->typeIdOffset;
+            }
+        }
+        return -1;
+    }
+
+    const ResTable* const           owner;
     String16 const                  name;
     uint32_t const                  id;
+
+    // This is mainly used to keep track of the loaded packages
+    // and to clean them up properly. Accessing resources happens from
+    // the 'types' array.
     Vector<Package*>                packages;
 
-    // This is for finding typeStrings and other common package stuff.
-    Package*                        basePackage;
+    ByteBucketArray<TypeList>       types;
 
-    // For quick access.
-    size_t                          typeCount;
+    uint8_t                         largestTypeId;
 
     // Computed attribute bags, first indexed by the type and second
     // by the entry in that type.
-    bag_set***                      bags;
+    ByteBucketArray<bag_set**>*     bags;
 
     // The table mapping dynamic references to resolved references for
     // this package group.
@@ -2879,7 +2990,7 @@
 
 void ResTable::Theme::free_package(package_info* pi)
 {
-    for (size_t j=0; j<pi->numTypes; j++) {
+    for (size_t j = 0; j <= Res_MAXTYPE; j++) {
         theme_entry* te = pi->types[j].entries;
         if (te != NULL) {
             free(te);
@@ -2890,10 +3001,8 @@
 
 ResTable::Theme::package_info* ResTable::Theme::copy_package(package_info* pi)
 {
-    package_info* newpi = (package_info*)malloc(
-        sizeof(package_info) + (pi->numTypes*sizeof(type_info)));
-    newpi->numTypes = pi->numTypes;
-    for (size_t j=0; j<newpi->numTypes; j++) {
+    package_info* newpi = (package_info*)malloc(sizeof(package_info));
+    for (size_t j = 0; j <= Res_MAXTYPE; j++) {
         size_t cnt = pi->types[j].numEntries;
         newpi->types[j].numEntries = cnt;
         theme_entry* te = pi->types[j].entries;
@@ -2946,17 +3055,14 @@
             curPI = mPackages[pidx];
             if (curPI == NULL) {
                 PackageGroup* const grp = mTable.mPackageGroups[pidx];
-                int cnt = grp->typeCount;
-                curPI = (package_info*)malloc(
-                    sizeof(package_info) + (cnt*sizeof(type_info)));
-                curPI->numTypes = cnt;
-                memset(curPI->types, 0, cnt*sizeof(type_info));
+                curPI = (package_info*)malloc(sizeof(package_info));
+                memset(curPI, 0, sizeof(*curPI));
                 mPackages[pidx] = curPI;
             }
             curType = 0xffffffff;
         }
         if (curType != t) {
-            if (t >= curPI->numTypes) {
+            if (t > Res_MAXTYPE) {
                 ALOGE("Style contains key with bad type: 0x%08x\n", attrRes);
                 bag++;
                 continue;
@@ -2965,8 +3071,8 @@
             curEntries = curPI->types[t].entries;
             if (curEntries == NULL) {
                 PackageGroup* const grp = mTable.mPackageGroups[curPackageIndex];
-                const Type* type = grp->packages[0]->getType(t);
-                int cnt = type != NULL ? type->entryCount : 0;
+                const TypeList& typeList = grp->types[t];
+                int cnt = typeList.isEmpty() ? 0 : typeList[0]->entryCount;
                 curEntries = (theme_entry*)malloc(cnt*sizeof(theme_entry));
                 memset(curEntries, Res_value::TYPE_NULL, cnt*sizeof(theme_entry));
                 curPI->types[t].numEntries = cnt;
@@ -2981,8 +3087,8 @@
         }
         theme_entry* curEntry = curEntries + e;
         TABLE_NOISY(ALOGV("Attr 0x%08x: type=0x%x, data=0x%08x; curType=0x%x",
-                   attrRes, bag->map.value.dataType, bag->map.value.data,
-             curEntry->value.dataType));
+                attrRes, bag->map.value.dataType, bag->map.value.data,
+                curEntry->value.dataType));
         if (force || curEntry->value.dataType == Res_value::TYPE_NULL) {
             curEntry->stringBlock = bag->stringBlock;
             curEntry->typeSpecFlags |= bagTypeSpecFlags;
@@ -3057,8 +3163,8 @@
             const package_info* const pi = mPackages[p];
             TABLE_THEME(ALOGI("Found package: %p", pi));
             if (pi != NULL) {
-                TABLE_THEME(ALOGI("Desired type index is %ld in avail %d", t, pi->numTypes));
-                if (t < pi->numTypes) {
+                TABLE_THEME(ALOGI("Desired type index is %ld in avail %d", t, Res_MAXTYPE + 1));
+                if (t <= Res_MAXTYPE) {
                     const type_info& ti = pi->types[t];
                     TABLE_THEME(ALOGI("Desired entry index is %ld in avail %d", e, ti.numEntries));
                     if (e < ti.numEntries) {
@@ -3120,14 +3226,13 @@
         package_info* pi = mPackages[i];
         if (pi == NULL) continue;
 
-        ALOGI("  Package #0x%02x:\n", (int)(i+1));
-        for (size_t j=0; j<pi->numTypes; j++) {
+        ALOGI("  Package #0x%02x:\n", (int)(i + 1));
+        for (size_t j = 0; j <= Res_MAXTYPE; j++) {
             type_info& ti = pi->types[j];
             if (ti.numEntries == 0) continue;
-
-            ALOGI("    Type #0x%02x:\n", (int)(j+1));
-            for (size_t k=0; k<ti.numEntries; k++) {
-                theme_entry& te = ti.entries[k];
+            ALOGI("    Type #0x%02x:\n", (int)(j + 1));
+            for (size_t k = 0; k < ti.numEntries; k++) {
+                const theme_entry& te = ti.entries[k];
                 if (te.value.dataType == Res_value::TYPE_NULL) continue;
                 ALOGI("      0x%08x: t=0x%x, d=0x%08x (block=%d)\n",
                      (int)Res_MAKEID(i, j, k),
@@ -3150,7 +3255,7 @@
 {
     memset(&mParams, 0, sizeof(mParams));
     memset(mPackageMap, 0, sizeof(mPackageMap));
-    addInternal(data, size, cookie, copyData, NULL /* idMap */);
+    addInternal(data, size, NULL, 0, cookie, copyData);
     LOG_FATAL_IF(mError != NO_ERROR, "Error parsing resource table");
     //ALOGI("Creating ResTable %p\n", this);
 }
@@ -3166,21 +3271,45 @@
     return ((ssize_t)mPackageMap[Res_GETPACKAGE(resID)+1])-1;
 }
 
-status_t ResTable::add(const void* data, size_t size) {
-    return addInternal(data, size, 0 /* cookie */,
-            false /* copyData */, NULL /* idMap */);
+status_t ResTable::add(const void* data, size_t size, const int32_t cookie, bool copyData) {
+    return addInternal(data, size, NULL, 0, cookie, copyData);
 }
 
-status_t ResTable::add(Asset* asset, const int32_t cookie, bool copyData, const void* idmap)
-{
+status_t ResTable::add(const void* data, size_t size, const void* idmapData, size_t idmapDataSize,
+        const int32_t cookie, bool copyData) {
+    return addInternal(data, size, idmapData, idmapDataSize, cookie, copyData);
+}
+
+status_t ResTable::add(Asset* asset, const int32_t cookie, bool copyData) {
     const void* data = asset->getBuffer(true);
     if (data == NULL) {
         ALOGW("Unable to get buffer of resource asset file");
         return UNKNOWN_ERROR;
     }
-    size_t size = (size_t)asset->getLength();
-    return addInternal(data, size, cookie, copyData,
-            reinterpret_cast<const Asset*>(idmap));
+
+    return addInternal(data, static_cast<size_t>(asset->getLength()), NULL, 0, cookie, copyData);
+}
+
+status_t ResTable::add(Asset* asset, Asset* idmapAsset, const int32_t cookie, bool copyData) {
+    const void* data = asset->getBuffer(true);
+    if (data == NULL) {
+        ALOGW("Unable to get buffer of resource asset file");
+        return UNKNOWN_ERROR;
+    }
+
+    size_t idmapSize = 0;
+    const void* idmapData = NULL;
+    if (idmapAsset != NULL) {
+        idmapData = idmapAsset->getBuffer(true);
+        if (idmapData == NULL) {
+            ALOGW("Unable to get buffer of idmap asset file");
+            return UNKNOWN_ERROR;
+        }
+        idmapSize = static_cast<size_t>(idmapAsset->getLength());
+    }
+
+    return addInternal(data, static_cast<size_t>(asset->getLength()),
+            idmapData, idmapSize, cookie, copyData);
 }
 
 status_t ResTable::add(ResTable* src)
@@ -3197,8 +3326,16 @@
         for (size_t j=0; j<srcPg->packages.size(); j++) {
             pg->packages.add(srcPg->packages[j]);
         }
-        pg->basePackage = srcPg->basePackage;
-        pg->typeCount = srcPg->typeCount;
+
+        for (size_t j = 0; j < srcPg->types.size(); j++) {
+            if (srcPg->types[j].isEmpty()) {
+                continue;
+            }
+
+            TypeList& typeList = pg->types.editItemAt(j);
+            typeList.appendVector(srcPg->types[j]);
+        }
+        pg->largestTypeId = max(pg->largestTypeId, srcPg->largestTypeId);
         mPackageGroups.add(pg);
     }
 
@@ -3224,38 +3361,39 @@
     return (mError=NO_ERROR);
 }
 
-status_t ResTable::addInternal(const void* data, size_t size, const int32_t cookie,
-                       bool copyData, const Asset* idmap)
+status_t ResTable::addInternal(const void* data, size_t dataSize, const void* idmapData, size_t idmapDataSize,
+        const int32_t cookie, bool copyData)
 {
-    if (!data) return NO_ERROR;
+    if (!data) {
+        return NO_ERROR;
+    }
+
     Header* header = new Header(this);
     header->index = mHeaders.size();
     header->cookie = cookie;
-    if (idmap != NULL) {
-        const size_t idmap_size = idmap->getLength();
-        const void* idmap_data = const_cast<Asset*>(idmap)->getBuffer(true);
-        header->resourceIDMap = (uint32_t*)malloc(idmap_size);
+    if (idmapData != NULL) {
+        header->resourceIDMap = (uint32_t*) malloc(idmapDataSize);
         if (header->resourceIDMap == NULL) {
             delete header;
             return (mError = NO_MEMORY);
         }
-        memcpy((void*)header->resourceIDMap, idmap_data, idmap_size);
-        header->resourceIDMapSize = idmap_size;
+        memcpy(header->resourceIDMap, idmapData, idmapDataSize);
+        header->resourceIDMapSize = idmapDataSize;
     }
     mHeaders.add(header);
 
     const bool notDeviceEndian = htods(0xf0) != 0xf0;
 
     LOAD_TABLE_NOISY(
-        ALOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%d, asset=%p, copy=%d "
-             "idmap=%p\n", data, size, cookie, asset, copyData, idmap));
+        ALOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%d, copy=%d "
+             "idmap=%p\n", data, dataSize, cookie, copyData, idmap));
 
     if (copyData || notDeviceEndian) {
-        header->ownedData = malloc(size);
+        header->ownedData = malloc(dataSize);
         if (header->ownedData == NULL) {
             return (mError=NO_MEMORY);
         }
-        memcpy(header->ownedData, data, size);
+        memcpy(header->ownedData, data, dataSize);
         data = header->ownedData;
     }
 
@@ -3265,10 +3403,10 @@
     //     dtohl(header->header->header.size), header->header->header.size);
     LOAD_TABLE_NOISY(ALOGV("Loading ResTable @%p:\n", header->header));
     if (dtohs(header->header->header.headerSize) > header->size
-            || header->size > size) {
+            || header->size > dataSize) {
         ALOGW("Bad resource table: header size 0x%x or total size 0x%x is larger than data size 0x%x\n",
              (int)dtohs(header->header->header.headerSize),
-             (int)header->size, (int)size);
+             (int)header->size, (int)dataSize);
         return (mError=BAD_TYPE);
     }
     if (((dtohs(header->header->header.headerSize)|header->size)&0x3) != 0) {
@@ -3313,16 +3451,8 @@
                      dtohl(header->header->packageCount));
                 return (mError=BAD_TYPE);
             }
-            uint32_t idmap_id = 0;
-            if (idmap != NULL) {
-                uint32_t tmp;
-                if (getIdmapPackageId(header->resourceIDMap,
-                                      header->resourceIDMapSize,
-                                      &tmp) == NO_ERROR) {
-                    idmap_id = tmp;
-                }
-            }
-            if (parsePackage((ResTable_package*)chunk, header, idmap_id) != NO_ERROR) {
+
+            if (parsePackage((ResTable_package*)chunk, header) != NO_ERROR) {
                 return mError;
             }
             curPackage++;
@@ -3405,46 +3535,38 @@
         ALOGW("Bad identifier when getting name for resource number 0x%08x", resID);
         return false;
     }
-    if (grp->packages.size() > 0) {
-        const Package* const package = grp->packages[0];
 
-        const ResTable_type* type;
-        const ResTable_entry* entry;
-        ssize_t offset = getEntry(package, t, e, NULL, &type, &entry, NULL);
-        if (offset <= 0) {
-            return false;
-        }
-
-        outName->package = grp->name.string();
-        outName->packageLen = grp->name.size();
-        if (allowUtf8) {
-            outName->type8 = grp->basePackage->typeStrings.string8At(t, &outName->typeLen);
-            outName->name8 = grp->basePackage->keyStrings.string8At(
-                dtohl(entry->key.index), &outName->nameLen);
-        } else {
-            outName->type8 = NULL;
-            outName->name8 = NULL;
-        }
-        if (outName->type8 == NULL) {
-            outName->type = grp->basePackage->typeStrings.stringAt(t, &outName->typeLen);
-            // If we have a bad index for some reason, we should abort.
-            if (outName->type == NULL) {
-                return false;
-            }
-        }
-        if (outName->name8 == NULL) {
-            outName->name = grp->basePackage->keyStrings.stringAt(
-                dtohl(entry->key.index), &outName->nameLen);
-            // If we have a bad index for some reason, we should abort.
-            if (outName->name == NULL) {
-                return false;
-            }
-        }
-
-        return true;
+    Entry entry;
+    status_t err = getEntry(grp, t, e, NULL, &entry);
+    if (err != NO_ERROR) {
+        return false;
     }
 
-    return false;
+    outName->package = grp->name.string();
+    outName->packageLen = grp->name.size();
+    if (allowUtf8) {
+        outName->type8 = entry.typeStr.string8(&outName->typeLen);
+        outName->name8 = entry.keyStr.string8(&outName->nameLen);
+    } else {
+        outName->type8 = NULL;
+        outName->name8 = NULL;
+    }
+    if (outName->type8 == NULL) {
+        outName->type = entry.typeStr.string16(&outName->typeLen);
+        // If we have a bad index for some reason, we should abort.
+        if (outName->type == NULL) {
+            return false;
+        }
+    }
+    if (outName->name8 == NULL) {
+        outName->name = entry.keyStr.string16(&outName->nameLen);
+        // If we have a bad index for some reason, we should abort.
+        if (outName->name == NULL) {
+            return false;
+        }
+    }
+
+    return true;
 }
 
 ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag, uint16_t density,
@@ -3471,15 +3593,6 @@
         return BAD_INDEX;
     }
 
-    const Res_value* bestValue = NULL;
-    const Package* bestPackage = NULL;
-    ResTable_config bestItem;
-    memset(&bestItem, 0, sizeof(bestItem)); // make the compiler shut up
-
-    if (outSpecFlags != NULL) *outSpecFlags = 0;
-
-    // Look through all resource packages, starting with the most
-    // recently added.
     const PackageGroup* const grp = mPackageGroups[p];
     if (grp == NULL) {
         ALOGW("Bad identifier when getting value for resource number 0x%08x", resID);
@@ -3487,142 +3600,62 @@
     }
 
     // Allow overriding density
-    const ResTable_config* desiredConfig = &mParams;
-    ResTable_config* overrideConfig = NULL;
+    ResTable_config desiredConfig = mParams;
     if (density > 0) {
-        overrideConfig = (ResTable_config*) malloc(sizeof(ResTable_config));
-        if (overrideConfig == NULL) {
-            ALOGE("Couldn't malloc ResTable_config for overrides: %s", strerror(errno));
-            return BAD_INDEX;
-        }
-        memcpy(overrideConfig, &mParams, sizeof(ResTable_config));
-        overrideConfig->density = density;
-        desiredConfig = overrideConfig;
+        desiredConfig.density = density;
     }
 
-    ssize_t rc = BAD_VALUE;
-    size_t ip = grp->packages.size();
-    while (ip > 0) {
-        ip--;
-        int T = t;
-        int E = e;
-
-        const Package* const package = grp->packages[ip];
-        if (package->header->resourceIDMap) {
-            uint32_t overlayResID = 0x0;
-            status_t retval = idmapLookup(package->header->resourceIDMap,
-                                          package->header->resourceIDMapSize,
-                                          resID, &overlayResID);
-            if (retval == NO_ERROR && overlayResID != 0x0) {
-                // for this loop iteration, this is the type and entry we really want
-                ALOGV("resource map 0x%08x -> 0x%08x\n", resID, overlayResID);
-                T = Res_GETTYPE(overlayResID);
-                E = Res_GETENTRY(overlayResID);
-            } else {
-                // resource not present in overlay package, continue with the next package
-                continue;
-            }
-        }
-
-        const ResTable_type* type;
-        const ResTable_entry* entry;
-        const Type* typeClass;
-        ssize_t offset = getEntry(package, T, E, desiredConfig, &type, &entry, &typeClass);
-        if (offset <= 0) {
-            // No {entry, appropriate config} pair found in package. If this
-            // package is an overlay package (ip != 0), this simply means the
-            // overlay package did not specify a default.
-            // Non-overlay packages are still required to provide a default.
-            if (offset < 0 && ip == 0) {
-                ALOGW("Failure getting entry for 0x%08x (t=%d e=%d) in package %zd (error %d)\n",
-                        resID, T, E, ip, (int)offset);
-                rc = offset;
-                goto out;
-            }
-            continue;
-        }
-
-        if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) != 0) {
-            if (!mayBeBag) {
-                ALOGW("Requesting resource 0x%x failed because it is complex\n",
-                     resID);
-            }
-            continue;
-        }
-
-        if ((size_t)offset > (dtohl(type->header.size)-sizeof(Res_value))) {
-            ALOGW("ResTable_item at %d is beyond type chunk data %d",
-                 (int)offset, dtohl(type->header.size));
-            rc = BAD_TYPE;
-            goto out;
-        }
-
-        const Res_value* item =
-            (const Res_value*)(((const uint8_t*)type) + offset);
-        ResTable_config thisConfig;
-        thisConfig.copyFromDtoH(type->config);
-
-        if (outSpecFlags != NULL) {
-            if (typeClass->typeSpecFlags != NULL) {
-                *outSpecFlags |= dtohl(typeClass->typeSpecFlags[E]);
-            } else {
-                *outSpecFlags = -1;
-            }
-        }
-
-        if (bestPackage != NULL &&
-            (bestItem.isMoreSpecificThan(thisConfig) || bestItem.diff(thisConfig) == 0)) {
-            // Discard thisConfig not only if bestItem is more specific, but also if the two configs
-            // are identical (diff == 0), or overlay packages will not take effect.
-            continue;
-        }
-
-        bestItem = thisConfig;
-        bestValue = item;
-        bestPackage = package;
+    Entry entry;
+    status_t err = getEntry(grp, t, e, &desiredConfig, &entry);
+    if (err != NO_ERROR) {
+        ALOGW("Failure getting entry for 0x%08x (t=%d e=%d) (error %d)\n",
+                resID, t, e, err);
+        return err;
     }
 
-    TABLE_NOISY(printf("Found result: package %p\n", bestPackage));
-
-    if (bestValue) {
-        outValue->size = dtohs(bestValue->size);
-        outValue->res0 = bestValue->res0;
-        outValue->dataType = bestValue->dataType;
-        outValue->data = dtohl(bestValue->data);
-
-        // The reference may be pointing to a resource in a shared library. These
-        // references have build-time generated package IDs. These ids may not match
-        // the actual package IDs of the corresponding packages in this ResTable.
-        // We need to fix the package ID based on a mapping.
-        status_t err = grp->dynamicRefTable.lookupResourceValue(outValue);
-        if (err != NO_ERROR) {
-            ALOGW("Failed to resolve referenced package: 0x%08x", outValue->data);
-            rc = BAD_VALUE;
-            goto out;
+    if ((dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) != 0) {
+        if (!mayBeBag) {
+            ALOGW("Requesting resource 0x%08x failed because it is complex\n", resID);
         }
-
-        if (outConfig != NULL) {
-            *outConfig = bestItem;
-        }
-        TABLE_NOISY(size_t len;
-              printf("Found value: pkg=%d, type=%d, str=%s, int=%d\n",
-                     bestPackage->header->index,
-                     outValue->dataType,
-                     outValue->dataType == bestValue->TYPE_STRING
-                     ? String8(bestPackage->header->values.stringAt(
-                         outValue->data, &len)).string()
-                     : "",
-                     outValue->data));
-        rc = bestPackage->header->index;
-        goto out;
+        return BAD_VALUE;
     }
 
-out:
-    if (overrideConfig != NULL) {
-        free(overrideConfig);
+    const Res_value* value = reinterpret_cast<const Res_value*>(
+            reinterpret_cast<const uint8_t*>(entry.entry) + entry.entry->size);
+
+    outValue->size = dtohs(value->size);
+    outValue->res0 = value->res0;
+    outValue->dataType = value->dataType;
+    outValue->data = dtohl(value->data);
+
+    // The reference may be pointing to a resource in a shared library. These
+    // references have build-time generated package IDs. These ids may not match
+    // the actual package IDs of the corresponding packages in this ResTable.
+    // We need to fix the package ID based on a mapping.
+    if (grp->dynamicRefTable.lookupResourceValue(outValue) != NO_ERROR) {
+        ALOGW("Failed to resolve referenced package: 0x%08x", outValue->data);
+        return BAD_VALUE;
     }
 
-    return rc;
+    TABLE_NOISY(size_t len;
+          printf("Found value: pkg=%d, type=%d, str=%s, int=%d\n",
+                 entry.package->header->index,
+                 outValue->dataType,
+                 outValue->dataType == Res_value::TYPE_STRING
+                 ? String8(entry.package->header->values.stringAt(
+                     outValue->data, &len)).string()
+                 : "",
+                 outValue->data));
+
+    if (outSpecFlags != NULL) {
+        *outSpecFlags = entry.specFlags;
+    }
+
+    if (outConfig != NULL) {
+        *outConfig = entry.config;
+    }
+
+    return entry.package->header->index;
 }
 
 ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex,
@@ -3721,29 +3754,25 @@
     PackageGroup* const grp = mPackageGroups[p];
     if (grp == NULL) {
         ALOGW("Bad identifier when getting bag for resource number 0x%08x", resID);
-        return false;
-    }
-
-    if (t >= (int)grp->typeCount) {
-        ALOGW("Type identifier 0x%x is larger than type count 0x%x",
-             t+1, (int)grp->typeCount);
         return BAD_INDEX;
     }
 
-    const Package* const basePackage = grp->packages[0];
+    const TypeList& typeConfigs = grp->types[t];
+    if (typeConfigs.isEmpty()) {
+        ALOGW("Type identifier 0x%x does not exist.", t+1);
+        return BAD_INDEX;
+    }
 
-    const Type* const typeConfigs = basePackage->getType(t);
-
-    const size_t NENTRY = typeConfigs->entryCount;
+    const size_t NENTRY = typeConfigs[0]->entryCount;
     if (e >= (int)NENTRY) {
         ALOGW("Entry identifier 0x%x is larger than entry count 0x%x",
-             e, (int)typeConfigs->entryCount);
+             e, (int)typeConfigs[0]->entryCount);
         return BAD_INDEX;
     }
 
     // First see if we've already computed this bag...
     if (grp->bags) {
-        bag_set** typeSet = grp->bags[t];
+        bag_set** typeSet = grp->bags->get(t);
         if (typeSet) {
             bag_set* set = typeSet[e];
             if (set) {
@@ -3764,229 +3793,174 @@
 
     // Bag not found, we need to compute it!
     if (!grp->bags) {
-        grp->bags = (bag_set***)calloc(grp->typeCount, sizeof(bag_set*));
+        grp->bags = new ByteBucketArray<bag_set**>();
         if (!grp->bags) return NO_MEMORY;
     }
 
-    bag_set** typeSet = grp->bags[t];
+    bag_set** typeSet = grp->bags->get(t);
     if (!typeSet) {
         typeSet = (bag_set**)calloc(NENTRY, sizeof(bag_set*));
         if (!typeSet) return NO_MEMORY;
-        grp->bags[t] = typeSet;
+        grp->bags->set(t, typeSet);
     }
 
     // Mark that we are currently working on this one.
     typeSet[e] = (bag_set*)0xFFFFFFFF;
 
+    TABLE_NOISY(ALOGI("Building bag: %p\n", (void*)resID));
+
+    // Now collect all bag attributes
+    Entry entry;
+    status_t err = getEntry(grp, t, e, &mParams, &entry);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    const uint16_t entrySize = dtohs(entry.entry->size);
+    const uint32_t parent = entrySize >= sizeof(ResTable_map_entry)
+        ? dtohl(((const ResTable_map_entry*)entry.entry)->parent.ident) : 0;
+    const uint32_t count = entrySize >= sizeof(ResTable_map_entry)
+        ? dtohl(((const ResTable_map_entry*)entry.entry)->count) : 0;
+
+    size_t N = count;
+
+    TABLE_NOISY(ALOGI("Found map: size=%p parent=%p count=%d\n",
+                     entrySize, parent, count));
+
+    // If this map inherits from another, we need to start
+    // with its parent's values.  Otherwise start out empty.
+    TABLE_NOISY(printf("Creating new bag, entrySize=0x%08x, parent=0x%08x\n",
+                       entrySize, parent));
+
     // This is what we are building.
     bag_set* set = NULL;
 
-    TABLE_NOISY(ALOGI("Building bag: %p\n", (void*)resID));
+    if (parent) {
+        uint32_t resolvedParent = parent;
 
-    ResTable_config bestConfig;
-    memset(&bestConfig, 0, sizeof(bestConfig));
-
-    // Now collect all bag attributes from all packages.
-    size_t ip = grp->packages.size();
-    while (ip > 0) {
-        ip--;
-        int T = t;
-        int E = e;
-
-        const Package* const package = grp->packages[ip];
-        if (package->header->resourceIDMap) {
-            uint32_t overlayResID = 0x0;
-            status_t retval = idmapLookup(package->header->resourceIDMap,
-                                          package->header->resourceIDMapSize,
-                                          resID, &overlayResID);
-            if (retval == NO_ERROR && overlayResID != 0x0) {
-                // for this loop iteration, this is the type and entry we really want
-                ALOGV("resource map 0x%08x -> 0x%08x\n", resID, overlayResID);
-                T = Res_GETTYPE(overlayResID);
-                E = Res_GETENTRY(overlayResID);
-            } else {
-                // resource not present in overlay package, continue with the next package
-                continue;
-            }
+        // Bags encode a parent reference without using the standard
+        // Res_value structure. That means we must always try to
+        // resolve a parent reference in case it is actually a
+        // TYPE_DYNAMIC_REFERENCE.
+        status_t err = grp->dynamicRefTable.lookupResourceId(&resolvedParent);
+        if (err != NO_ERROR) {
+            ALOGE("Failed resolving bag parent id 0x%08x", parent);
+            return UNKNOWN_ERROR;
         }
 
-        const ResTable_type* type;
-        const ResTable_entry* entry;
-        const Type* typeClass;
-        ALOGV("Getting entry pkg=%p, t=%d, e=%d\n", package, T, E);
-        ssize_t offset = getEntry(package, T, E, &mParams, &type, &entry, &typeClass);
-        ALOGV("Resulting offset=%d\n", (int)offset);
-        if (offset <= 0) {
-            // No {entry, appropriate config} pair found in package. If this
-            // package is an overlay package (ip != 0), this simply means the
-            // overlay package did not specify a default.
-            // Non-overlay packages are still required to provide a default.
-            if (offset < 0 && ip == 0) {
-                if (set) free(set);
-                return offset;
-            }
-            continue;
+        const bag_entry* parentBag;
+        uint32_t parentTypeSpecFlags = 0;
+        const ssize_t NP = getBagLocked(resolvedParent, &parentBag, &parentTypeSpecFlags);
+        const size_t NT = ((NP >= 0) ? NP : 0) + N;
+        set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*NT);
+        if (set == NULL) {
+            return NO_MEMORY;
         }
-
-        if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) == 0) {
-            ALOGW("Skipping entry 0x%x in package table %zu because it is not complex!\n",
-                 resID, ip);
-            continue;
-        }
-
-        if (set != NULL && !type->config.isBetterThan(bestConfig, NULL)) {
-            continue;
-        }
-        bestConfig = type->config;
-        if (set) {
-            free(set);
-            set = NULL;
-        }
-
-        const uint16_t entrySize = dtohs(entry->size);
-        const uint32_t parent = entrySize >= sizeof(ResTable_map_entry)
-            ? dtohl(((const ResTable_map_entry*)entry)->parent.ident) : 0;
-        const uint32_t count = entrySize >= sizeof(ResTable_map_entry)
-            ? dtohl(((const ResTable_map_entry*)entry)->count) : 0;
-
-        size_t N = count;
-
-        TABLE_NOISY(ALOGI("Found map: size=%p parent=%p count=%d\n",
-                         entrySize, parent, count));
-
-        // If this map inherits from another, we need to start
-        // with its parent's values.  Otherwise start out empty.
-        TABLE_NOISY(printf("Creating new bag, entrySize=0x%08x, parent=0x%08x\n",
-                           entrySize, parent));
-        if (parent) {
-            uint32_t resolvedParent = parent;
-
-            // Bags encode a parent reference without using the standard
-            // Res_value structure. That means we must always try to
-            // resolve a parent reference in case it is actually a
-            // TYPE_DYNAMIC_REFERENCE.
-            status_t err = grp->dynamicRefTable.lookupResourceId(&resolvedParent);
-            if (err != NO_ERROR) {
-                ALOGE("Failed resolving bag parent id 0x%08x", parent);
-                return UNKNOWN_ERROR;
-            }
-
-            const bag_entry* parentBag;
-            uint32_t parentTypeSpecFlags = 0;
-            const ssize_t NP = getBagLocked(resolvedParent, &parentBag, &parentTypeSpecFlags);
-            const size_t NT = ((NP >= 0) ? NP : 0) + N;
-            set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*NT);
-            if (set == NULL) {
-                return NO_MEMORY;
-            }
-            if (NP > 0) {
-                memcpy(set+1, parentBag, NP*sizeof(bag_entry));
-                set->numAttrs = NP;
-                TABLE_NOISY(ALOGI("Initialized new bag with %d inherited attributes.\n", NP));
-            } else {
-                TABLE_NOISY(ALOGI("Initialized new bag with no inherited attributes.\n"));
-                set->numAttrs = 0;
-            }
-            set->availAttrs = NT;
-            set->typeSpecFlags = parentTypeSpecFlags;
+        if (NP > 0) {
+            memcpy(set+1, parentBag, NP*sizeof(bag_entry));
+            set->numAttrs = NP;
+            TABLE_NOISY(ALOGI("Initialized new bag with %d inherited attributes.\n", NP));
         } else {
-            set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*N);
-            if (set == NULL) {
-                return NO_MEMORY;
-            }
+            TABLE_NOISY(ALOGI("Initialized new bag with no inherited attributes.\n"));
             set->numAttrs = 0;
-            set->availAttrs = N;
-            set->typeSpecFlags = 0;
         }
-
-        if (typeClass->typeSpecFlags != NULL) {
-            set->typeSpecFlags |= dtohl(typeClass->typeSpecFlags[E]);
-        } else {
-            set->typeSpecFlags = -1;
+        set->availAttrs = NT;
+        set->typeSpecFlags = parentTypeSpecFlags;
+    } else {
+        set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*N);
+        if (set == NULL) {
+            return NO_MEMORY;
         }
+        set->numAttrs = 0;
+        set->availAttrs = N;
+        set->typeSpecFlags = 0;
+    }
 
-        // Now merge in the new attributes...
-        ssize_t curOff = offset;
-        const ResTable_map* map;
-        bag_entry* entries = (bag_entry*)(set+1);
-        size_t curEntry = 0;
-        uint32_t pos = 0;
-        TABLE_NOISY(ALOGI("Starting with set %p, entries=%p, avail=%d\n",
-                     set, entries, set->availAttrs));
-        while (pos < count) {
-            TABLE_NOISY(printf("Now at %p\n", (void*)curOff));
+    set->typeSpecFlags |= entry.specFlags;
 
-            if ((size_t)curOff > (dtohl(type->header.size)-sizeof(ResTable_map))) {
-                ALOGW("ResTable_map at %d is beyond type chunk data %d",
-                     (int)curOff, dtohl(type->header.size));
-                return BAD_TYPE;
-            }
-            map = (const ResTable_map*)(((const uint8_t*)type) + curOff);
-            N++;
+    // Now merge in the new attributes...
+    size_t curOff = (reinterpret_cast<uintptr_t>(entry.entry) - reinterpret_cast<uintptr_t>(entry.type))
+        + dtohs(entry.entry->size);
+    const ResTable_map* map;
+    bag_entry* entries = (bag_entry*)(set+1);
+    size_t curEntry = 0;
+    uint32_t pos = 0;
+    TABLE_NOISY(ALOGI("Starting with set %p, entries=%p, avail=%d\n",
+                 set, entries, set->availAttrs));
+    while (pos < count) {
+        TABLE_NOISY(printf("Now at %p\n", (void*)curOff));
 
-            const uint32_t newName = htodl(map->name.ident);
-            bool isInside;
-            uint32_t oldName = 0;
-            while ((isInside=(curEntry < set->numAttrs))
-                    && (oldName=entries[curEntry].map.name.ident) < newName) {
-                TABLE_NOISY(printf("#%d: Keeping existing attribute: 0x%08x\n",
-                             curEntry, entries[curEntry].map.name.ident));
-                curEntry++;
-            }
+        if (curOff > (dtohl(entry.type->header.size)-sizeof(ResTable_map))) {
+            ALOGW("ResTable_map at %d is beyond type chunk data %d",
+                 (int)curOff, dtohl(entry.type->header.size));
+            return BAD_TYPE;
+        }
+        map = (const ResTable_map*)(((const uint8_t*)entry.type) + curOff);
+        N++;
 
-            if ((!isInside) || oldName != newName) {
-                // This is a new attribute...  figure out what to do with it.
-                if (set->numAttrs >= set->availAttrs) {
-                    // Need to alloc more memory...
-                    const size_t newAvail = set->availAttrs+N;
-                    set = (bag_set*)realloc(set,
-                                            sizeof(bag_set)
-                                            + sizeof(bag_entry)*newAvail);
-                    if (set == NULL) {
-                        return NO_MEMORY;
-                    }
-                    set->availAttrs = newAvail;
-                    entries = (bag_entry*)(set+1);
-                    TABLE_NOISY(printf("Reallocated set %p, entries=%p, avail=%d\n",
-                                 set, entries, set->availAttrs));
-                }
-                if (isInside) {
-                    // Going in the middle, need to make space.
-                    memmove(entries+curEntry+1, entries+curEntry,
-                            sizeof(bag_entry)*(set->numAttrs-curEntry));
-                    set->numAttrs++;
-                }
-                TABLE_NOISY(printf("#%d: Inserting new attribute: 0x%08x\n",
-                             curEntry, newName));
-            } else {
-                TABLE_NOISY(printf("#%d: Replacing existing attribute: 0x%08x\n",
-                             curEntry, oldName));
-            }
-
-            bag_entry* cur = entries+curEntry;
-
-            cur->stringBlock = package->header->index;
-            cur->map.name.ident = newName;
-            cur->map.value.copyFrom_dtoh(map->value);
-            status_t err = grp->dynamicRefTable.lookupResourceValue(&cur->map.value);
-            if (err != NO_ERROR) {
-                ALOGE("Reference item(0x%08x) in bag could not be resolved.", cur->map.value.data);
-                return UNKNOWN_ERROR;
-            }
-
-            TABLE_NOISY(printf("Setting entry #%d %p: block=%d, name=0x%08x, type=%d, data=0x%08x\n",
-                         curEntry, cur, cur->stringBlock, cur->map.name.ident,
-                         cur->map.value.dataType, cur->map.value.data));
-
-            // On to the next!
+        const uint32_t newName = htodl(map->name.ident);
+        bool isInside;
+        uint32_t oldName = 0;
+        while ((isInside=(curEntry < set->numAttrs))
+                && (oldName=entries[curEntry].map.name.ident) < newName) {
+            TABLE_NOISY(printf("#%d: Keeping existing attribute: 0x%08x\n",
+                         curEntry, entries[curEntry].map.name.ident));
             curEntry++;
-            pos++;
-            const size_t size = dtohs(map->value.size);
-            curOff += size + sizeof(*map)-sizeof(map->value);
-        };
-        if (curEntry > set->numAttrs) {
-            set->numAttrs = curEntry;
         }
+
+        if ((!isInside) || oldName != newName) {
+            // This is a new attribute...  figure out what to do with it.
+            if (set->numAttrs >= set->availAttrs) {
+                // Need to alloc more memory...
+                const size_t newAvail = set->availAttrs+N;
+                set = (bag_set*)realloc(set,
+                                        sizeof(bag_set)
+                                        + sizeof(bag_entry)*newAvail);
+                if (set == NULL) {
+                    return NO_MEMORY;
+                }
+                set->availAttrs = newAvail;
+                entries = (bag_entry*)(set+1);
+                TABLE_NOISY(printf("Reallocated set %p, entries=%p, avail=%d\n",
+                             set, entries, set->availAttrs));
+            }
+            if (isInside) {
+                // Going in the middle, need to make space.
+                memmove(entries+curEntry+1, entries+curEntry,
+                        sizeof(bag_entry)*(set->numAttrs-curEntry));
+                set->numAttrs++;
+            }
+            TABLE_NOISY(printf("#%d: Inserting new attribute: 0x%08x\n",
+                         curEntry, newName));
+        } else {
+            TABLE_NOISY(printf("#%d: Replacing existing attribute: 0x%08x\n",
+                         curEntry, oldName));
+        }
+
+        bag_entry* cur = entries+curEntry;
+
+        cur->stringBlock = entry.package->header->index;
+        cur->map.name.ident = newName;
+        cur->map.value.copyFrom_dtoh(map->value);
+        status_t err = grp->dynamicRefTable.lookupResourceValue(&cur->map.value);
+        if (err != NO_ERROR) {
+            ALOGE("Reference item(0x%08x) in bag could not be resolved.", cur->map.value.data);
+            return UNKNOWN_ERROR;
+        }
+
+        TABLE_NOISY(printf("Setting entry #%d %p: block=%d, name=0x%08x, type=%d, data=0x%08x\n",
+                     curEntry, cur, cur->stringBlock, cur->map.name.ident,
+                     cur->map.value.dataType, cur->map.value.data));
+
+        // On to the next!
+        curEntry++;
+        pos++;
+        const size_t size = dtohs(map->value.size);
+        curOff += size + sizeof(*map)-sizeof(map->value);
+    };
+
+    if (curEntry > set->numAttrs) {
+        set->numAttrs = curEntry;
     }
 
     // And this is it...
@@ -4154,80 +4128,63 @@
             continue;
         }
 
-        const ssize_t ti = group->basePackage->typeStrings.indexOfString(type, typeLen);
+        const ssize_t ti = group->findType16(type, typeLen);
         if (ti < 0) {
             TABLE_NOISY(printf("Type not found in package %s\n", String8(group->name).string()));
             continue;
         }
 
-        const ssize_t ei = group->basePackage->keyStrings.indexOfString(name, nameLen);
-        if (ei < 0) {
-            TABLE_NOISY(printf("Name not found in package %s\n", String8(group->name).string()));
+        const TypeList& typeList = group->types[ti];
+        if (typeList.isEmpty()) {
+            TABLE_NOISY(printf("Expected type structure not found in package %s for index %d\n",
+                               String8(group->name).string(), ti));
             continue;
         }
 
-        TABLE_NOISY(printf("Search indices: type=%d, name=%d\n", ti, ei));
+        const size_t typeCount = typeList.size();
+        for (size_t i = 0; i < typeCount; i++) {
+            const Type* t = typeList[i];
+            const ssize_t ei = t->package->keyStrings.indexOfString(name, nameLen);
+            if (ei < 0) {
+                continue;
+            }
 
-        const Type* const typeConfigs = group->packages[0]->getType(ti);
-        if (typeConfigs == NULL || typeConfigs->configs.size() <= 0) {
-            TABLE_NOISY(printf("Expected type structure not found in package %s for idnex %d\n",
-                               String8(group->name).string(), ti));
-        }
-
-        size_t NTC = typeConfigs->configs.size();
-        for (size_t tci=0; tci<NTC; tci++) {
-            const ResTable_type* const ty = typeConfigs->configs[tci];
-            const uint32_t typeOffset = dtohl(ty->entriesStart);
-
-            const uint8_t* const end = ((const uint8_t*)ty) + dtohl(ty->header.size);
-            const uint32_t* const eindex = (const uint32_t*)
-                (((const uint8_t*)ty) + dtohs(ty->header.headerSize));
-
-            const size_t NE = dtohl(ty->entryCount);
-            for (size_t i=0; i<NE; i++) {
-                uint32_t offset = dtohl(eindex[i]);
-                if (offset == ResTable_type::NO_ENTRY) {
-                    continue;
-                }
-
-                offset += typeOffset;
-
-                if (offset > (dtohl(ty->header.size)-sizeof(ResTable_entry))) {
-                    ALOGW("ResTable_entry at %d is beyond type chunk data %d",
-                         offset, dtohl(ty->header.size));
-                    return 0;
-                }
-                if ((offset&0x3) != 0) {
-                    ALOGW("ResTable_entry at %d (pkg=%d type=%d ent=%d) is not on an integer boundary when looking for %s:%s/%s",
-                         (int)offset, (int)group->id, (int)ti+1, (int)i,
-                         String8(package, packageLen).string(),
-                         String8(type, typeLen).string(),
-                         String8(name, nameLen).string());
-                    return 0;
-                }
-
-                const ResTable_entry* const entry = (const ResTable_entry*)
-                    (((const uint8_t*)ty) + offset);
-                if (dtohs(entry->size) < sizeof(*entry)) {
-                    ALOGW("ResTable_entry size %d is too small", dtohs(entry->size));
-                    return BAD_TYPE;
-                }
-
-                TABLE_SUPER_NOISY(printf("Looking at entry #%d: want str %d, have %d\n",
-                                         i, ei, dtohl(entry->key.index)));
-                if (dtohl(entry->key.index) == (size_t)ei) {
-                    if (outTypeSpecFlags) {
-                        *outTypeSpecFlags = typeConfigs->typeSpecFlags[i];
-                        if (fakePublic) {
-                            *outTypeSpecFlags |= ResTable_typeSpec::SPEC_PUBLIC;
-                        }
+            const size_t configCount = t->configs.size();
+            for (size_t j = 0; j < configCount; j++) {
+                const TypeVariant tv(t->configs[j]);
+                for (TypeVariant::iterator iter = tv.beginEntries();
+                     iter != tv.endEntries();
+                     iter++) {
+                    const ResTable_entry* entry = *iter;
+                    if (entry == NULL) {
+                        continue;
                     }
-                    return Res_MAKEID(group->id-1, ti, i);
+
+                    if (dtohl(entry->key.index) == (size_t) ei) {
+                        uint32_t resId = Res_MAKEID(group->id - 1, ti, iter.index());
+                        if (outTypeSpecFlags) {
+                            Entry result;
+                            if (getEntry(group, ti, iter.index(), NULL, &result) != NO_ERROR) {
+                                ALOGW("Failed to find spec flags for %s:%s/%s (0x%08x)",
+                                        String8(group->name).string(),
+                                        String8(String16(type, typeLen)).string(),
+                                        String8(String16(name, nameLen)).string(),
+                                        resId);
+                                return 0;
+                            }
+                            *outTypeSpecFlags = result.specFlags;
+
+                            if (fakePublic) {
+                                *outTypeSpecFlags |= ResTable_typeSpec::SPEC_PUBLIC;
+                            }
+                        }
+                        return resId;
+                    }
                 }
             }
         }
+        break;
     }
-
     return 0;
 }
 
@@ -5260,6 +5217,18 @@
     return mPackageGroups[idx]->id;
 }
 
+uint32_t ResTable::getLastTypeIdForPackage(size_t idx) const
+{
+    if (mError != NO_ERROR) {
+        return 0;
+    }
+    LOG_FATAL_IF(idx >= mPackageGroups.size(),
+            "Requested package index %d past package count %d",
+            (int)idx, (int)mPackageGroups.size());
+    const PackageGroup* const group = mPackageGroups[idx];
+    return group->largestTypeId;
+}
+
 size_t ResTable::getTableCount() const
 {
     return mHeaders.size();
@@ -5292,32 +5261,31 @@
 
 void ResTable::getConfigurations(Vector<ResTable_config>* configs) const
 {
-    const size_t I = mPackageGroups.size();
-    for (size_t i=0; i<I; i++) {
+    const size_t packageCount = mPackageGroups.size();
+    for (size_t i = 0; i < packageCount; i++) {
         const PackageGroup* packageGroup = mPackageGroups[i];
-        const size_t J = packageGroup->packages.size();
-        for (size_t j=0; j<J; j++) {
-            const Package* package = packageGroup->packages[j];
-            const size_t K = package->types.size();
-            for (size_t k=0; k<K; k++) {
-                const Type* type = package->types[k];
-                if (type == NULL) continue;
-                const size_t L = type->configs.size();
-                for (size_t l=0; l<L; l++) {
-                    const ResTable_type* config = type->configs[l];
+        const size_t typeCount = packageGroup->types.size();
+        for (size_t j = 0; j < typeCount; j++) {
+            const TypeList& typeList = packageGroup->types[j];
+            const size_t numTypes = typeList.size();
+            for (size_t k = 0; k < numTypes; k++) {
+                const Type* type = typeList[k];
+                const size_t numConfigs = type->configs.size();
+                for (size_t m = 0; m < numConfigs; m++) {
+                    const ResTable_type* config = type->configs[m];
                     ResTable_config cfg;
                     memset(&cfg, 0, sizeof(ResTable_config));
                     cfg.copyFromDtoH(config->config);
                     // only insert unique
-                    const size_t M = configs->size();
-                    size_t m;
-                    for (m=0; m<M; m++) {
-                        if (0 == (*configs)[m].compare(cfg)) {
+                    const size_t N = configs->size();
+                    size_t n;
+                    for (n = 0; n < N; n++) {
+                        if (0 == (*configs)[n].compare(cfg)) {
                             break;
                         }
                     }
                     // if we didn't find it
-                    if (m == M) {
+                    if (n == N) {
                         configs->add(cfg);
                     }
                 }
@@ -5350,122 +5318,180 @@
     }
 }
 
-ssize_t ResTable::getEntry(
-    const Package* package, int typeIndex, int entryIndex,
-    const ResTable_config* config,
-    const ResTable_type** outType, const ResTable_entry** outEntry,
-    const Type** outTypeClass) const
-{
-    ALOGV("Getting entry from package %p\n", package);
-    const ResTable_package* const pkg = package->package;
+StringPoolRef::StringPoolRef(const ResStringPool* pool, uint32_t index)
+    : mPool(pool), mIndex(index) {}
 
-    const Type* allTypes = package->getType(typeIndex);
-    ALOGV("allTypes=%p\n", allTypes);
-    if (allTypes == NULL) {
-        ALOGV("Skipping entry type index 0x%02x because type is NULL!\n", typeIndex);
-        return 0;
+StringPoolRef::StringPoolRef()
+    : mPool(NULL), mIndex(0) {}
+
+const char* StringPoolRef::string8(size_t* outLen) const {
+    if (mPool != NULL) {
+        return mPool->string8At(mIndex, outLen);
     }
+    if (outLen != NULL) {
+        *outLen = 0;
+    }
+    return NULL;
+}
 
-    if ((size_t)entryIndex >= allTypes->entryCount) {
-        ALOGW("getEntry failing because entryIndex %d is beyond type entryCount %d",
-            entryIndex, (int)allTypes->entryCount);
+const char16_t* StringPoolRef::string16(size_t* outLen) const {
+    if (mPool != NULL) {
+        return mPool->stringAt(mIndex, outLen);
+    }
+    if (outLen != NULL) {
+        *outLen = 0;
+    }
+    return NULL;
+}
+
+status_t ResTable::getEntry(
+        const PackageGroup* packageGroup, int typeIndex, int entryIndex,
+        const ResTable_config* config,
+        Entry* outEntry) const
+{
+    const TypeList& typeList = packageGroup->types[typeIndex];
+    if (typeList.isEmpty()) {
+        ALOGV("Skipping entry type index 0x%02x because type is NULL!\n", typeIndex);
         return BAD_TYPE;
     }
 
-    const ResTable_type* type = NULL;
-    uint32_t offset = ResTable_type::NO_ENTRY;
+    const ResTable_type* bestType = NULL;
+    uint32_t bestOffset = ResTable_type::NO_ENTRY;
+    const Package* bestPackage = NULL;
+    uint32_t specFlags = 0;
+    uint8_t actualTypeIndex = typeIndex;
     ResTable_config bestConfig;
-    memset(&bestConfig, 0, sizeof(bestConfig)); // make the compiler shut up
+    memset(&bestConfig, 0, sizeof(bestConfig));
 
-    const size_t NT = allTypes->configs.size();
-    for (size_t i=0; i<NT; i++) {
-        const ResTable_type* const thisType = allTypes->configs[i];
-        if (thisType == NULL) continue;
+    // Iterate over the Types of each package.
+    const size_t typeCount = typeList.size();
+    for (size_t i = 0; i < typeCount; i++) {
+        const Type* const typeSpec = typeList[i];
 
-        ResTable_config thisConfig;
-        thisConfig.copyFromDtoH(thisType->config);
+        int realEntryIndex = entryIndex;
+        int realTypeIndex = typeIndex;
+        bool currentTypeIsOverlay = false;
 
-        TABLE_GETENTRY(ALOGI("Match entry 0x%x in type 0x%x (sz 0x%x): %s\n",
-                           entryIndex, typeIndex+1, dtohl(thisType->config.size),
-                           thisConfig.toString().string()));
-
-        // Check to make sure this one is valid for the current parameters.
-        if (config && !thisConfig.match(*config)) {
-            TABLE_GETENTRY(ALOGI("Does not match config!\n"));
-            continue;
-        }
-
-        // Check if there is the desired entry in this type.
-
-        const uint8_t* const end = ((const uint8_t*)thisType)
-            + dtohl(thisType->header.size);
-        const uint32_t* const eindex = (const uint32_t*)
-            (((const uint8_t*)thisType) + dtohs(thisType->header.headerSize));
-
-        uint32_t thisOffset = dtohl(eindex[entryIndex]);
-        if (thisOffset == ResTable_type::NO_ENTRY) {
-            TABLE_GETENTRY(ALOGI("Skipping because it is not defined!\n"));
-            continue;
-        }
-
-        if (type != NULL) {
-            // Check if this one is less specific than the last found.  If so,
-            // we will skip it.  We check starting with things we most care
-            // about to those we least care about.
-            if (!thisConfig.isBetterThan(bestConfig, config)) {
-                TABLE_GETENTRY(ALOGI("This config is worse than last!\n"));
+        // Runtime overlay packages provide a mapping of app resource
+        // ID to package resource ID.
+        if (typeSpec->idmapEntries.hasEntries()) {
+            uint16_t overlayEntryIndex;
+            if (typeSpec->idmapEntries.lookup(entryIndex, &overlayEntryIndex) != NO_ERROR) {
+                // No such mapping exists
                 continue;
             }
+            realEntryIndex = overlayEntryIndex;
+            realTypeIndex = typeSpec->idmapEntries.overlayTypeId() - 1;
+            currentTypeIsOverlay = true;
         }
 
-        type = thisType;
-        offset = thisOffset;
-        bestConfig = thisConfig;
-        TABLE_GETENTRY(ALOGI("Best entry so far -- using it!\n"));
-        if (!config) break;
+        if (static_cast<size_t>(realEntryIndex) >= typeSpec->entryCount) {
+            ALOGW("For resource 0x%08x, entry index(%d) is beyond type entryCount(%d)",
+                    Res_MAKEID(packageGroup->id - 1, typeIndex, entryIndex),
+                    entryIndex, static_cast<int>(typeSpec->entryCount));
+            // We should normally abort here, but some legacy apps declare
+            // resources in the 'android' package (old bug in AAPT).
+            continue;
+        }
+
+        // Aggregate all the flags for each package that defines this entry.
+        if (typeSpec->typeSpecFlags != NULL) {
+            specFlags |= dtohl(typeSpec->typeSpecFlags[realEntryIndex]);
+        } else {
+            specFlags = -1;
+        }
+
+        const size_t numConfigs = typeSpec->configs.size();
+        for (size_t c = 0; c < numConfigs; c++) {
+            const ResTable_type* const thisType = typeSpec->configs[c];
+            if (thisType == NULL) {
+                continue;
+            }
+
+            ResTable_config thisConfig;
+            thisConfig.copyFromDtoH(thisType->config);
+
+            // Check to make sure this one is valid for the current parameters.
+            if (config != NULL && !thisConfig.match(*config)) {
+                continue;
+            }
+
+            // Check if there is the desired entry in this type.
+            const uint8_t* const end = reinterpret_cast<const uint8_t*>(thisType)
+                    + dtohl(thisType->header.size);
+            const uint32_t* const eindex = reinterpret_cast<const uint32_t*>(
+                    reinterpret_cast<const uint8_t*>(thisType) + dtohs(thisType->header.headerSize));
+
+            uint32_t thisOffset = dtohl(eindex[realEntryIndex]);
+            if (thisOffset == ResTable_type::NO_ENTRY) {
+                // There is no entry for this index and configuration.
+                continue;
+            }
+
+            if (bestType != NULL) {
+                // Check if this one is less specific than the last found.  If so,
+                // we will skip it.  We check starting with things we most care
+                // about to those we least care about.
+                if (!thisConfig.isBetterThan(bestConfig, config)) {
+                    if (!currentTypeIsOverlay || thisConfig.compare(bestConfig) != 0) {
+                        continue;
+                    }
+                }
+            }
+
+            bestType = thisType;
+            bestOffset = thisOffset;
+            bestConfig = thisConfig;
+            bestPackage = typeSpec->package;
+            actualTypeIndex = realTypeIndex;
+
+            // If no config was specified, any type will do, so skip
+            if (config == NULL) {
+                break;
+            }
+        }
     }
 
-    if (type == NULL) {
-        TABLE_GETENTRY(ALOGI("No value found for requested entry!\n"));
+    if (bestType == NULL) {
         return BAD_INDEX;
     }
 
-    offset += dtohl(type->entriesStart);
-    TABLE_NOISY(ALOGD("Looking in resource table %p, typeOff=%p, offset=%p",
-            package->header->header, (void*)(((const char*)type)-((const char*)package->header->header)),
-            (void*)offset));
+    bestOffset += dtohl(bestType->entriesStart);
 
-    if (offset > (dtohl(type->header.size)-sizeof(ResTable_entry))) {
+    if (bestOffset > (dtohl(bestType->header.size)-sizeof(ResTable_entry))) {
         ALOGW("ResTable_entry at 0x%x is beyond type chunk data 0x%x",
-             offset, dtohl(type->header.size));
+                bestOffset, dtohl(bestType->header.size));
         return BAD_TYPE;
     }
-    if ((offset&0x3) != 0) {
-        ALOGW("ResTable_entry at 0x%x is not on an integer boundary",
-             offset);
+    if ((bestOffset & 0x3) != 0) {
+        ALOGW("ResTable_entry at 0x%x is not on an integer boundary", bestOffset);
         return BAD_TYPE;
     }
 
-    const ResTable_entry* const entry = (const ResTable_entry*)
-        (((const uint8_t*)type) + offset);
+    const ResTable_entry* const entry = reinterpret_cast<const ResTable_entry*>(
+            reinterpret_cast<const uint8_t*>(bestType) + bestOffset);
     if (dtohs(entry->size) < sizeof(*entry)) {
         ALOGW("ResTable_entry size 0x%x is too small", dtohs(entry->size));
         return BAD_TYPE;
     }
 
-    *outType = type;
-    *outEntry = entry;
-    if (outTypeClass != NULL) {
-        *outTypeClass = allTypes;
+    if (outEntry != NULL) {
+        outEntry->entry = entry;
+        outEntry->config = bestConfig;
+        outEntry->type = bestType;
+        outEntry->specFlags = specFlags;
+        outEntry->package = bestPackage;
+        outEntry->typeStr = StringPoolRef(&bestPackage->typeStrings, actualTypeIndex - bestPackage->typeIdOffset);
+        outEntry->keyStr = StringPoolRef(&bestPackage->keyStrings, dtohl(entry->key.index));
     }
-    return offset + dtohs(entry->size);
+    return NO_ERROR;
 }
 
 status_t ResTable::parsePackage(const ResTable_package* const pkg,
-                                const Header* const header, uint32_t idmap_id)
+                                const Header* const header)
 {
     const uint8_t* base = (const uint8_t*)pkg;
-    status_t err = validate_chunk(&pkg->header, sizeof(*pkg),
+    status_t err = validate_chunk(&pkg->header, sizeof(*pkg) - sizeof(pkg->typeIdOffset),
                                   header->dataEnd, "ResTable_package");
     if (err != NO_ERROR) {
         return (mError=err);
@@ -5494,89 +5520,88 @@
         return (mError=BAD_TYPE);
     }
 
-    Package* package = NULL;
-    PackageGroup* group = NULL;
-    uint32_t id = idmap_id != 0 ? idmap_id : dtohl(pkg->id);
-    // If at this point id == 0, pkg is an overlay package without a
-    // corresponding idmap. During regular usage, overlay packages are
-    // always loaded alongside their idmaps, but during idmap creation
-    // the package is temporarily loaded by itself.
-    if (id < 256) {
+    uint32_t id = dtohl(pkg->id);
+    KeyedVector<uint8_t, IdmapEntries> idmapEntries;
 
-        package = new Package(this, header, pkg);
-        if (package == NULL) {
+    if (header->resourceIDMap != NULL) {
+        uint8_t targetPackageId = 0;
+        status_t err = parseIdmap(header->resourceIDMap, header->resourceIDMapSize, &targetPackageId, &idmapEntries);
+        if (err != NO_ERROR) {
+            ALOGW("Overlay is broken");
+            return (mError=err);
+        }
+        id = targetPackageId;
+    }
+
+    if (id >= 256) {
+        LOG_ALWAYS_FATAL("Package id out of range");
+        return NO_ERROR;
+    } else if (id == 0) {
+        // This is a library so assign an ID
+        id = mNextPackageId++;
+    }
+
+    PackageGroup* group = NULL;
+    Package* package = new Package(this, header, pkg);
+    if (package == NULL) {
+        return (mError=NO_MEMORY);
+    }
+
+    err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings),
+                                   header->dataEnd-(base+dtohl(pkg->typeStrings)));
+    if (err != NO_ERROR) {
+        delete group;
+        delete package;
+        return (mError=err);
+    }
+
+    err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings),
+                                  header->dataEnd-(base+dtohl(pkg->keyStrings)));
+    if (err != NO_ERROR) {
+        delete group;
+        delete package;
+        return (mError=err);
+    }
+
+    size_t idx = mPackageMap[id];
+    if (idx == 0) {
+        idx = mPackageGroups.size() + 1;
+
+        char16_t tmpName[sizeof(pkg->name)/sizeof(char16_t)];
+        strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(char16_t));
+        group = new PackageGroup(this, String16(tmpName), id);
+        if (group == NULL) {
+            delete package;
             return (mError=NO_MEMORY);
         }
 
-        if (idmap_id == 0) {
-            err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings),
-                                           header->dataEnd-(base+dtohl(pkg->typeStrings)));
-            if (err != NO_ERROR) {
-                delete group;
-                delete package;
-                return (mError=err);
-            }
-
-            err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings),
-                                          header->dataEnd-(base+dtohl(pkg->keyStrings)));
-            if (err != NO_ERROR) {
-                delete group;
-                delete package;
-                return (mError=err);
-            }
-        }
-
-        if (id == 0) {
-            // This is a library so assign an ID
-            id = mNextPackageId++;
-        }
-
-        size_t idx = mPackageMap[id];
-        if (idx == 0) {
-            idx = mPackageGroups.size()+1;
-
-            char16_t tmpName[sizeof(pkg->name)/sizeof(char16_t)];
-            strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(char16_t));
-            group = new PackageGroup(this, String16(tmpName), id);
-            if (group == NULL) {
-                delete package;
-                return (mError=NO_MEMORY);
-            }
-
-            //printf("Adding new package id %d at index %d\n", id, idx);
-            err = mPackageGroups.add(group);
-            if (err < NO_ERROR) {
-                return (mError=err);
-            }
-            group->basePackage = package;
-
-            mPackageMap[id] = (uint8_t)idx;
-
-            // Find all packages that reference this package
-            size_t N = mPackageGroups.size();
-            for (size_t i = 0; i < N; i++) {
-                mPackageGroups[i]->dynamicRefTable.addMapping(
-                        group->name, static_cast<uint8_t>(group->id));
-            }
-        } else {
-            group = mPackageGroups.itemAt(idx-1);
-            if (group == NULL) {
-                return (mError=UNKNOWN_ERROR);
-            }
-        }
-        err = group->packages.add(package);
+        //printf("Adding new package id %d at index %d\n", id, idx);
+        err = mPackageGroups.add(group);
         if (err < NO_ERROR) {
             return (mError=err);
         }
+
+        mPackageMap[id] = static_cast<uint8_t>(idx);
+
+        // Find all packages that reference this package
+        size_t N = mPackageGroups.size();
+        for (size_t i = 0; i < N; i++) {
+            mPackageGroups[i]->dynamicRefTable.addMapping(
+                    group->name, static_cast<uint8_t>(group->id));
+        }
     } else {
-        LOG_ALWAYS_FATAL("Package id out of range");
-        return NO_ERROR;
+        group = mPackageGroups.itemAt(idx - 1);
+        if (group == NULL) {
+            return (mError=UNKNOWN_ERROR);
+        }
     }
 
+    err = group->packages.add(package);
+    if (err < NO_ERROR) {
+        return (mError=err);
+    }
 
     // Iterate through all chunks.
-    size_t curPackage = 0;
-
     const ResChunk_header* chunk =
         (const ResChunk_header*)(((const uint8_t*)pkg)
                                  + dtohs(pkg->header.headerSize));
@@ -5597,6 +5622,7 @@
             }
 
             const size_t typeSpecSize = dtohl(typeSpec->header.size);
+            const size_t newEntryCount = dtohl(typeSpec->entryCount);
 
             LOAD_TABLE_NOISY(printf("TypeSpec off %p: type=0x%x, headerSize=0x%x, size=%p\n",
                                     (void*)(base-(const uint8_t*)chunk),
@@ -5605,12 +5631,11 @@
                                     (void*)typeSpecSize));
             // look for block overrun or int overflow when multiplying by 4
             if ((dtohl(typeSpec->entryCount) > (INT32_MAX/sizeof(uint32_t))
-                    || dtohs(typeSpec->header.headerSize)+(sizeof(uint32_t)*dtohl(typeSpec->entryCount))
+                    || dtohs(typeSpec->header.headerSize)+(sizeof(uint32_t)*newEntryCount)
                     > typeSpecSize)) {
                 ALOGW("ResTable_typeSpec entry index to %p extends beyond chunk end %p.",
-                     (void*)(dtohs(typeSpec->header.headerSize)
-                             +(sizeof(uint32_t)*dtohl(typeSpec->entryCount))),
-                     (void*)typeSpecSize);
+                        (void*)(dtohs(typeSpec->header.headerSize) + (sizeof(uint32_t)*newEntryCount)),
+                        (void*)typeSpecSize);
                 return (mError=BAD_TYPE);
             }
 
@@ -5619,21 +5644,36 @@
                 return (mError=BAD_TYPE);
             }
 
-            while (package->types.size() < typeSpec->id) {
-                package->types.add(NULL);
+            if (newEntryCount > 0) {
+                uint8_t typeIndex = typeSpec->id - 1;
+                ssize_t idmapIndex = idmapEntries.indexOfKey(typeSpec->id);
+                if (idmapIndex >= 0) {
+                    typeIndex = idmapEntries[idmapIndex].targetTypeId() - 1;
+                }
+
+                TypeList& typeList = group->types.editItemAt(typeIndex);
+                if (!typeList.isEmpty()) {
+                    const Type* existingType = typeList[0];
+                    if (existingType->entryCount != newEntryCount && idmapIndex < 0) {
+                        ALOGW("ResTable_typeSpec entry count inconsistent: given %d, previously %d",
+                                (int) newEntryCount, (int) existingType->entryCount);
+                        // We should normally abort here, but some legacy apps declare
+                        // resources in the 'android' package (old bug in AAPT).
+                    }
+                }
+
+                Type* t = new Type(header, package, newEntryCount);
+                t->typeSpec = typeSpec;
+                t->typeSpecFlags = (const uint32_t*)(
+                        ((const uint8_t*)typeSpec) + dtohs(typeSpec->header.headerSize));
+                if (idmapIndex >= 0) {
+                    t->idmapEntries = idmapEntries[idmapIndex];
+                }
+                typeList.add(t);
+                group->largestTypeId = max(group->largestTypeId, typeSpec->id);
+            } else {
+                ALOGV("Skipping empty ResTable_typeSpec for type %d", typeSpec->id);
             }
-            Type* t = package->types[typeSpec->id-1];
-            if (t == NULL) {
-                t = new Type(header, package, dtohl(typeSpec->entryCount));
-                package->types.editItemAt(typeSpec->id-1) = t;
-            } else if (dtohl(typeSpec->entryCount) != t->entryCount) {
-                ALOGW("ResTable_typeSpec entry count inconsistent: given %d, previously %d",
-                    (int)dtohl(typeSpec->entryCount), (int)t->entryCount);
-                return (mError=BAD_TYPE);
-            }
-            t->typeSpecFlags = (const uint32_t*)(
-                    ((const uint8_t*)typeSpec) + dtohs(typeSpec->header.headerSize));
-            t->typeSpec = typeSpec;
 
         } else if (ctype == RES_TABLE_TYPE_TYPE) {
             const ResTable_type* type = (const ResTable_type*)(chunk);
@@ -5644,50 +5684,69 @@
             }
 
             const uint32_t typeSize = dtohl(type->header.size);
+            const size_t newEntryCount = dtohl(type->entryCount);
 
             LOAD_TABLE_NOISY(printf("Type off %p: type=0x%x, headerSize=0x%x, size=%p\n",
                                     (void*)(base-(const uint8_t*)chunk),
                                     dtohs(type->header.type),
                                     dtohs(type->header.headerSize),
                                     (void*)typeSize));
-            if (dtohs(type->header.headerSize)+(sizeof(uint32_t)*dtohl(type->entryCount))
-                > typeSize) {
+            if (dtohs(type->header.headerSize)+(sizeof(uint32_t)*newEntryCount)
+                    > typeSize) {
                 ALOGW("ResTable_type entry index to %p extends beyond chunk end 0x%x.",
-                     (void*)(dtohs(type->header.headerSize)
-                             +(sizeof(uint32_t)*dtohl(type->entryCount))),
-                     typeSize);
+                        (void*)(dtohs(type->header.headerSize) + (sizeof(uint32_t)*newEntryCount)),
+                        typeSize);
                 return (mError=BAD_TYPE);
             }
-            if (dtohl(type->entryCount) != 0
+
+            if (newEntryCount != 0
                 && dtohl(type->entriesStart) > (typeSize-sizeof(ResTable_entry))) {
                 ALOGW("ResTable_type entriesStart at 0x%x extends beyond chunk end 0x%x.",
                      dtohl(type->entriesStart), typeSize);
                 return (mError=BAD_TYPE);
             }
+
             if (type->id == 0) {
                 ALOGW("ResTable_type has an id of 0.");
                 return (mError=BAD_TYPE);
             }
 
-            while (package->types.size() < type->id) {
-                package->types.add(NULL);
-            }
-            Type* t = package->types[type->id-1];
-            if (t == NULL) {
-                t = new Type(header, package, dtohl(type->entryCount));
-                package->types.editItemAt(type->id-1) = t;
-            } else if (dtohl(type->entryCount) != t->entryCount) {
-                ALOGW("ResTable_type entry count inconsistent: given %d, previously %d",
-                    (int)dtohl(type->entryCount), (int)t->entryCount);
-                return (mError=BAD_TYPE);
+            if (newEntryCount > 0) {
+                uint8_t typeIndex = type->id - 1;
+                ssize_t idmapIndex = idmapEntries.indexOfKey(type->id);
+                if (idmapIndex >= 0) {
+                    typeIndex = idmapEntries[idmapIndex].targetTypeId() - 1;
+                }
+
+                TypeList& typeList = group->types.editItemAt(typeIndex);
+                if (typeList.isEmpty()) {
+                    ALOGE("No TypeSpec for type %d", type->id);
+                    return (mError=BAD_TYPE);
+                }
+
+                Type* t = typeList.editItemAt(typeList.size() - 1);
+                if (newEntryCount != t->entryCount) {
+                    ALOGE("ResTable_type entry count inconsistent: given %d, previously %d",
+                        (int)newEntryCount, (int)t->entryCount);
+                    return (mError=BAD_TYPE);
+                }
+
+                if (t->package != package) {
+                    ALOGE("No TypeSpec for type %d", type->id);
+                    return (mError=BAD_TYPE);
+                }
+
+                t->configs.add(type);
+
+                TABLE_GETENTRY(
+                    ResTable_config thisConfig;
+                    thisConfig.copyFromDtoH(type->config);
+                    ALOGI("Adding config to type %d: %s\n",
+                          type->id, thisConfig.toString().string()));
+            } else {
+                ALOGV("Skipping empty ResTable_type for type %d", type->id);
             }
 
-            TABLE_GETENTRY(
-                ResTable_config thisConfig;
-                thisConfig.copyFromDtoH(type->config);
-                ALOGI("Adding config to type %d: %s\n",
-                      type->id, thisConfig.toString().string()));
-            t->configs.add(type);
         } else if (ctype == RES_TABLE_LIBRARY_TYPE) {
             if (group->dynamicRefTable.entries().size() == 0) {
                 status_t err = group->dynamicRefTable.load((const ResTable_lib_header*) chunk);
@@ -5714,10 +5773,6 @@
             (((const uint8_t*)chunk) + csize);
     }
 
-    if (group->typeCount == 0) {
-        group->typeCount = package->types.size();
-    }
-
     return NO_ERROR;
 }
 
@@ -5818,6 +5873,12 @@
     return NO_ERROR;
 }
 
+struct IdmapTypeMap {
+    ssize_t overlayTypeId;
+    size_t entryOffset;
+    Vector<uint32_t> entryMap;
+};
+
 status_t ResTable::createIdmap(const ResTable& overlay,
         uint32_t targetCrc, uint32_t overlayCrc,
         const char* targetPath, const char* overlayPath,
@@ -5828,41 +5889,46 @@
         ALOGW("idmap: target package has no package groups, cannot create idmap\n");
         return UNKNOWN_ERROR;
     }
+
     if (mPackageGroups[0]->packages.size() == 0) {
         ALOGW("idmap: target package has no packages in its first package group, "
                 "cannot create idmap\n");
         return UNKNOWN_ERROR;
     }
 
-    Vector<Vector<uint32_t> > map;
+    KeyedVector<uint8_t, IdmapTypeMap> map;
+
     // overlaid packages are assumed to contain only one package group
     const PackageGroup* pg = mPackageGroups[0];
-    const Package* pkg = pg->packages[0];
-    size_t typeCount = pkg->types.size();
-    // starting size is header + first item (number of types in map)
-    *outSize = (IDMAP_HEADER_SIZE + 1) * sizeof(uint32_t);
+
+    // starting size is header
+    *outSize = ResTable::IDMAP_HEADER_SIZE_BYTES;
+
+    // target package id and number of types in map
+    *outSize += 2 * sizeof(uint16_t);
+
     // overlay packages are assumed to contain only one package group
     const String16 overlayPackage(overlay.mPackageGroups[0]->packages[0]->package->name);
-    const uint32_t pkg_id = pkg->package->id << 24;
 
-    for (size_t typeIndex = 0; typeIndex < typeCount; ++typeIndex) {
-        ssize_t first = -1;
-        ssize_t last = -1;
-        const Type* typeConfigs = pkg->getType(typeIndex);
-        ssize_t mapIndex = map.add();
-        if (mapIndex < 0) {
-            return NO_MEMORY;
+    for (size_t typeIndex = 0; typeIndex < pg->types.size(); ++typeIndex) {
+        const TypeList& typeList = pg->types[typeIndex];
+        if (typeList.isEmpty()) {
+            continue;
         }
-        Vector<uint32_t>& vector = map.editItemAt(mapIndex);
+
+        const Type* typeConfigs = typeList[0];
+
+        IdmapTypeMap typeMap;
+        typeMap.overlayTypeId = -1;
+        typeMap.entryOffset = 0;
+
         for (size_t entryIndex = 0; entryIndex < typeConfigs->entryCount; ++entryIndex) {
-            uint32_t resID = pkg_id
-                | (0x00ff0000 & ((typeIndex+1)<<16))
-                | (0x0000ffff & (entryIndex));
+            uint32_t resID = Res_MAKEID(pg->id - 1, typeIndex, entryIndex);
             resource_name resName;
             if (!this->getResourceName(resID, false, &resName)) {
-                ALOGW("idmap: resource 0x%08x has spec but lacks values, skipping\n", resID);
-                // add dummy value, or trimming leading/trailing zeroes later will fail
-                vector.push(0);
+                if (typeMap.entryMap.isEmpty()) {
+                    typeMap.entryOffset++;
+                }
                 continue;
             }
 
@@ -5874,49 +5940,55 @@
                                                               overlayType.size(),
                                                               overlayPackage.string(),
                                                               overlayPackage.size());
-            if (overlayResID != 0) {
-                overlayResID = pkg_id | (0x00ffffff & overlayResID);
-                last = Res_GETENTRY(resID);
-                if (first == -1) {
-                    first = Res_GETENTRY(resID);
+            if (overlayResID == 0) {
+                if (typeMap.entryMap.isEmpty()) {
+                    typeMap.entryOffset++;
                 }
+                continue;
             }
-            vector.push(overlayResID);
-#if 0
-            if (overlayResID != 0) {
-                ALOGD("%s/%s 0x%08x -> 0x%08x\n",
-                     String8(String16(resName.type)).string(),
-                     String8(String16(resName.name)).string(),
-                     resID, overlayResID);
+
+            if (typeMap.overlayTypeId == -1) {
+                typeMap.overlayTypeId = Res_GETTYPE(overlayResID) + 1;
             }
-#endif
+
+            if (Res_GETTYPE(overlayResID) + 1 != static_cast<size_t>(typeMap.overlayTypeId)) {
+                ALOGE("idmap: can't mix type ids in entry map. Resource 0x%08x maps to 0x%08x"
+                        " but entries should map to resources of type %02x",
+                        resID, overlayResID, typeMap.overlayTypeId);
+                return BAD_TYPE;
+            }
+
+            if (typeMap.entryOffset + typeMap.entryMap.size() < entryIndex) {
+                // Resize to accomodate this entry and the 0's in between.
+                if (typeMap.entryMap.resize((entryIndex - typeMap.entryOffset) + 1) < 0) {
+                    return NO_MEMORY;
+                }
+                typeMap.entryMap.editTop() = Res_GETENTRY(overlayResID);
+            } else {
+                typeMap.entryMap.add(Res_GETENTRY(overlayResID));
+            }
         }
 
-        if (first != -1) {
-            // shave off trailing entries which lack overlay values
-            const size_t last_past_one = last + 1;
-            if (last_past_one < vector.size()) {
-                vector.removeItemsAt(last_past_one, vector.size() - last_past_one);
+        if (!typeMap.entryMap.isEmpty()) {
+            if (map.add(static_cast<uint8_t>(typeIndex), typeMap) < 0) {
+                return NO_MEMORY;
             }
-            // shave off leading entries which lack overlay values
-            vector.removeItemsAt(0, first);
-            // store offset to first overlaid resource ID of this type
-            vector.insertAt((uint32_t)first, 0, 1);
-            // reserve space for number and offset of entries, and the actual entries
-            *outSize += (2 + vector.size()) * sizeof(uint32_t);
-        } else {
-            // no entries of current type defined in overlay package
-            vector.clear();
-            // reserve space for type offset
-            *outSize += 1 * sizeof(uint32_t);
+            *outSize += (4 * sizeof(uint16_t)) + (typeMap.entryMap.size() * sizeof(uint32_t));
         }
     }
 
+    if (map.isEmpty()) {
+        ALOGW("idmap: no resources in overlay package present in base package");
+        return UNKNOWN_ERROR;
+    }
+
     if ((*outData = malloc(*outSize)) == NULL) {
         return NO_MEMORY;
     }
+
     uint32_t* data = (uint32_t*)*outData;
     *data++ = htodl(IDMAP_MAGIC);
+    *data++ = htodl(IDMAP_CURRENT_VERSION);
     *data++ = htodl(targetCrc);
     *data++ = htodl(overlayCrc);
     const char* paths[] = { targetPath, overlayPath };
@@ -5934,44 +6006,30 @@
         data += 256 / sizeof(uint32_t);
     }
     const size_t mapSize = map.size();
-    *data++ = htodl(mapSize);
-    size_t offset = mapSize;
+    uint16_t* typeData = reinterpret_cast<uint16_t*>(data);
+    *typeData++ = htods(pg->id);
+    *typeData++ = htods(mapSize);
     for (size_t i = 0; i < mapSize; ++i) {
-        const Vector<uint32_t>& vector = map.itemAt(i);
-        const size_t N = vector.size();
-        if (N == 0) {
-            *data++ = htodl(0);
-        } else {
-            offset++;
-            *data++ = htodl(offset);
-            offset += N;
+        uint8_t targetTypeId = map.keyAt(i);
+        const IdmapTypeMap& typeMap = map[i];
+        *typeData++ = htods(targetTypeId + 1);
+        *typeData++ = htods(typeMap.overlayTypeId);
+        *typeData++ = htods(typeMap.entryMap.size());
+        *typeData++ = htods(typeMap.entryOffset);
+
+        const size_t entryCount = typeMap.entryMap.size();
+        uint32_t* entries = reinterpret_cast<uint32_t*>(typeData);
+        for (size_t j = 0; j < entryCount; j++) {
+            entries[j] = htodl(typeMap.entryMap[j]);
         }
-    }
-    if (offset == mapSize) {
-        ALOGW("idmap: no resources in overlay package present in base package\n");
-        return UNKNOWN_ERROR;
-    }
-    for (size_t i = 0; i < mapSize; ++i) {
-        const Vector<uint32_t>& vector = map.itemAt(i);
-        const size_t N = vector.size();
-        if (N == 0) {
-            continue;
-        }
-        if (N == 1) { // vector expected to hold (offset) + (N > 0 entries)
-            ALOGW("idmap: type %u supposedly has entries, but no entries found\n", (uint32_t)i);
-            return UNKNOWN_ERROR;
-        }
-        *data++ = htodl(N - 1); // do not count the offset (which is vector's first element)
-        for (size_t j = 0; j < N; ++j) {
-            const uint32_t& overlayResID = vector.itemAt(j);
-            *data++ = htodl(overlayResID);
-        }
+        typeData += entryCount * 2;
     }
 
     return NO_ERROR;
 }
 
 bool ResTable::getIdmapInfo(const void* idmap, size_t sizeBytes,
+                            uint32_t* pVersion,
                             uint32_t* pTargetCrc, uint32_t* pOverlayCrc,
                             String8* pTargetPath, String8* pOverlayPath)
 {
@@ -5979,17 +6037,20 @@
     if (!assertIdmapHeader(map, sizeBytes)) {
         return false;
     }
+    if (pVersion) {
+        *pVersion = dtohl(map[1]);
+    }
     if (pTargetCrc) {
-        *pTargetCrc = map[1];
+        *pTargetCrc = dtohl(map[2]);
     }
     if (pOverlayCrc) {
-        *pOverlayCrc = map[2];
+        *pOverlayCrc = dtohl(map[3]);
     }
     if (pTargetPath) {
-        pTargetPath->setTo(reinterpret_cast<const char*>(map + 3));
+        pTargetPath->setTo(reinterpret_cast<const char*>(map + 4));
     }
     if (pOverlayPath) {
-        pOverlayPath->setTo(reinterpret_cast<const char*>(map + 3 + 256 / sizeof(uint32_t)));
+        pOverlayPath->setTo(reinterpret_cast<const char*>(map + 4 + 256 / sizeof(uint32_t)));
     }
     return true;
 }
@@ -6138,184 +6199,184 @@
         size_t pkgCount = pg->packages.size();
         for (size_t pkgIndex=0; pkgIndex<pkgCount; pkgIndex++) {
             const Package* pkg = pg->packages[pkgIndex];
-            size_t typeCount = pkg->types.size();
-            printf("  Package %d id=%d name=%s typeCount=%d\n", (int)pkgIndex,
-                    pkg->package->id, String8(String16(pkg->package->name)).string(),
-                    (int)typeCount);
-            for (size_t typeIndex=0; typeIndex<typeCount; typeIndex++) {
-                const Type* typeConfigs = pkg->getType(typeIndex);
-                if (typeConfigs == NULL) {
-                    printf("    type %d NULL\n", (int)typeIndex);
+            printf("  Package %d id=%d name=%s\n", (int)pkgIndex,
+                    pkg->package->id, String8(String16(pkg->package->name)).string());
+        }
+
+        for (size_t typeIndex=0; typeIndex < pg->types.size(); typeIndex++) {
+            const TypeList& typeList = pg->types[typeIndex];
+            if (typeList.isEmpty()) {
+                //printf("    type %d NULL\n", (int)typeIndex);
+                continue;
+            }
+            const Type* typeConfigs = typeList[0];
+            const size_t NTC = typeConfigs->configs.size();
+            printf("    type %d configCount=%d entryCount=%d\n",
+                   (int)typeIndex, (int)NTC, (int)typeConfigs->entryCount);
+            if (typeConfigs->typeSpecFlags != NULL) {
+                for (size_t entryIndex=0; entryIndex<typeConfigs->entryCount; entryIndex++) {
+                    uint32_t resID = (0xff000000 & ((pg->id)<<24))
+                                | (0x00ff0000 & ((typeIndex+1)<<16))
+                                | (0x0000ffff & (entryIndex));
+                    // Since we are creating resID without actually
+                    // iterating over them, we have no idea which is a
+                    // dynamic reference. We must check.
+                    pg->dynamicRefTable.lookupResourceId(&resID);
+
+                    resource_name resName;
+                    if (this->getResourceName(resID, true, &resName)) {
+                        String8 type8;
+                        String8 name8;
+                        if (resName.type8 != NULL) {
+                            type8 = String8(resName.type8, resName.typeLen);
+                        } else {
+                            type8 = String8(resName.type, resName.typeLen);
+                        }
+                        if (resName.name8 != NULL) {
+                            name8 = String8(resName.name8, resName.nameLen);
+                        } else {
+                            name8 = String8(resName.name, resName.nameLen);
+                        }
+                        printf("      spec resource 0x%08x %s:%s/%s: flags=0x%08x\n",
+                            resID,
+                            CHAR16_TO_CSTR(resName.package, resName.packageLen),
+                            type8.string(), name8.string(),
+                            dtohl(typeConfigs->typeSpecFlags[entryIndex]));
+                    } else {
+                        printf("      INVALID TYPE CONFIG FOR RESOURCE 0x%08x\n", resID);
+                    }
+                }
+            }
+            for (size_t configIndex=0; configIndex<NTC; configIndex++) {
+                const ResTable_type* type = typeConfigs->configs[configIndex];
+                if ((((uint64_t)type)&0x3) != 0) {
+                    printf("      NON-INTEGER ResTable_type ADDRESS: %p\n", type);
                     continue;
                 }
-                const size_t NTC = typeConfigs->configs.size();
-                printf("    type %d configCount=%d entryCount=%d\n",
-                       (int)typeIndex, (int)NTC, (int)typeConfigs->entryCount);
-                if (typeConfigs->typeSpecFlags != NULL) {
-                    for (size_t entryIndex=0; entryIndex<typeConfigs->entryCount; entryIndex++) {
-                        uint32_t resID = (0xff000000 & ((pkg->package->id)<<24))
-                                    | (0x00ff0000 & ((typeIndex+1)<<16))
-                                    | (0x0000ffff & (entryIndex));
-                        // Since we are creating resID without actually
-                        // iterating over them, we have no idea which is a
-                        // dynamic reference. We must check.
-                        pg->dynamicRefTable.lookupResourceId(&resID);
-
-                        resource_name resName;
-                        if (this->getResourceName(resID, true, &resName)) {
-                            String8 type8;
-                            String8 name8;
-                            if (resName.type8 != NULL) {
-                                type8 = String8(resName.type8, resName.typeLen);
-                            } else {
-                                type8 = String8(resName.type, resName.typeLen);
-                            }
-                            if (resName.name8 != NULL) {
-                                name8 = String8(resName.name8, resName.nameLen);
-                            } else {
-                                name8 = String8(resName.name, resName.nameLen);
-                            }
-                            printf("      spec resource 0x%08x %s:%s/%s: flags=0x%08x\n",
-                                resID,
-                                CHAR16_TO_CSTR(resName.package, resName.packageLen),
-                                type8.string(), name8.string(),
-                                dtohl(typeConfigs->typeSpecFlags[entryIndex]));
-                        } else {
-                            printf("      INVALID TYPE CONFIG FOR RESOURCE 0x%08x\n", resID);
-                        }
-                    }
+                String8 configStr = type->config.toString();
+                printf("      config %s:\n", configStr.size() > 0
+                        ? configStr.string() : "(default)");
+                size_t entryCount = dtohl(type->entryCount);
+                uint32_t entriesStart = dtohl(type->entriesStart);
+                if ((entriesStart&0x3) != 0) {
+                    printf("      NON-INTEGER ResTable_type entriesStart OFFSET: 0x%x\n", entriesStart);
+                    continue;
                 }
-                for (size_t configIndex=0; configIndex<NTC; configIndex++) {
-                    const ResTable_type* type = typeConfigs->configs[configIndex];
-                    if ((((uint64_t)type)&0x3) != 0) {
-                        printf("      NON-INTEGER ResTable_type ADDRESS: %p\n", type);
+                uint32_t typeSize = dtohl(type->header.size);
+                if ((typeSize&0x3) != 0) {
+                    printf("      NON-INTEGER ResTable_type header.size: 0x%x\n", typeSize);
+                    continue;
+                }
+                for (size_t entryIndex=0; entryIndex<entryCount; entryIndex++) {
+
+                    const uint8_t* const end = ((const uint8_t*)type)
+                        + dtohl(type->header.size);
+                    const uint32_t* const eindex = (const uint32_t*)
+                        (((const uint8_t*)type) + dtohs(type->header.headerSize));
+
+                    uint32_t thisOffset = dtohl(eindex[entryIndex]);
+                    if (thisOffset == ResTable_type::NO_ENTRY) {
                         continue;
                     }
-                    String8 configStr = type->config.toString();
-                    printf("      config %s:\n", configStr.size() > 0
-                            ? configStr.string() : "(default)");
-                    size_t entryCount = dtohl(type->entryCount);
-                    uint32_t entriesStart = dtohl(type->entriesStart);
-                    if ((entriesStart&0x3) != 0) {
-                        printf("      NON-INTEGER ResTable_type entriesStart OFFSET: 0x%x\n", entriesStart);
-                        continue;
-                    }
-                    uint32_t typeSize = dtohl(type->header.size);
-                    if ((typeSize&0x3) != 0) {
-                        printf("      NON-INTEGER ResTable_type header.size: 0x%x\n", typeSize);
-                        continue;
-                    }
-                    for (size_t entryIndex=0; entryIndex<entryCount; entryIndex++) {
 
-                        const uint8_t* const end = ((const uint8_t*)type)
-                            + dtohl(type->header.size);
-                        const uint32_t* const eindex = (const uint32_t*)
-                            (((const uint8_t*)type) + dtohs(type->header.headerSize));
-
-                        uint32_t thisOffset = dtohl(eindex[entryIndex]);
-                        if (thisOffset == ResTable_type::NO_ENTRY) {
-                            continue;
-                        }
-
-                        uint32_t resID = (0xff000000 & ((pkg->package->id)<<24))
-                                    | (0x00ff0000 & ((typeIndex+1)<<16))
-                                    | (0x0000ffff & (entryIndex));
-                        pg->dynamicRefTable.lookupResourceId(&resID);
-                        resource_name resName;
-                        if (this->getResourceName(resID, true, &resName)) {
-                            String8 type8;
-                            String8 name8;
-                            if (resName.type8 != NULL) {
-                                type8 = String8(resName.type8, resName.typeLen);
-                            } else {
-                                type8 = String8(resName.type, resName.typeLen);
-                            }
-                            if (resName.name8 != NULL) {
-                                name8 = String8(resName.name8, resName.nameLen);
-                            } else {
-                                name8 = String8(resName.name, resName.nameLen);
-                            }
-                            printf("        resource 0x%08x %s:%s/%s: ", resID,
-                                    CHAR16_TO_CSTR(resName.package, resName.packageLen),
-                                    type8.string(), name8.string());
+                    uint32_t resID = (0xff000000 & ((pg->id)<<24))
+                                | (0x00ff0000 & ((typeIndex+1)<<16))
+                                | (0x0000ffff & (entryIndex));
+                    pg->dynamicRefTable.lookupResourceId(&resID);
+                    resource_name resName;
+                    if (this->getResourceName(resID, true, &resName)) {
+                        String8 type8;
+                        String8 name8;
+                        if (resName.type8 != NULL) {
+                            type8 = String8(resName.type8, resName.typeLen);
                         } else {
-                            printf("        INVALID RESOURCE 0x%08x: ", resID);
+                            type8 = String8(resName.type, resName.typeLen);
                         }
-                        if ((thisOffset&0x3) != 0) {
-                            printf("NON-INTEGER OFFSET: 0x%x\n", thisOffset);
-                            continue;
-                        }
-                        if ((thisOffset+sizeof(ResTable_entry)) > typeSize) {
-                            printf("OFFSET OUT OF BOUNDS: 0x%x+0x%x (size is 0x%x)\n",
-                                   entriesStart, thisOffset, typeSize);
-                            continue;
-                        }
-
-                        const ResTable_entry* ent = (const ResTable_entry*)
-                            (((const uint8_t*)type) + entriesStart + thisOffset);
-                        if (((entriesStart + thisOffset)&0x3) != 0) {
-                            printf("NON-INTEGER ResTable_entry OFFSET: 0x%x\n",
-                                 (entriesStart + thisOffset));
-                            continue;
-                        }
-
-                        uintptr_t esize = dtohs(ent->size);
-                        if ((esize&0x3) != 0) {
-                            printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void *)esize);
-                            continue;
-                        }
-                        if ((thisOffset+esize) > typeSize) {
-                            printf("ResTable_entry OUT OF BOUNDS: 0x%x+0x%x+%p (size is 0x%x)\n",
-                                   entriesStart, thisOffset, (void *)esize, typeSize);
-                            continue;
-                        }
-
-                        const Res_value* valuePtr = NULL;
-                        const ResTable_map_entry* bagPtr = NULL;
-                        Res_value value;
-                        if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) {
-                            printf("<bag>");
-                            bagPtr = (const ResTable_map_entry*)ent;
+                        if (resName.name8 != NULL) {
+                            name8 = String8(resName.name8, resName.nameLen);
                         } else {
-                            valuePtr = (const Res_value*)
-                                (((const uint8_t*)ent) + esize);
-                            value.copyFrom_dtoh(*valuePtr);
-                            printf("t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)",
-                                   (int)value.dataType, (int)value.data,
-                                   (int)value.size, (int)value.res0);
+                            name8 = String8(resName.name, resName.nameLen);
                         }
+                        printf("        resource 0x%08x %s:%s/%s: ", resID,
+                                CHAR16_TO_CSTR(resName.package, resName.packageLen),
+                                type8.string(), name8.string());
+                    } else {
+                        printf("        INVALID RESOURCE 0x%08x: ", resID);
+                    }
+                    if ((thisOffset&0x3) != 0) {
+                        printf("NON-INTEGER OFFSET: 0x%x\n", thisOffset);
+                        continue;
+                    }
+                    if ((thisOffset+sizeof(ResTable_entry)) > typeSize) {
+                        printf("OFFSET OUT OF BOUNDS: 0x%x+0x%x (size is 0x%x)\n",
+                               entriesStart, thisOffset, typeSize);
+                        continue;
+                    }
 
-                        if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) {
-                            printf(" (PUBLIC)");
-                        }
-                        printf("\n");
+                    const ResTable_entry* ent = (const ResTable_entry*)
+                        (((const uint8_t*)type) + entriesStart + thisOffset);
+                    if (((entriesStart + thisOffset)&0x3) != 0) {
+                        printf("NON-INTEGER ResTable_entry OFFSET: 0x%x\n",
+                             (entriesStart + thisOffset));
+                        continue;
+                    }
 
-                        if (inclValues) {
-                            if (valuePtr != NULL) {
-                                printf("          ");
-                                print_value(pkg, value);
-                            } else if (bagPtr != NULL) {
-                                const int N = dtohl(bagPtr->count);
-                                const uint8_t* baseMapPtr = (const uint8_t*)ent;
-                                size_t mapOffset = esize;
-                                const ResTable_map* mapPtr = (ResTable_map*)(baseMapPtr+mapOffset);
-                                const uint32_t parent = dtohl(bagPtr->parent.ident);
-                                uint32_t resolvedParent = parent;
-                                status_t err = pg->dynamicRefTable.lookupResourceId(&resolvedParent);
-                                if (err != NO_ERROR) {
-                                    resolvedParent = 0;
-                                }
-                                printf("          Parent=0x%08x(Resolved=0x%08x), Count=%d\n",
-                                        parent, resolvedParent, N);
-                                for (int i=0; i<N && mapOffset < (typeSize-sizeof(ResTable_map)); i++) {
-                                    printf("          #%i (Key=0x%08x): ",
-                                        i, dtohl(mapPtr->name.ident));
-                                    value.copyFrom_dtoh(mapPtr->value);
-                                    print_value(pkg, value);
-                                    const size_t size = dtohs(mapPtr->value.size);
-                                    mapOffset += size + sizeof(*mapPtr)-sizeof(mapPtr->value);
-                                    mapPtr = (ResTable_map*)(baseMapPtr+mapOffset);
-                                }
+                    uintptr_t esize = dtohs(ent->size);
+                    if ((esize&0x3) != 0) {
+                        printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void *)esize);
+                        continue;
+                    }
+                    if ((thisOffset+esize) > typeSize) {
+                        printf("ResTable_entry OUT OF BOUNDS: 0x%x+0x%x+%p (size is 0x%x)\n",
+                               entriesStart, thisOffset, (void *)esize, typeSize);
+                        continue;
+                    }
+
+                    const Res_value* valuePtr = NULL;
+                    const ResTable_map_entry* bagPtr = NULL;
+                    Res_value value;
+                    if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) {
+                        printf("<bag>");
+                        bagPtr = (const ResTable_map_entry*)ent;
+                    } else {
+                        valuePtr = (const Res_value*)
+                            (((const uint8_t*)ent) + esize);
+                        value.copyFrom_dtoh(*valuePtr);
+                        printf("t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)",
+                               (int)value.dataType, (int)value.data,
+                               (int)value.size, (int)value.res0);
+                    }
+
+                    if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) {
+                        printf(" (PUBLIC)");
+                    }
+                    printf("\n");
+
+                    if (inclValues) {
+                        if (valuePtr != NULL) {
+                            printf("          ");
+                            print_value(typeConfigs->package, value);
+                        } else if (bagPtr != NULL) {
+                            const int N = dtohl(bagPtr->count);
+                            const uint8_t* baseMapPtr = (const uint8_t*)ent;
+                            size_t mapOffset = esize;
+                            const ResTable_map* mapPtr = (ResTable_map*)(baseMapPtr+mapOffset);
+                            const uint32_t parent = dtohl(bagPtr->parent.ident);
+                            uint32_t resolvedParent = parent;
+                            status_t err = pg->dynamicRefTable.lookupResourceId(&resolvedParent);
+                            if (err != NO_ERROR) {
+                                resolvedParent = 0;
+                            }
+                            printf("          Parent=0x%08x(Resolved=0x%08x), Count=%d\n",
+                                    parent, resolvedParent, N);
+                            for (int i=0; i<N && mapOffset < (typeSize-sizeof(ResTable_map)); i++) {
+                                printf("          #%i (Key=0x%08x): ",
+                                    i, dtohl(mapPtr->name.ident));
+                                value.copyFrom_dtoh(mapPtr->value);
+                                print_value(typeConfigs->package, value);
+                                const size_t size = dtohs(mapPtr->value.size);
+                                mapOffset += size + sizeof(*mapPtr)-sizeof(mapPtr->value);
+                                mapPtr = (ResTable_map*)(baseMapPtr+mapOffset);
                             }
                         }
                     }
diff --git a/libs/androidfw/TypeWrappers.cpp b/libs/androidfw/TypeWrappers.cpp
new file mode 100644
index 0000000..06b4040
--- /dev/null
+++ b/libs/androidfw/TypeWrappers.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <androidfw/TypeWrappers.h>
+
+namespace android {
+
+TypeVariant::iterator& TypeVariant::iterator::operator++() {
+    mIndex++;
+    if (mIndex > dtohl(mTypeVariant->data->entryCount)) {
+        mIndex = dtohl(mTypeVariant->data->entryCount);
+    }
+    return *this;
+}
+
+const ResTable_entry* TypeVariant::iterator::operator*() const {
+    const ResTable_type* type = mTypeVariant->data;
+    const uint32_t entryCount = dtohl(type->entryCount);
+    if (mIndex >= entryCount) {
+        return NULL;
+    }
+
+    const uintptr_t containerEnd = reinterpret_cast<uintptr_t>(type)
+            + dtohl(type->header.size);
+    const uint32_t* const entryIndices = reinterpret_cast<const uint32_t*>(
+            reinterpret_cast<uintptr_t>(type) + dtohs(type->header.headerSize));
+    if (reinterpret_cast<uintptr_t>(entryIndices) + (sizeof(uint32_t) * entryCount) > containerEnd) {
+        ALOGE("Type's entry indices extend beyond its boundaries");
+        return NULL;
+    }
+
+    const uint32_t entryOffset = dtohl(entryIndices[mIndex]);
+    if (entryOffset == ResTable_type::NO_ENTRY) {
+        return NULL;
+    }
+
+    if ((entryOffset & 0x3) != 0) {
+        ALOGE("Index %u points to entry with unaligned offset 0x%08x", mIndex, entryOffset);
+        return NULL;
+    }
+
+    const ResTable_entry* entry = reinterpret_cast<const ResTable_entry*>(
+            reinterpret_cast<uintptr_t>(type) + dtohl(type->entriesStart) + entryOffset);
+    if (reinterpret_cast<uintptr_t>(entry) > containerEnd - sizeof(*entry)) {
+        ALOGE("Entry offset at index %u points outside the Type's boundaries", mIndex);
+        return NULL;
+    } else if (reinterpret_cast<uintptr_t>(entry) + dtohs(entry->size) > containerEnd) {
+        ALOGE("Entry at index %u extends beyond Type's boundaries", mIndex);
+        return NULL;
+    } else if (dtohs(entry->size) < sizeof(*entry)) {
+        ALOGE("Entry at index %u is too small (%u)", mIndex, dtohs(entry->size));
+        return NULL;
+    }
+    return entry;
+}
+
+} // namespace android
diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk
index 9e9649c..4ff6eec 100644
--- a/libs/androidfw/tests/Android.mk
+++ b/libs/androidfw/tests/Android.mk
@@ -1,33 +1,66 @@
-# Build the unit tests.
+#
+# 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.
+#
+
+# ==========================================================
+# Setup some common variables for the different build
+# targets here.
+# ==========================================================
 LOCAL_PATH:= $(call my-dir)
+testFiles := \
+    ByteBucketArray_test.cpp \
+    Idmap_test.cpp \
+    ResourceTypes_test.cpp \
+    ResTable_test.cpp \
+    Split_test.cpp \
+    TypeWrappers_test.cpp \
+    ZipUtils_test.cpp
+
+# ==========================================================
+# Build the host tests: libandroidfw_tests
+# ==========================================================
 include $(CLEAR_VARS)
 
-# Build the unit tests.
-test_src_files := \
-    BackupData_test.cpp \
-    ObbFile_test.cpp \
-    ZipUtils_test.cpp \
-    ResourceTypes_test.cpp
+LOCAL_MODULE := libandroidfw_tests
 
-shared_libraries := \
+LOCAL_SRC_FILES := $(testFiles)
+LOCAL_STATIC_LIBRARIES := \
+    libandroidfw \
+    libutils \
+    libcutils \
+	liblog
+
+include $(BUILD_HOST_NATIVE_TEST)
+
+
+# ==========================================================
+# Build the device tests: libandroidfw_tests
+# ==========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libandroidfw_tests
+
+LOCAL_SRC_FILES := $(testFiles) \
+    BackupData_test.cpp \
+    ObbFile_test.cpp
+
+LOCAL_SHARED_LIBRARIES := \
     libandroidfw \
     libcutils \
     libutils \
     libui \
     libstlport
 
-static_libraries := \
-    libgtest \
-    libgtest_main
-
-$(foreach file,$(test_src_files), \
-    $(eval include $(CLEAR_VARS)) \
-    $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
-    $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \
-    $(eval LOCAL_SRC_FILES := $(file)) \
-    $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
-    $(eval include $(BUILD_NATIVE_TEST)) \
-)
-
-# Build the manual test programs.
-include $(call all-makefiles-under, $(LOCAL_PATH))
+include $(BUILD_NATIVE_TEST)
diff --git a/libs/androidfw/tests/ByteBucketArray_test.cpp b/libs/androidfw/tests/ByteBucketArray_test.cpp
new file mode 100644
index 0000000..376e79c
--- /dev/null
+++ b/libs/androidfw/tests/ByteBucketArray_test.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <androidfw/ByteBucketArray.h>
+
+#include <gtest/gtest.h>
+
+using android::ByteBucketArray;
+
+TEST(ByteBucketArrayTest, TestSparseInsertion) {
+    ByteBucketArray<int> bba;
+    ASSERT_TRUE(bba.set(0, 1));
+    ASSERT_TRUE(bba.set(10, 2));
+    ASSERT_TRUE(bba.set(26, 3));
+    ASSERT_TRUE(bba.set(129, 4));
+    ASSERT_TRUE(bba.set(234, 5));
+
+    for (size_t i = 0; i < bba.size(); i++) {
+        switch (i) {
+            case 0: EXPECT_EQ(1, bba[i]); break;
+            case 10: EXPECT_EQ(2, bba[i]); break;
+            case 26: EXPECT_EQ(3, bba[i]); break;
+            case 129: EXPECT_EQ(4, bba[i]); break;
+            case 234: EXPECT_EQ(5, bba[i]); break;
+            default: EXPECT_EQ(0, bba[i]); break;
+        }
+    }
+}
diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp
new file mode 100644
index 0000000..d829b76
--- /dev/null
+++ b/libs/androidfw/tests/Idmap_test.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <androidfw/ResourceTypes.h>
+
+#include <utils/String8.h>
+#include <utils/String16.h>
+#include "TestHelpers.h"
+
+#include <gtest/gtest.h>
+
+using namespace android;
+
+namespace {
+
+/**
+ * Include a binary resource table.
+ *
+ * Package: com.android.test.basic
+ */
+#include "data/basic/basic_arsc.h"
+
+/**
+ * Include a binary resource table.
+ * This table is an overlay.
+ *
+ * Package: com.android.test.basic
+ */
+#include "data/overlay/overlay_arsc.h"
+
+enum { MAY_NOT_BE_BAG = false };
+
+static const uint32_t attr_attr1            = 0x7f010000;
+static const uint32_t attr_attr2            = 0x7f010001;
+static const uint32_t string_test1          = 0x7f020000;
+static const uint32_t string_test2          = 0x7f020001;
+static const uint32_t integer_number1       = 0x7f030000;
+static const uint32_t integer_number2       = 0x7f030001;
+static const uint32_t style_Theme1          = 0x7f040000;
+static const uint32_t style_Theme2          = 0x7f040001;
+static const uint32_t array_integerArray1   = 0x7f050000;
+
+class IdmapTest : public ::testing::Test {
+protected:
+    virtual void SetUp() {
+        ASSERT_EQ(NO_ERROR, mTargetTable.add(basic_arsc, basic_arsc_len));
+        ASSERT_EQ(NO_ERROR, mOverlayTable.add(overlay_arsc, overlay_arsc_len));
+        char targetName[256] = "com.android.test.basic";
+        ASSERT_EQ(NO_ERROR, mTargetTable.createIdmap(mOverlayTable, 0, 0,
+                    targetName, targetName, &mData, &mDataSize));
+    }
+
+    virtual void TearDown() {
+        free(mData);
+    }
+
+    ResTable mTargetTable;
+    ResTable mOverlayTable;
+    void* mData;
+    size_t mDataSize;
+};
+
+TEST_F(IdmapTest, canLoadIdmap) {
+    ASSERT_EQ(NO_ERROR, mTargetTable.add(overlay_arsc, overlay_arsc_len, mData, mDataSize));
+}
+
+TEST_F(IdmapTest, overlayOverridesResourceValue) {
+    Res_value val;
+    ssize_t block = mTargetTable.getResource(string_test2, &val, false);
+    ASSERT_GE(block, 0);
+    ASSERT_EQ(Res_value::TYPE_STRING, val.dataType);
+    const ResStringPool* pool = mTargetTable.getTableStringBlock(block);
+    ASSERT_TRUE(pool != NULL);
+    ASSERT_LT(val.data, pool->size());
+
+    size_t strLen;
+    const char16_t* targetStr16 = pool->stringAt(val.data, &strLen);
+    ASSERT_TRUE(targetStr16 != NULL);
+    ASSERT_EQ(String16("test2"), String16(targetStr16, strLen));
+
+    ASSERT_EQ(NO_ERROR, mTargetTable.add(overlay_arsc, overlay_arsc_len, mData, mDataSize));
+
+    ssize_t newBlock = mTargetTable.getResource(string_test2, &val, false);
+    ASSERT_GE(newBlock, 0);
+    ASSERT_NE(block, newBlock);
+    ASSERT_EQ(Res_value::TYPE_STRING, val.dataType);
+    pool = mTargetTable.getTableStringBlock(newBlock);
+    ASSERT_TRUE(pool != NULL);
+    ASSERT_LT(val.data, pool->size());
+
+    targetStr16 = pool->stringAt(val.data, &strLen);
+    ASSERT_TRUE(targetStr16 != NULL);
+    ASSERT_EQ(String16("test2-overlay"), String16(targetStr16, strLen));
+}
+
+TEST_F(IdmapTest, overlaidResourceHasSameName) {
+    ASSERT_EQ(NO_ERROR, mTargetTable.add(overlay_arsc, overlay_arsc_len, mData, mDataSize));
+
+    ResTable::resource_name resName;
+    ASSERT_TRUE(mTargetTable.getResourceName(array_integerArray1, false, &resName));
+
+    ASSERT_TRUE(resName.package != NULL);
+    ASSERT_TRUE(resName.type != NULL);
+    ASSERT_TRUE(resName.name != NULL);
+
+    EXPECT_EQ(String16("com.android.test.basic"), String16(resName.package, resName.packageLen));
+    EXPECT_EQ(String16("array"), String16(resName.type, resName.typeLen));
+    EXPECT_EQ(String16("integerArray1"), String16(resName.name, resName.nameLen));
+}
+
+} // namespace
diff --git a/libs/androidfw/tests/ResTable_test.cpp b/libs/androidfw/tests/ResTable_test.cpp
new file mode 100644
index 0000000..54d42c3
--- /dev/null
+++ b/libs/androidfw/tests/ResTable_test.cpp
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <androidfw/ResourceTypes.h>
+
+#include <utils/String8.h>
+#include <utils/String16.h>
+#include "TestHelpers.h"
+
+#include <gtest/gtest.h>
+
+using namespace android;
+
+namespace {
+
+/**
+ * Include a binary resource table.
+ *
+ * Package: com.android.test.basic
+ */
+#include "data/basic/basic_arsc.h"
+
+enum { MAY_NOT_BE_BAG = false };
+
+static const uint32_t attr_attr1            = 0x7f010000;
+static const uint32_t attr_attr2            = 0x7f010001;
+static const uint32_t string_test1          = 0x7f020000;
+static const uint32_t string_test2          = 0x7f020001;
+static const uint32_t integer_number1       = 0x7f030000;
+static const uint32_t integer_number2       = 0x7f030001;
+static const uint32_t style_Theme1          = 0x7f040000;
+static const uint32_t style_Theme2          = 0x7f040001;
+static const uint32_t array_integerArray1   = 0x7f050000;
+
+TEST(ResTableTest, shouldLoadSuccessfully) {
+    ResTable table;
+    ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+}
+
+TEST(ResTableTest, simpleTypeIsRetrievedCorrectly) {
+    ResTable table;
+    ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+    Res_value val;
+    ssize_t block = table.getResource(string_test1, &val, MAY_NOT_BE_BAG);
+
+    ASSERT_GE(block, 0);
+    ASSERT_EQ(Res_value::TYPE_STRING, val.dataType);
+
+    const ResStringPool* pool = table.getTableStringBlock(block);
+    ASSERT_TRUE(NULL != pool);
+    ASSERT_EQ(String8("test1"), pool->string8ObjectAt(val.data));
+}
+
+TEST(ResTableTest, resourceNameIsResolved) {
+    ResTable table;
+    ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+    String16 defPackage("com.android.test.basic");
+    String16 testName("@string/test1");
+    uint32_t resID = table.identifierForName(testName.string(), testName.size(),
+                                             0, 0,
+                                             defPackage.string(), defPackage.size());
+    ASSERT_NE(uint32_t(0x00000000), resID);
+    ASSERT_EQ(string_test1, resID);
+}
+
+TEST(ResTableTest, noParentThemeIsAppliedCorrectly) {
+    ResTable table;
+    ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+    ResTable::Theme theme(table);
+    ASSERT_EQ(NO_ERROR, theme.applyStyle(style_Theme1));
+
+    Res_value val;
+    uint32_t specFlags = 0;
+    ssize_t index = theme.getAttribute(attr_attr1, &val, &specFlags);
+    ASSERT_GE(index, 0);
+    ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+    ASSERT_EQ(uint32_t(100), val.data);
+
+    index = theme.getAttribute(attr_attr2, &val, &specFlags);
+    ASSERT_GE(index, 0);
+    ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
+    ASSERT_EQ(integer_number1, val.data);
+}
+
+TEST(ResTableTest, parentThemeIsAppliedCorrectly) {
+    ResTable table;
+    ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+    ResTable::Theme theme(table);
+    ASSERT_EQ(NO_ERROR, theme.applyStyle(style_Theme2));
+
+    Res_value val;
+    uint32_t specFlags = 0;
+    ssize_t index = theme.getAttribute(attr_attr1, &val, &specFlags);
+    ASSERT_GE(index, 0);
+    ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+    ASSERT_EQ(uint32_t(300), val.data);
+
+    index = theme.getAttribute(attr_attr2, &val, &specFlags);
+    ASSERT_GE(index, 0);
+    ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
+    ASSERT_EQ(integer_number1, val.data);
+}
+
+TEST(ResTableTest, referenceToBagIsNotResolved) {
+    ResTable table;
+    ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+    Res_value val;
+    ssize_t block = table.getResource(integer_number2, &val, MAY_NOT_BE_BAG);
+    ASSERT_GE(block, 0);
+    ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
+    ASSERT_EQ(array_integerArray1, val.data);
+
+    ssize_t newBlock = table.resolveReference(&val, block);
+    EXPECT_EQ(block, newBlock);
+    EXPECT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
+    EXPECT_EQ(array_integerArray1, val.data);
+}
+
+TEST(ResTableTest, resourcesStillAccessibleAfterParameterChange) {
+    ResTable table;
+    ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+    Res_value val;
+    ssize_t block = table.getResource(integer_number1, &val, MAY_NOT_BE_BAG);
+    ASSERT_GE(block, 0);
+    ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+
+    const ResTable::bag_entry* entry;
+    ssize_t count = table.lockBag(array_integerArray1, &entry);
+    ASSERT_GE(count, 0);
+    table.unlockBag(entry);
+
+    ResTable_config param;
+    memset(&param, 0, sizeof(param));
+    param.density = 320;
+    table.setParameters(&param);
+
+    block = table.getResource(integer_number1, &val, MAY_NOT_BE_BAG);
+    ASSERT_GE(block, 0);
+    ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+
+    count = table.lockBag(array_integerArray1, &entry);
+    ASSERT_GE(count, 0);
+    table.unlockBag(entry);
+}
+
+TEST(ResTableTest, resourceIsOverridenWithBetterConfig) {
+    ResTable table;
+    ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+    Res_value val;
+    ssize_t block = table.getResource(integer_number1, &val, MAY_NOT_BE_BAG);
+    ASSERT_GE(block, 0);
+    ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+    ASSERT_EQ(uint32_t(200), val.data);
+
+    ResTable_config param;
+    memset(&param, 0, sizeof(param));
+    param.language[0] = 's';
+    param.language[1] = 'v';
+    param.country[0] = 'S';
+    param.country[1] = 'E';
+    table.setParameters(&param);
+
+    block = table.getResource(integer_number1, &val, MAY_NOT_BE_BAG);
+    ASSERT_GE(block, 0);
+    ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+    ASSERT_EQ(uint32_t(400), val.data);
+}
+
+}
diff --git a/libs/androidfw/tests/ResourceTypes_test.cpp b/libs/androidfw/tests/ResourceTypes_test.cpp
index 4888b4a..6041e08 100644
--- a/libs/androidfw/tests/ResourceTypes_test.cpp
+++ b/libs/androidfw/tests/ResourceTypes_test.cpp
@@ -64,8 +64,8 @@
      config.packLanguage("eng");
 
      // 1-00110-01 101-00100
-     EXPECT_EQ(0x99, config.language[0]);
-     EXPECT_EQ(0xa4, config.language[1]);
+     EXPECT_EQ('\x99', config.language[0]);
+     EXPECT_EQ('\xA4', config.language[1]);
 
      char out[4] = { 1, 1, 1, 1};
      config.unpackLanguage(out);
diff --git a/libs/androidfw/tests/Split_test.cpp b/libs/androidfw/tests/Split_test.cpp
new file mode 100644
index 0000000..dbfdeae
--- /dev/null
+++ b/libs/androidfw/tests/Split_test.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <androidfw/ResourceTypes.h>
+
+#include <utils/String8.h>
+#include <utils/String16.h>
+#include "TestHelpers.h"
+
+#include <gtest/gtest.h>
+
+/**
+ * Include a binary resource table. This table
+ * is a base table for an APK split.
+ *
+ * Package: com.android.example.split
+ *
+ * layout/main          0x7f020000 {default, fr-sw600dp-v13}
+ *
+ * string/app_title     0x7f030000 {default}
+ * string/test          0x7f030001 {default}
+ * string/boom          0x7f030002 {default}
+ * string/blah          0x7f030003 {default}
+ *
+ * array/lotsofstrings  0x7f040000 {default}
+ * array/numList        0x7f040001 {default}
+ * array/ary            0x7f040002 {default}
+ *
+ */
+#include "data/split_base_arsc.h"
+
+/**
+ * Include a binary resource table. This table
+ * is a configuration split table for an APK split.
+ *
+ * Package: com.android.example.split
+ *
+ * string/app_title     0x7f030000 {fr}
+ * string/test          0x7f030001 {de,fr}
+ * string/blah          0x7f030003 {fr}
+ *
+ * array/lotsofstrings  0x7f040000 {fr}
+ *
+ */
+#include "data/split_de_fr_arsc.h"
+
+
+using namespace android;
+
+enum { MAY_NOT_BE_BAG = false };
+
+void makeConfigFrench(ResTable_config* config) {
+    memset(config, 0, sizeof(*config));
+    config->language[0] = 'f';
+    config->language[1] = 'r';
+}
+
+TEST(SplitTest, TestLoadBase) {
+    ResTable table;
+    ASSERT_EQ(NO_ERROR, table.add(split_base_arsc, split_base_arsc_len));
+}
+
+TEST(SplitTest, TestGetResourceFromBase) {
+    ResTable_config frenchConfig;
+    makeConfigFrench(&frenchConfig);
+
+    ResTable table;
+    table.setParameters(&frenchConfig);
+
+    ASSERT_EQ(NO_ERROR, table.add(split_base_arsc, split_base_arsc_len));
+
+    ResTable_config expectedConfig;
+    memset(&expectedConfig, 0, sizeof(expectedConfig));
+
+    Res_value val;
+    ResTable_config config;
+    ssize_t block = table.getResource(0x7f030000, &val, MAY_NOT_BE_BAG, 0, NULL, &config);
+
+    // The returned block should tell us which string pool to get the value, if it is a string.
+    EXPECT_GE(block, 0);
+
+    // We expect the default resource to be selected since it is the only resource configuration.
+    EXPECT_EQ(0, expectedConfig.compare(config));
+
+    EXPECT_EQ(Res_value::TYPE_STRING, val.dataType);
+}
+
+TEST(SplitTest, TestGetResourceFromSplit) {
+    ResTable_config expectedConfig;
+    makeConfigFrench(&expectedConfig);
+
+    ResTable table;
+    table.setParameters(&expectedConfig);
+
+    ASSERT_EQ(NO_ERROR, table.add(split_base_arsc, split_base_arsc_len));
+    ASSERT_EQ(NO_ERROR, table.add(split_de_fr_arsc, split_de_fr_arsc_len));
+
+    Res_value val;
+    ResTable_config config;
+    ssize_t block = table.getResource(0x7f030000, &val, MAY_NOT_BE_BAG, 0, NULL, &config);
+
+    EXPECT_GE(block, 0);
+
+    EXPECT_EQ(0, expectedConfig.compare(config));
+
+    EXPECT_EQ(Res_value::TYPE_STRING, val.dataType);
+}
+
+TEST(SplitTest, ResourcesFromBaseAndSplitHaveSameNames) {
+    ResTable_config expectedConfig;
+    makeConfigFrench(&expectedConfig);
+
+    ResTable table;
+    table.setParameters(&expectedConfig);
+
+    ASSERT_EQ(NO_ERROR, table.add(split_base_arsc, split_base_arsc_len));
+
+    ResTable::resource_name baseName;
+    EXPECT_TRUE(table.getResourceName(0x7f030003, false, &baseName));
+
+    ASSERT_EQ(NO_ERROR, table.add(split_de_fr_arsc, split_de_fr_arsc_len));
+
+    ResTable::resource_name frName;
+    EXPECT_TRUE(table.getResourceName(0x7f030003, false, &frName));
+
+    EXPECT_EQ(
+            String16(baseName.package, baseName.packageLen),
+            String16(frName.package, frName.packageLen));
+
+    EXPECT_EQ(
+            String16(baseName.type, baseName.typeLen),
+            String16(frName.type, frName.typeLen));
+
+    EXPECT_EQ(
+            String16(baseName.name, baseName.nameLen),
+            String16(frName.name, frName.nameLen));
+}
+
+TEST(SplitTest, TypeEntrySpecFlagsAreUpdated) {
+    ResTable_config defaultConfig;
+    memset(&defaultConfig, 0, sizeof(defaultConfig));
+
+    ResTable table;
+    ASSERT_EQ(NO_ERROR, table.add(split_base_arsc, split_base_arsc_len));
+
+    Res_value val;
+    uint32_t specFlags = 0;
+    ssize_t block = table.getResource(0x7f030000, &val, MAY_NOT_BE_BAG, 0, &specFlags, NULL);
+    EXPECT_GE(block, 0);
+
+    EXPECT_EQ(static_cast<uint32_t>(0), specFlags);
+
+    ASSERT_EQ(NO_ERROR, table.add(split_de_fr_arsc, split_de_fr_arsc_len));
+
+    uint32_t frSpecFlags = 0;
+    block = table.getResource(0x7f030000, &val, MAY_NOT_BE_BAG, 0, &frSpecFlags, NULL);
+    EXPECT_GE(block, 0);
+
+    EXPECT_EQ(ResTable_config::CONFIG_LOCALE, frSpecFlags);
+}
diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h
new file mode 100644
index 0000000..75a233a
--- /dev/null
+++ b/libs/androidfw/tests/TestHelpers.h
@@ -0,0 +1,17 @@
+#ifndef __TEST_HELPERS_H
+#define __TEST_HELPERS_H
+
+#include <ostream>
+
+#include <utils/String8.h>
+#include <utils/String16.h>
+
+static inline ::std::ostream& operator<<(::std::ostream& out, const android::String8& str) {
+    return out << str.string();
+}
+
+static inline ::std::ostream& operator<<(::std::ostream& out, const android::String16& str) {
+    return out << android::String8(str).string();
+}
+
+#endif // __TEST_HELPERS_H
diff --git a/libs/androidfw/tests/TypeWrappers_test.cpp b/libs/androidfw/tests/TypeWrappers_test.cpp
new file mode 100644
index 0000000..d69abe5
--- /dev/null
+++ b/libs/androidfw/tests/TypeWrappers_test.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <androidfw/ResourceTypes.h>
+#include <androidfw/TypeWrappers.h>
+#include <utils/String8.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+void* createTypeData() {
+    ResTable_type t;
+    memset(&t, 0, sizeof(t));
+    t.header.type = RES_TABLE_TYPE_TYPE;
+    t.header.headerSize = sizeof(t);
+    t.id = 1;
+    t.entryCount = 3;
+
+    uint32_t offsets[3];
+    t.entriesStart = t.header.headerSize + sizeof(offsets);
+    t.header.size = t.entriesStart;
+
+    offsets[0] = 0;
+    ResTable_entry e1;
+    memset(&e1, 0, sizeof(e1));
+    e1.size = sizeof(e1);
+    e1.key.index = 0;
+    t.header.size += sizeof(e1);
+
+    Res_value v1;
+    memset(&v1, 0, sizeof(v1));
+    t.header.size += sizeof(v1);
+
+    offsets[1] = ResTable_type::NO_ENTRY;
+
+    offsets[2] = sizeof(e1) + sizeof(v1);
+    ResTable_entry e2;
+    memset(&e2, 0, sizeof(e2));
+    e2.size = sizeof(e2);
+    e2.key.index = 1;
+    t.header.size += sizeof(e2);
+
+    Res_value v2;
+    memset(&v2, 0, sizeof(v2));
+    t.header.size += sizeof(v2);
+
+    uint8_t* data = (uint8_t*)malloc(t.header.size);
+    uint8_t* p = data;
+    memcpy(p, &t, sizeof(t));
+    p += sizeof(t);
+    memcpy(p, offsets, sizeof(offsets));
+    p += sizeof(offsets);
+    memcpy(p, &e1, sizeof(e1));
+    p += sizeof(e1);
+    memcpy(p, &v1, sizeof(v1));
+    p += sizeof(v1);
+    memcpy(p, &e2, sizeof(e2));
+    p += sizeof(e2);
+    memcpy(p, &v2, sizeof(v2));
+    p += sizeof(v2);
+    return data;
+}
+
+TEST(TypeVariantIteratorTest, shouldIterateOverTypeWithoutErrors) {
+    ResTable_type* data = (ResTable_type*) createTypeData();
+
+    TypeVariant v(data);
+
+    TypeVariant::iterator iter = v.beginEntries();
+    ASSERT_EQ(uint32_t(0), iter.index());
+    ASSERT_TRUE(NULL != *iter);
+    ASSERT_EQ(uint32_t(0), iter->key.index);
+    ASSERT_NE(v.endEntries(), iter);
+
+    iter++;
+
+    ASSERT_EQ(uint32_t(1), iter.index());
+    ASSERT_TRUE(NULL == *iter);
+    ASSERT_NE(v.endEntries(), iter);
+
+    iter++;
+
+    ASSERT_EQ(uint32_t(2), iter.index());
+    ASSERT_TRUE(NULL != *iter);
+    ASSERT_EQ(uint32_t(1), iter->key.index);
+    ASSERT_NE(v.endEntries(), iter);
+
+    iter++;
+
+    ASSERT_EQ(v.endEntries(), iter);
+
+    free(data);
+}
+
+} // namespace android
diff --git a/libs/androidfw/tests/data/.gitignore b/libs/androidfw/tests/data/.gitignore
new file mode 100644
index 0000000..c05cfb0
--- /dev/null
+++ b/libs/androidfw/tests/data/.gitignore
@@ -0,0 +1,2 @@
+*.apk
+*.arsc
diff --git a/libs/androidfw/tests/data/basic/AndroidManifest.xml b/libs/androidfw/tests/data/basic/AndroidManifest.xml
new file mode 100644
index 0000000..a56ac18
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.test.basic">
+    <application>
+    </application>
+</manifest>
diff --git a/libs/androidfw/tests/data/basic/basic_arsc.h b/libs/androidfw/tests/data/basic/basic_arsc.h
new file mode 100644
index 0000000..6532076
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/basic_arsc.h
@@ -0,0 +1,131 @@
+unsigned char basic_arsc[] = {
+  0x02, 0x00, 0x0c, 0x00, 0xfc, 0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+  0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x31, 0x00,
+  0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
+  0x32, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xb0, 0x05, 0x00, 0x00,
+  0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00,
+  0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00,
+  0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
+  0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
+  0x05, 0x00, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x7c, 0x00, 0x00, 0x00,
+  0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00,
+  0x3c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00,
+  0x72, 0x00, 0x00, 0x00, 0x06, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00,
+  0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x00, 0x00, 0x07, 0x00, 0x69, 0x00,
+  0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00,
+  0x00, 0x00, 0x05, 0x00, 0x73, 0x00, 0x74, 0x00, 0x79, 0x00, 0x6c, 0x00,
+  0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00,
+  0x61, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00,
+  0xdc, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+  0x2a, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00,
+  0x5c, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00,
+  0x05, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x31, 0x00,
+  0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00,
+  0x32, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00,
+  0x74, 0x00, 0x31, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00,
+  0x73, 0x00, 0x74, 0x00, 0x32, 0x00, 0x00, 0x00, 0x07, 0x00, 0x6e, 0x00,
+  0x75, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x31, 0x00,
+  0x00, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x62, 0x00,
+  0x65, 0x00, 0x72, 0x00, 0x32, 0x00, 0x00, 0x00, 0x06, 0x00, 0x54, 0x00,
+  0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x31, 0x00, 0x00, 0x00,
+  0x06, 0x00, 0x54, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00,
+  0x32, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00,
+  0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x41, 0x00, 0x72, 0x00,
+  0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x02, 0x44, 0x00, 0x84, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x1c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+  0x08, 0x00, 0x00, 0x10, 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x10, 0x05, 0x00, 0x00, 0x00,
+  0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x02, 0x44, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00,
+  0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x02, 0x44, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x00, 0x10, 0xc8, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0x7f,
+  0x01, 0x02, 0x44, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x73, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x00, 0x10, 0x90, 0x01, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+  0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+  0x90, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
+  0x10, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7f, 0x08, 0x00, 0x00, 0x10,
+  0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x7f, 0x08, 0x00, 0x00, 0x01,
+  0x00, 0x00, 0x03, 0x7f, 0x10, 0x00, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x04, 0x7f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7f,
+  0x08, 0x00, 0x00, 0x10, 0x2c, 0x01, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+  0x14, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x7c, 0x00, 0x00, 0x00,
+  0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00,
+  0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+  0x08, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02,
+  0x08, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02,
+  0x08, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, 0x00
+};
+unsigned int basic_arsc_len = 1532;
diff --git a/libs/androidfw/tests/data/basic/build b/libs/androidfw/tests/data/basic/build
new file mode 100755
index 0000000..237342c
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/build
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+aapt package -M AndroidManifest.xml -S res -F bundle.apk -f && \
+unzip bundle.apk resources.arsc && \
+mv resources.arsc basic.arsc && \
+xxd -i basic.arsc > basic_arsc.h
diff --git a/libs/androidfw/tests/data/basic/res/values-sv/values.xml b/libs/androidfw/tests/data/basic/res/values-sv/values.xml
new file mode 100644
index 0000000..9d52307
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/res/values-sv/values.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <integer name="number1">400</integer>
+</resources>
diff --git a/libs/androidfw/tests/data/basic/res/values/values.xml b/libs/androidfw/tests/data/basic/res/values/values.xml
new file mode 100644
index 0000000..662eda6
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/res/values/values.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <attr name="attr1" format="reference|integer" />
+    <attr name="attr2" format="reference|integer" />
+
+    <string name="test1">test1</string>
+    <string name="test2">test2</string>
+
+    <integer name="number1">200</integer>
+    <integer name="number2">@array/integerArray1</integer>
+
+    <style name="Theme1">
+        <item name="com.android.test.basic:attr1">100</item>
+        <item name="com.android.test.basic:attr2">@integer/number1</item>
+    </style>
+
+    <style name="Theme2" parent="@com.android.test.basic:style/Theme1">
+        <item name="com.android.test.basic:attr1">300</item>
+    </style>
+
+    <integer-array name="integerArray1">
+        <item>1</item>
+        <item>2</item>
+        <item>3</item>
+    </integer-array>
+</resources>
diff --git a/libs/androidfw/tests/data/overlay/AndroidManifest.xml b/libs/androidfw/tests/data/overlay/AndroidManifest.xml
new file mode 100644
index 0000000..a56ac18
--- /dev/null
+++ b/libs/androidfw/tests/data/overlay/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.test.basic">
+    <application>
+    </application>
+</manifest>
diff --git a/libs/androidfw/tests/data/overlay/build b/libs/androidfw/tests/data/overlay/build
new file mode 100755
index 0000000..87cf6de
--- /dev/null
+++ b/libs/androidfw/tests/data/overlay/build
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+aapt package -M AndroidManifest.xml -S res -F bundle.apk -f && \
+unzip bundle.apk resources.arsc && \
+mv resources.arsc overlay.arsc && \
+xxd -i overlay.arsc > overlay_arsc.h
diff --git a/libs/androidfw/tests/data/overlay/overlay_arsc.h b/libs/androidfw/tests/data/overlay/overlay_arsc.h
new file mode 100644
index 0000000..5bd98b2
--- /dev/null
+++ b/libs/androidfw/tests/data/overlay/overlay_arsc.h
@@ -0,0 +1,69 @@
+unsigned char overlay_arsc[] = {
+  0x02, 0x00, 0x0c, 0x00, 0x10, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x74, 0x00,
+  0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x6f, 0x00,
+  0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xc4, 0x02, 0x00, 0x00,
+  0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00,
+  0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00,
+  0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
+  0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
+  0x03, 0x00, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x54, 0x00, 0x00, 0x00,
+  0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00,
+  0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00, 0x06, 0x00, 0x73, 0x00,
+  0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x00, 0x00,
+  0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x50, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x0e, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00,
+  0x74, 0x00, 0x32, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x69, 0x00, 0x6e, 0x00,
+  0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x41, 0x00,
+  0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x31, 0x00, 0x00, 0x00,
+  0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x02, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
+  0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
+  0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x02, 0x44, 0x00, 0x70, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x10, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
+  0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
+  0x0b, 0x00, 0x00, 0x00
+};
+unsigned int overlay_arsc_len = 784;
diff --git a/libs/androidfw/tests/data/overlay/res/values/values.xml b/libs/androidfw/tests/data/overlay/res/values/values.xml
new file mode 100644
index 0000000..227e889
--- /dev/null
+++ b/libs/androidfw/tests/data/overlay/res/values/values.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="test2">test2-overlay</string>
+    <integer-array name="integerArray1">
+        <item>10</item>
+        <item>11</item>
+    </integer-array>
+</resources>
diff --git a/libs/androidfw/tests/data/split_base_arsc.h b/libs/androidfw/tests/data/split_base_arsc.h
new file mode 100644
index 0000000..e0321e9
--- /dev/null
+++ b/libs/androidfw/tests/data/split_base_arsc.h
@@ -0,0 +1,221 @@
+unsigned char split_base_arsc[] = {
+  0x02, 0x00, 0x0c, 0x00, 0x30, 0x0a, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x1c, 0x00, 0x94, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00,
+  0x72, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00,
+  0xb4, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00,
+  0xf4, 0x00, 0x00, 0x00, 0x0c, 0x01, 0x00, 0x00, 0x2a, 0x01, 0x00, 0x00,
+  0x44, 0x01, 0x00, 0x00, 0x13, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73, 0x00,
+  0x2f, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, 0x6f, 0x00, 0x75, 0x00,
+  0x74, 0x00, 0x2f, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00,
+  0x2e, 0x00, 0x78, 0x00, 0x6d, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x22, 0x00,
+  0x72, 0x00, 0x65, 0x00, 0x73, 0x00, 0x2f, 0x00, 0x6c, 0x00, 0x61, 0x00,
+  0x79, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x74, 0x00, 0x2d, 0x00, 0x66, 0x00,
+  0x72, 0x00, 0x2d, 0x00, 0x73, 0x00, 0x77, 0x00, 0x36, 0x00, 0x30, 0x00,
+  0x30, 0x00, 0x64, 0x00, 0x70, 0x00, 0x2d, 0x00, 0x76, 0x00, 0x31, 0x00,
+  0x33, 0x00, 0x2f, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00,
+  0x2e, 0x00, 0x78, 0x00, 0x6d, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x09, 0x00,
+  0x53, 0x00, 0x70, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x74, 0x00, 0x20, 0x00,
+  0x41, 0x00, 0x50, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x48, 0x00,
+  0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x2c, 0x00, 0x20, 0x00,
+  0x57, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x6c, 0x00, 0x64, 0x00, 0x21, 0x00,
+  0x00, 0x00, 0x05, 0x00, 0x42, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x6d, 0x00,
+  0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0x42, 0x00, 0x6c, 0x00, 0x61, 0x00,
+  0x68, 0x00, 0x2e, 0x00, 0x2e, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x0b, 0x00,
+  0x48, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x20, 0x00,
+  0x74, 0x00, 0x68, 0x00, 0x65, 0x00, 0x72, 0x00, 0x65, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x47, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x64, 0x00, 0x20, 0x00,
+  0x62, 0x00, 0x79, 0x00, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x49, 0x00,
+  0x20, 0x00, 0x68, 0x00, 0x61, 0x00, 0x7a, 0x00, 0x20, 0x00, 0x41, 0x00,
+  0x4c, 0x00, 0x4c, 0x00, 0x21, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x49, 0x00,
+  0x20, 0x00, 0x68, 0x00, 0x61, 0x00, 0x7a, 0x00, 0x20, 0x00, 0x31, 0x00,
+  0x21, 0x00, 0x31, 0x00, 0x21, 0x00, 0x20, 0x00, 0x3a, 0x00, 0x29, 0x00,
+  0x00, 0x00, 0x0b, 0x00, 0x49, 0x00, 0x20, 0x00, 0x6e, 0x00, 0x6f, 0x00,
+  0x20, 0x00, 0x68, 0x00, 0x61, 0x00, 0x7a, 0x00, 0x20, 0x00, 0x3a, 0x00,
+  0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1c, 0x01,
+  0x90, 0x08, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00,
+  0x6d, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00,
+  0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x2e, 0x00, 0x65, 0x00, 0x78, 0x00,
+  0x61, 0x00, 0x6d, 0x00, 0x70, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x2e, 0x00,
+  0x73, 0x00, 0x70, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x74, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x1c, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x00, 0x00,
+  0x0e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0xd4, 0x00, 0x00, 0x00,
+  0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00,
+  0x3a, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00,
+  0x66, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00,
+  0x06, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, 0x6f, 0x00, 0x75, 0x00,
+  0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00,
+  0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x00, 0x00, 0x05, 0x00, 0x61, 0x00,
+  0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x00, 0x00, 0x07, 0x00,
+  0x70, 0x00, 0x6c, 0x00, 0x75, 0x00, 0x72, 0x00, 0x61, 0x00, 0x6c, 0x00,
+  0x73, 0x00, 0x00, 0x00, 0x04, 0x00, 0x62, 0x00, 0x6f, 0x00, 0x6f, 0x00,
+  0x6c, 0x00, 0x00, 0x00, 0x05, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6c, 0x00,
+  0x6f, 0x00, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x64, 0x00, 0x69, 0x00,
+  0x6d, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x69, 0x00,
+  0x64, 0x00, 0x00, 0x00, 0x07, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00,
+  0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x1c, 0x00, 0x30, 0x01, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+  0x22, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00,
+  0x46, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00,
+  0x80, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00,
+  0xa4, 0x00, 0x00, 0x00, 0xb2, 0x00, 0x00, 0x00, 0xca, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x00, 0x00,
+  0x09, 0x00, 0x61, 0x00, 0x70, 0x00, 0x70, 0x00, 0x5f, 0x00, 0x74, 0x00,
+  0x69, 0x00, 0x74, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x00, 0x00, 0x04, 0x00,
+  0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x00, 0x00, 0x04, 0x00,
+  0x62, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x00,
+  0x62, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x68, 0x00, 0x00, 0x00, 0x0d, 0x00,
+  0x6c, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x66, 0x00,
+  0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00,
+  0x73, 0x00, 0x00, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00,
+  0x4c, 0x00, 0x69, 0x00, 0x73, 0x00, 0x74, 0x00, 0x00, 0x00, 0x03, 0x00,
+  0x61, 0x00, 0x72, 0x00, 0x79, 0x00, 0x00, 0x00, 0x04, 0x00, 0x70, 0x00,
+  0x6c, 0x00, 0x75, 0x00, 0x72, 0x00, 0x00, 0x00, 0x03, 0x00, 0x71, 0x00,
+  0x75, 0x00, 0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x67, 0x00, 0x72, 0x00,
+  0x65, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x05, 0x00, 0x77, 0x00,
+  0x69, 0x00, 0x64, 0x00, 0x74, 0x00, 0x68, 0x00, 0x00, 0x00, 0x0a, 0x00,
+  0x69, 0x00, 0x64, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x69, 0x00,
+  0x66, 0x00, 0x69, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x06, 0x00,
+  0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+  0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x04, 0x24, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00,
+  0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+  0x58, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x66, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x02,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00,
+  0x02, 0x02, 0x10, 0x00, 0x20, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+  0x94, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+  0x54, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+  0x20, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
+  0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x00, 0x03, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00,
+  0x02, 0x02, 0x10, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+  0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0xc8, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00,
+  0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00,
+  0x10, 0x00, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x03,
+  0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x03,
+  0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+  0x08, 0x00, 0x00, 0x10, 0xd2, 0x04, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,
+  0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x01, 0x01, 0x00, 0x03, 0x7f,
+  0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x01, 0x02, 0x00, 0x03, 0x7f,
+  0x02, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x05, 0x01, 0x19, 0x00, 0x00,
+  0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+  0x7c, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,
+  0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+  0x05, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x03, 0x0a, 0x00, 0x00, 0x00,
+  0x06, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x03, 0x09, 0x00, 0x00, 0x00,
+  0x09, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x00,
+  0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+  0x58, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x12, 0xff, 0xff, 0xff, 0xff,
+  0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+  0x58, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x0a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x1d, 0x00, 0xff, 0x00, 0xff,
+  0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+  0x58, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x0b, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x05, 0x01, 0x17, 0x00, 0x00,
+  0x01, 0x02, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x58, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x05,
+  0x01, 0xe6, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
+  0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x02, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
+  0x0b, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
+  0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x02, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10,
+  0x7b, 0x00, 0x00, 0x00
+};
+unsigned int split_base_arsc_len = 2608;
diff --git a/libs/androidfw/tests/data/split_de_fr_arsc.h b/libs/androidfw/tests/data/split_de_fr_arsc.h
new file mode 100644
index 0000000..6f6a416
--- /dev/null
+++ b/libs/androidfw/tests/data/split_de_fr_arsc.h
@@ -0,0 +1,118 @@
+unsigned char split_de_fr_arsc[] = {
+  0x02, 0x00, 0x0c, 0x00, 0x64, 0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x1c, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+  0x2c, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x00,
+  0x6c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x41, 0x00, 0x63, 0x00, 0x68, 0x00,
+  0x74, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x21, 0x00, 0x00, 0x00,
+  0x0a, 0x00, 0x41, 0x00, 0x50, 0x00, 0x4b, 0x00, 0x20, 0x00, 0x44, 0x00,
+  0x69, 0x00, 0x76, 0x00, 0x69, 0x00, 0x73, 0x00, 0xe9, 0x00, 0x00, 0x00,
+  0x0f, 0x00, 0x42, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x6a, 0x00, 0x6f, 0x00,
+  0x75, 0x00, 0x72, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x4d, 0x00, 0x6f, 0x00,
+  0x6e, 0x00, 0x64, 0x00, 0x65, 0x00, 0x21, 0x00, 0x00, 0x00, 0x06, 0x00,
+  0x42, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x68, 0x00, 0x2e, 0x00, 0x2e, 0x00,
+  0x00, 0x00, 0x05, 0x00, 0x48, 0x00, 0xe9, 0x00, 0x20, 0x00, 0x6c, 0x00,
+  0xe0, 0x00, 0x00, 0x00, 0x09, 0x00, 0x41, 0x00, 0x75, 0x00, 0x20, 0x00,
+  0x72, 0x00, 0x65, 0x00, 0x76, 0x00, 0x6f, 0x00, 0x69, 0x00, 0x72, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1c, 0x01, 0xa0, 0x04, 0x00, 0x00,
+  0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00,
+  0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00,
+  0x64, 0x00, 0x2e, 0x00, 0x65, 0x00, 0x78, 0x00, 0x61, 0x00, 0x6d, 0x00,
+  0x70, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x2e, 0x00, 0x73, 0x00, 0x70, 0x00,
+  0x6c, 0x00, 0x69, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x01, 0x00, 0x00,
+  0x0a, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x1c, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+  0x1c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00,
+  0x4c, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00,
+  0x74, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00,
+  0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00, 0x06, 0x00, 0x6c, 0x00,
+  0x61, 0x00, 0x79, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x74, 0x00, 0x00, 0x00,
+  0x06, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00,
+  0x67, 0x00, 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00,
+  0x61, 0x00, 0x79, 0x00, 0x00, 0x00, 0x07, 0x00, 0x70, 0x00, 0x6c, 0x00,
+  0x75, 0x00, 0x72, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x73, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x62, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x6c, 0x00, 0x00, 0x00,
+  0x05, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x72, 0x00,
+  0x00, 0x00, 0x05, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x65, 0x00,
+  0x6e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x69, 0x00, 0x64, 0x00, 0x00, 0x00,
+  0x07, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00,
+  0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00,
+  0x78, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
+  0x2e, 0x00, 0x00, 0x00, 0x09, 0x00, 0x61, 0x00, 0x70, 0x00, 0x70, 0x00,
+  0x5f, 0x00, 0x74, 0x00, 0x69, 0x00, 0x74, 0x00, 0x6c, 0x00, 0x65, 0x00,
+  0x00, 0x00, 0x04, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
+  0x00, 0x00, 0x04, 0x00, 0x62, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x68, 0x00,
+  0x00, 0x00, 0x0d, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x73, 0x00,
+  0x6f, 0x00, 0x66, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00,
+  0x6e, 0x00, 0x67, 0x00, 0x73, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+  0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+  0x20, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x64, 0x00, 0x00, 0x00,
+  0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
+  0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x65, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+  0x84, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+  0x54, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x66, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+  0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
+  0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+  0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x02, 0x44, 0x00, 0x78, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+  0x03, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x66, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x01, 0x00,
+  0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x03, 0x04, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00,
+  0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+  0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
+  0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+  0x14, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
+  0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+unsigned int split_de_fr_arsc_len = 1380;
diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp
index 77d16ab..8b32c40 100644
--- a/libs/hwui/DamageAccumulator.cpp
+++ b/libs/hwui/DamageAccumulator.cpp
@@ -170,6 +170,9 @@
     }
 
     const RenderProperties& props = frame->renderNode->properties();
+    if (props.getAlpha() <= 0) {
+        return;
+    }
 
     // Perform clipping
     if (props.getClipDamageToBounds() && !frame->pendingDirty.isEmpty()) {
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 233f3f0..d578ef5 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -1118,8 +1118,8 @@
             const DeferredDisplayState& state) {
         DrawStrokableOp::onDefer(renderer, deferInfo, state);
         if (!mPaint->getPathEffect()) {
-            renderer.getCaches().tessellationCache.precacheRoundRect(state.mMatrix,
-                    mLocalBounds.getWidth(), mLocalBounds.getHeight(), mRx, mRy, mPaint);
+            renderer.getCaches().tessellationCache.precacheRoundRect(state.mMatrix, *mPaint,
+                    mLocalBounds.getWidth(), mLocalBounds.getHeight(), mRx, mRy);
         }
     }
 
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 0e47c6e2..fc4d40b 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -308,6 +308,10 @@
 
 status_t DisplayListRenderer::drawArc(float left, float top, float right, float bottom,
         float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint) {
+    if (fabs(sweepAngle) > 360.0f) {
+        return drawOval(left, top, right, bottom, paint);
+    }
+
     paint = refPaint(paint);
     addDrawOp(new (alloc()) DrawArcOp(left, top, right, bottom,
                     startAngle, sweepAngle, useCenter, paint));
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 8f3872a..6397478 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -2556,8 +2556,8 @@
         return drawShape(left, top, texture, p);
     }
 
-    const VertexBuffer* vertexBuffer = mCaches.tessellationCache.getRoundRect(*currentTransform(),
-            right - left, bottom - top, rx, ry, p);
+    const VertexBuffer* vertexBuffer = mCaches.tessellationCache.getRoundRect(
+            *currentTransform(), *p, right - left, bottom - top, rx, ry);
     return drawVertexBuffer(left, top, *vertexBuffer, p);
 }
 
@@ -2611,10 +2611,6 @@
         return DrawGlInfo::kStatusDone;
     }
 
-    if (fabs(sweepAngle) >= 360.0f) {
-        return drawOval(left, top, right, bottom, p);
-    }
-
     // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180)
     if (p->getStyle() != SkPaint::kStroke_Style || p->getPathEffect() != 0 || useCenter) {
         mCaches.activeTexture(0);
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index fc51170..3d93383 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -20,6 +20,7 @@
 #include "RenderNode.h"
 
 #include <algorithm>
+#include <string>
 
 #include <SkCanvas.h>
 #include <algorithm>
@@ -117,7 +118,7 @@
 }
 
 void RenderNode::damageSelf(TreeInfo& info) {
-    if (isRenderable() && properties().getAlpha() > 0) {
+    if (isRenderable()) {
         if (properties().getClipDamageToBounds()) {
             info.damageAccumulator->dirty(0, 0, properties().getWidth(), properties().getHeight());
         } else {
@@ -158,7 +159,10 @@
         applyLayerPropertiesToLayer(info);
         damageSelf(info);
     } else if (mLayer->layer.getWidth() != getWidth() || mLayer->layer.getHeight() != getHeight()) {
-        LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight());
+        if (!LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight())) {
+            LayerRenderer::destroyLayer(mLayer);
+            mLayer = 0;
+        }
         damageSelf(info);
     }
 
@@ -166,6 +170,15 @@
     info.damageAccumulator->peekAtDirty(&dirty);
     info.damageAccumulator->popTransform();
 
+    if (!mLayer) {
+        if (info.errorHandler) {
+            std::string msg = "Unable to create layer for ";
+            msg += getName();
+            info.errorHandler->onError(msg);
+        }
+        return;
+    }
+
     if (!dirty.isEmpty()) {
         mLayer->updateDeferred(this, dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom);
     }
diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp
index ef3d0d7..08b54ff 100644
--- a/libs/hwui/TessellationCache.cpp
+++ b/libs/hwui/TessellationCache.cpp
@@ -39,6 +39,8 @@
 
 TessellationCache::Description::Description()
         : type(kNone)
+        , scaleX(1.0f)
+        , scaleY(1.0f)
         , aa(false)
         , cap(SkPaint::kDefault_Cap)
         , style(SkPaint::kFill_Style)
@@ -46,21 +48,13 @@
     memset(&shape, 0, sizeof(Shape));
 }
 
-TessellationCache::Description::Description(Type type)
+TessellationCache::Description::Description(Type type, const Matrix4& transform, const SkPaint& paint)
         : type(type)
-        , aa(false)
-        , cap(SkPaint::kDefault_Cap)
-        , style(SkPaint::kFill_Style)
-        , strokeWidth(1.0f) {
-    memset(&shape, 0, sizeof(Shape));
-}
-
-TessellationCache::Description::Description(Type type, const SkPaint* paint)
-        : type(type)
-        , aa(paint->isAntiAlias())
-        , cap(paint->getStrokeCap())
-        , style(paint->getStyle())
-        , strokeWidth(paint->getStrokeWidth()) {
+        , aa(paint.isAntiAlias())
+        , cap(paint.getStrokeCap())
+        , style(paint.getStyle())
+        , strokeWidth(paint.getStrokeWidth()) {
+    PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY);
     memset(&shape, 0, sizeof(Shape));
 }
 
@@ -70,10 +64,20 @@
     hash = JenkinsHashMix(hash, cap);
     hash = JenkinsHashMix(hash, style);
     hash = JenkinsHashMix(hash, android::hash_type(strokeWidth));
+    hash = JenkinsHashMix(hash, android::hash_type(scaleX));
+    hash = JenkinsHashMix(hash, android::hash_type(scaleY));
     hash = JenkinsHashMixBytes(hash, (uint8_t*) &shape, sizeof(Shape));
     return JenkinsHashWhiten(hash);
 }
 
+void TessellationCache::Description::setupMatrixAndPaint(Matrix4* matrix, SkPaint* paint) const {
+    matrix->loadScale(scaleX, scaleY, 1.0f);
+    paint->setAntiAlias(aa);
+    paint->setStrokeCap(cap);
+    paint->setStyle(style);
+    paint->setStrokeWidth(strokeWidth);
+}
+
 TessellationCache::ShadowDescription::ShadowDescription()
         : nodeKey(NULL) {
     memset(&matrixData, 0, 16 * sizeof(float));
@@ -96,20 +100,15 @@
 
 class TessellationCache::TessellationTask : public Task<VertexBuffer*> {
 public:
-    TessellationTask(Tessellator tessellator, const Description& description,
-                const SkPaint* paint)
+    TessellationTask(Tessellator tessellator, const Description& description)
         : tessellator(tessellator)
-        , description(description)
-        , paint(*paint) {
+        , description(description) {
     }
 
     ~TessellationTask() {}
 
     Tessellator tessellator;
     Description description;
-
-    //copied, since input paint may not be immutable
-    const SkPaint paint;
 };
 
 class TessellationCache::TessellationProcessor : public TaskProcessor<VertexBuffer*> {
@@ -121,7 +120,7 @@
     virtual void onProcess(const sp<Task<VertexBuffer*> >& task) {
         TessellationTask* t = static_cast<TessellationTask*>(task.get());
         ATRACE_NAME("shape tessellation");
-        VertexBuffer* buffer = t->tessellator(t->description, t->paint);
+        VertexBuffer* buffer = t->tessellator(t->description);
         t->setResult(buffer);
     }
 };
@@ -416,21 +415,12 @@
 // Tessellation precaching
 ///////////////////////////////////////////////////////////////////////////////
 
-static VertexBuffer* tessellatePath(const SkPath& path, const SkPaint* paint,
-        float scaleX, float scaleY) {
-    VertexBuffer* buffer = new VertexBuffer();
-    Matrix4 matrix;
-    matrix.loadScale(scaleX, scaleY, 1);
-    PathTessellator::tessellatePath(path, paint, matrix, *buffer);
-    return buffer;
-}
-
 TessellationCache::Buffer* TessellationCache::getOrCreateBuffer(
-        const Description& entry, Tessellator tessellator, const SkPaint* paint) {
+        const Description& entry, Tessellator tessellator) {
     Buffer* buffer = mCache.get(entry);
     if (!buffer) {
         // not cached, enqueue a task to fill the buffer
-        sp<TessellationTask> task = new TessellationTask(tessellator, entry, paint);
+        sp<TessellationTask> task = new TessellationTask(tessellator, entry);
         buffer = new Buffer(task);
 
         if (mProcessor == NULL) {
@@ -442,43 +432,49 @@
     return buffer;
 }
 
+static VertexBuffer* tessellatePath(const TessellationCache::Description& description,
+        const SkPath& path) {
+    Matrix4 matrix;
+    SkPaint paint;
+    description.setupMatrixAndPaint(&matrix, &paint);
+    VertexBuffer* buffer = new VertexBuffer();
+    PathTessellator::tessellatePath(path, &paint, matrix, *buffer);
+    return buffer;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
-// Rounded rects
+// RoundRect
 ///////////////////////////////////////////////////////////////////////////////
 
-static VertexBuffer* tessellateRoundRect(const TessellationCache::Description& description,
-        const SkPaint& paint) {
-    SkRect rect = SkRect::MakeWH(description.shape.roundRect.mWidth,
-            description.shape.roundRect.mHeight);
-    float rx = description.shape.roundRect.mRx;
-    float ry = description.shape.roundRect.mRy;
-    if (paint.getStyle() == SkPaint::kStrokeAndFill_Style) {
-        float outset = paint.getStrokeWidth() / 2;
+static VertexBuffer* tessellateRoundRect(const TessellationCache::Description& description) {
+    SkRect rect = SkRect::MakeWH(description.shape.roundRect.width,
+            description.shape.roundRect.height);
+    float rx = description.shape.roundRect.rx;
+    float ry = description.shape.roundRect.ry;
+    if (description.style == SkPaint::kStrokeAndFill_Style) {
+        float outset = description.strokeWidth / 2;
         rect.outset(outset, outset);
         rx += outset;
         ry += outset;
     }
     SkPath path;
     path.addRoundRect(rect, rx, ry);
-    return tessellatePath(path, &paint,
-            description.shape.roundRect.mScaleX, description.shape.roundRect.mScaleY);
+    return tessellatePath(description, path);
 }
 
-TessellationCache::Buffer* TessellationCache::getRoundRectBuffer(const Matrix4& transform,
-        float width, float height, float rx, float ry, const SkPaint* paint) {
-    Description entry(Description::kRoundRect, paint);
-    entry.shape.roundRect.mWidth = width;
-    entry.shape.roundRect.mHeight = height;
-    entry.shape.roundRect.mRx = rx;
-    entry.shape.roundRect.mRy = ry;
-    PathTessellator::extractTessellationScales(transform,
-            &entry.shape.roundRect.mScaleX, &entry.shape.roundRect.mScaleY);
-
-    return getOrCreateBuffer(entry, &tessellateRoundRect, paint);
+TessellationCache::Buffer* TessellationCache::getRoundRectBuffer(
+        const Matrix4& transform, const SkPaint& paint,
+        float width, float height, float rx, float ry) {
+    Description entry(Description::kRoundRect, transform, paint);
+    entry.shape.roundRect.width = width;
+    entry.shape.roundRect.height = height;
+    entry.shape.roundRect.rx = rx;
+    entry.shape.roundRect.ry = ry;
+    return getOrCreateBuffer(entry, &tessellateRoundRect);
 }
-const VertexBuffer* TessellationCache::getRoundRect(const Matrix4& transform,
-        float width, float height, float rx, float ry, const SkPaint* paint) {
-    return getRoundRectBuffer(transform, width, height, rx, ry, paint)->getVertexBuffer();
+const VertexBuffer* TessellationCache::getRoundRect(const Matrix4& transform, const SkPaint& paint,
+        float width, float height, float rx, float ry) {
+    return getRoundRectBuffer(transform, paint, width, height, rx, ry)->getVertexBuffer();
 }
 
 }; // namespace uirenderer
diff --git a/libs/hwui/TessellationCache.h b/libs/hwui/TessellationCache.h
index d4ff943..688a699 100644
--- a/libs/hwui/TessellationCache.h
+++ b/libs/hwui/TessellationCache.h
@@ -50,30 +50,28 @@
         enum Type {
             kNone,
             kRoundRect,
-            kAmbientShadow,
-            kSpotShadow
         };
 
         Type type;
+        float scaleX;
+        float scaleY;
         bool aa;
         SkPaint::Cap cap;
         SkPaint::Style style;
         float strokeWidth;
         union Shape {
             struct RoundRect {
-                float mScaleX;
-                float mScaleY;
-                float mWidth;
-                float mHeight;
-                float mRx;
-                float mRy;
+                float width;
+                float height;
+                float rx;
+                float ry;
             } roundRect;
         } shape;
 
         Description();
-        Description(Type type);
-        Description(Type type, const SkPaint* paint);
+        Description(Type type, const Matrix4& transform, const SkPaint& paint);
         hash_t hash() const;
+        void setupMatrixAndPaint(Matrix4* matrix, SkPaint* paint) const;
     };
 
     struct ShadowDescription {
@@ -123,12 +121,12 @@
 
     // TODO: precache/get for Oval, Lines, Points, etc.
 
-    void precacheRoundRect(const Matrix4& transform,
-            float width, float height, float rx, float ry, const SkPaint* paint) {
-        getRoundRectBuffer(transform, width, height, rx, ry, paint);
+    void precacheRoundRect(const Matrix4& transform, const SkPaint& paint,
+            float width, float height, float rx, float ry) {
+        getRoundRectBuffer(transform, paint, width, height, rx, ry);
     }
-    const VertexBuffer* getRoundRect(const Matrix4& transform,
-            float width, float height, float rx, float ry, const SkPaint* paint);
+    const VertexBuffer* getRoundRect(const Matrix4& transform, const SkPaint& paint,
+            float width, float height, float rx, float ry);
 
     void precacheShadows(const Matrix4* drawTransform, const Rect& localClip,
             bool opaque, const SkPath* casterPerimeter,
@@ -146,14 +144,14 @@
     class TessellationTask;
     class TessellationProcessor;
 
+    typedef VertexBuffer* (*Tessellator)(const Description&);
 
-    typedef VertexBuffer* (*Tessellator)(const Description&, const SkPaint&);
+    Buffer* getRectBuffer(const Matrix4& transform, const SkPaint& paint,
+            float width, float height);
+    Buffer* getRoundRectBuffer(const Matrix4& transform, const SkPaint& paint,
+            float width, float height, float rx, float ry);
 
-    Buffer* getRoundRectBuffer(const Matrix4& transform,
-            float width, float height, float rx, float ry, const SkPaint* paint);
-
-    Buffer* getOrCreateBuffer(const Description& entry,
-            Tessellator tessellator, const SkPaint* paint);
+    Buffer* getOrCreateBuffer(const Description& entry, Tessellator tessellator);
 
     uint32_t mSize;
     uint32_t mMaxSize;
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index 0fc0cef..f67e434 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -16,6 +16,8 @@
 #ifndef TREEINFO_H
 #define TREEINFO_H
 
+#include <string>
+
 #include <utils/Timers.h>
 
 #include "DamageAccumulator.h"
@@ -35,6 +37,13 @@
     ~AnimationHook() {}
 };
 
+class ErrorHandler {
+public:
+    virtual void onError(const std::string& message) = 0;
+protected:
+    ~ErrorHandler() {}
+};
+
 // This would be a struct, but we want to PREVENT_COPY_AND_ASSIGN
 class TreeInfo {
     PREVENT_COPY_AND_ASSIGN(TreeInfo);
@@ -65,6 +74,7 @@
         , prepareTextures(mode == MODE_FULL)
         , damageAccumulator(NullDamageAccumulator::instance())
         , renderer(0)
+        , errorHandler(0)
     {}
 
     const TraversalMode mode;
@@ -78,6 +88,7 @@
     // The renderer that will be drawing the next frame. Use this to push any
     // layer updates or similar. May be NULL.
     OpenGLRenderer* renderer;
+    ErrorHandler* errorHandler;
 
     struct Out {
         Out()
diff --git a/libs/hwui/thread/Task.h b/libs/hwui/thread/Task.h
index 9a211a2..30b6ff2 100644
--- a/libs/hwui/thread/Task.h
+++ b/libs/hwui/thread/Task.h
@@ -17,8 +17,6 @@
 #ifndef ANDROID_HWUI_TASK_H
 #define ANDROID_HWUI_TASK_H
 
-#define ATRACE_TAG ATRACE_TAG_VIEW
-
 #include <utils/RefBase.h>
 #include <utils/Trace.h>
 
@@ -40,7 +38,7 @@
     virtual ~Task() { }
 
     T getResult() const {
-        ATRACE_NAME("waitForTask");
+        ScopedTrace tracer(ATRACE_TAG_VIEW, "waitForTask");
         return mFuture->get();
     }
 
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index f70110c..bdd1195 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -583,7 +583,8 @@
     }
 
     /**
-     * Get the altitude if available, in meters above sea level.
+     * Get the altitude if available, in meters above the WGS 84 reference
+     * ellipsoid.
      *
      * <p>If this location does not have an altitude then 0.0 is returned.
      */
@@ -592,7 +593,7 @@
     }
 
     /**
-     * Set the altitude, in meters above sea level.
+     * Set the altitude, in meters above the WGS 84 reference ellipsoid.
      *
      * <p>Following this call {@link #hasAltitude} will return true.
      */
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 4b4be1b..e05aef0 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -152,6 +152,8 @@
         switch (audioFormat) {
         case ENCODING_PCM_8BIT:
             return 1;
+        case ENCODING_PCM_FLOAT:
+            return 4;
         case ENCODING_PCM_16BIT:
         case ENCODING_DEFAULT:
             return 2;
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index e25714a..66175d0 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1826,11 +1826,7 @@
             }
             SubtitleTrack track = mInbandSubtitleTracks[index];
             if (track != null) {
-                long runID = data.getStartTimeUs() + 1;
-                track.onData(data.getData(), true /* eos */, runID);
-                track.setRunDiscardTimeMs(
-                        runID,
-                        (data.getStartTimeUs() + data.getDurationUs()) / 1000);
+                track.onData(data);
             }
         }
     };
diff --git a/media/java/android/media/SubtitleTrack.java b/media/java/android/media/SubtitleTrack.java
index b0e182d..9fedf63 100644
--- a/media/java/android/media/SubtitleTrack.java
+++ b/media/java/android/media/SubtitleTrack.java
@@ -75,6 +75,14 @@
 
     private long mNextScheduledTimeMs = -1;
 
+    protected void onData(SubtitleData data) {
+        long runID = data.getStartTimeUs() + 1;
+        onData(data.getData(), true /* eos */, runID);
+        setRunDiscardTimeMs(
+                runID,
+                (data.getStartTimeUs() + data.getDurationUs()) / 1000);
+    }
+
     /**
      * Called when there is input data for the subtitle track.  The
      * complete subtitle for a track can include multiple whole units
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 3f0405d..7e9d279 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -22,7 +22,9 @@
 import android.net.Uri;
 import android.provider.BaseColumns;
 
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * <p>
@@ -380,6 +382,81 @@
         /** The service type for radio channels that have audio only. */
         public static final int SERVICE_TYPE_AUDIO = 0x2;
 
+        /** The video format for 240p. */
+        public static final String VIDEO_FORMAT_240P = "VIDEO_FORMAT_240P";
+
+        /** The video format for 360p. */
+        public static final String VIDEO_FORMAT_360P = "VIDEO_FORMAT_360P";
+
+        /** The video format for 480i. */
+        public static final String VIDEO_FORMAT_480I = "VIDEO_FORMAT_480I";
+
+        /** The video format for 480p. */
+        public static final String VIDEO_FORMAT_480P = "VIDEO_FORMAT_480P";
+
+        /** The video format for 576i. */
+        public static final String VIDEO_FORMAT_576I = "VIDEO_FORMAT_576I";
+
+        /** The video format for 576p. */
+        public static final String VIDEO_FORMAT_576P = "VIDEO_FORMAT_576P";
+
+        /** The video format for 720p. */
+        public static final String VIDEO_FORMAT_720P = "VIDEO_FORMAT_720P";
+
+        /** The video format for 1080i. */
+        public static final String VIDEO_FORMAT_1080I = "VIDEO_FORMAT_1080I";
+
+        /** The video format for 1080p. */
+        public static final String VIDEO_FORMAT_1080P = "VIDEO_FORMAT_1080P";
+
+        /** The video format for 2160p. */
+        public static final String VIDEO_FORMAT_2160P = "VIDEO_FORMAT_2160P";
+
+        /** The video format for 4320p. */
+        public static final String VIDEO_FORMAT_4320P = "VIDEO_FORMAT_4320P";
+
+        /** The video resolution for standard-definition. */
+        public static final String VIDEO_RESOLUTION_SD = "VIDEO_RESOLUTION_SD";
+
+        /** The video resolution for enhanced-definition. */
+        public static final String VIDEO_RESOLUTION_ED = "VIDEO_RESOLUTION_ED";
+
+        /** The video resolution for high-definition. */
+        public static final String VIDEO_RESOLUTION_HD = "VIDEO_RESOLUTION_HD";
+
+        /** The video resolution for full high-definition. */
+        public static final String VIDEO_RESOLUTION_FHD = "VIDEO_RESOLUTION_FHD";
+
+        /** The video resolution for ultra high-definition. */
+        public static final String VIDEO_RESOLUTION_UHD = "VIDEO_RESOLUTION_UHD";
+
+        private static final Map<String, String> VIDEO_FORMAT_TO_RESOLUTION_MAP =
+                new HashMap<String, String>();
+
+        static {
+            VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_480I, VIDEO_RESOLUTION_SD);
+            VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_480P, VIDEO_RESOLUTION_ED);
+            VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_576I, VIDEO_RESOLUTION_SD);
+            VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_576P, VIDEO_RESOLUTION_ED);
+            VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_720P, VIDEO_RESOLUTION_HD);
+            VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_1080I, VIDEO_RESOLUTION_HD);
+            VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_1080P, VIDEO_RESOLUTION_FHD);
+            VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_2160P, VIDEO_RESOLUTION_UHD);
+            VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_4320P, VIDEO_RESOLUTION_UHD);
+        }
+
+        /**
+         * Returns the video resolution (definition) for a given video format.
+         *
+         * @param videoFormat The video format defined in {@link Channels}.
+         * @return the corresponding video resolution string. {@code null} if the resolution string
+         *         is not defined for the given video format.
+         * @see #COLUMN_VIDEO_FORMAT
+         */
+        public static final String getVideoResolution(String videoFormat) {
+            return VIDEO_FORMAT_TO_RESOLUTION_MAP.get(videoFormat);
+        }
+
         /**
          * The name of the {@link TvInputService} subclass that provides this TV channel. This
          * should be a fully qualified class name (such as, "com.example.project.TvInputService").
@@ -513,6 +590,24 @@
         public static final String COLUMN_DESCRIPTION = "description";
 
         /**
+         * The typical video format for programs from this TV channel.
+         * <p>
+         * This is primarily used to filter out channels based on video format by applications. The
+         * value should match one of the followings: {@link #VIDEO_FORMAT_240P},
+         * {@link #VIDEO_FORMAT_360P}, {@link #VIDEO_FORMAT_480I}, {@link #VIDEO_FORMAT_480P},
+         * {@link #VIDEO_FORMAT_576I}, {@link #VIDEO_FORMAT_576P}, {@link #VIDEO_FORMAT_720P},
+         * {@link #VIDEO_FORMAT_1080I}, {@link #VIDEO_FORMAT_1080P}, {@link #VIDEO_FORMAT_2160P},
+         * {@link #VIDEO_FORMAT_4320P}. Note that the actual video resolution of each program from a
+         * given channel can vary thus one should use {@link Programs#COLUMN_VIDEO_WIDTH} and
+         * {@link Programs#COLUMN_VIDEO_HEIGHT} to get more accurate video resolution.
+         * </p><p>
+         * Type: TEXT
+         * </p><p>
+         * @see #getVideoResolution
+         */
+        public static final String COLUMN_VIDEO_FORMAT = "video_format";
+
+        /**
          * The flag indicating whether this TV channel is browsable or not.
          * <p>
          * A value of 1 indicates the channel is included in the channel list that applications use
@@ -719,6 +814,32 @@
         public static final String COLUMN_LONG_DESCRIPTION = "long_description";
 
         /**
+         * The width of the video for this TV program, in the unit of pixels.
+         * <p>
+         * Together with {@link #COLUMN_VIDEO_HEIGHT} this is used to determine the video resolution
+         * of the current TV program. Can be empty if it is not known initially or the program does
+         * not convey any video such as the programs from type {@link Channels#SERVICE_TYPE_AUDIO}
+         * channels.
+         * </p><p>
+         * Type: INTEGER
+         * </p>
+         */
+        public static final String COLUMN_VIDEO_WIDTH = "video_width";
+
+        /**
+         * The height of the video for this TV program, in the unit of pixels.
+         * <p>
+         * Together with {@link #COLUMN_VIDEO_WIDTH} this is used to determine the video resolution
+         * of the current TV program. Can be empty if it is not known initially or the program does
+         * not convey any video such as the programs from type {@link Channels#SERVICE_TYPE_AUDIO}
+         * channels.
+         * </p><p>
+         * Type: INTEGER
+         * </p>
+         */
+        public static final String COLUMN_VIDEO_HEIGHT = "video_height";
+
+        /**
          * The comma-separated audio languages of this TV program.
          * <p>
          * This is used to describe available audio languages included in the program. Use
@@ -778,37 +899,37 @@
         /** Canonical genres for TV programs. */
         public static final class Genres {
             /** The genre for Family/Kids. */
-            public static final String FAMILY_KIDS = "Family/Kids";
+            public static final String FAMILY_KIDS = "FAMILY_KIDS";
 
             /** The genre for Sports. */
-            public static final String SPORTS = "Sports";
+            public static final String SPORTS = "SPORTS";
 
             /** The genre for Shopping. */
-            public static final String SHOPPING = "Shopping";
+            public static final String SHOPPING = "SHOPPING";
 
             /** The genre for Movies. */
-            public static final String MOVIES = "Movies";
+            public static final String MOVIES = "MOVIES";
 
             /** The genre for Comedy. */
-            public static final String COMEDY = "Comedy";
+            public static final String COMEDY = "COMEDY";
 
             /** The genre for Travel. */
-            public static final String TRAVEL = "Travel";
+            public static final String TRAVEL = "TRAVEL";
 
             /** The genre for Drama. */
-            public static final String DRAMA = "Drama";
+            public static final String DRAMA = "DRAMA";
 
             /** The genre for Education. */
-            public static final String EDUCATION = "Education";
+            public static final String EDUCATION = "EDUCATION";
 
             /** The genre for Animal/Wildlife. */
-            public static final String ANIMAL_WILDLIFE = "Animal/Wildlife";
+            public static final String ANIMAL_WILDLIFE = "ANIMAL_WILDLIFE";
 
             /** The genre for News. */
-            public static final String NEWS = "News";
+            public static final String NEWS = "NEWS";
 
             /** The genre for Gaming. */
-            public static final String GAMING = "Gaming";
+            public static final String GAMING = "GAMING";
 
             private Genres() {}
 
@@ -848,7 +969,7 @@
      *
      * @hide
      */
-    public static final class WatchedPrograms implements BaseColumns {
+    public static final class WatchedPrograms implements BaseTvColumns {
 
         /** The content:// style URI for this table. */
         public static final Uri CONTENT_URI =
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index edfdd60..daa7009 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -533,12 +533,11 @@
 
         /**
          * Releases this session.
-         *
-         * @throws IllegalStateException if the session has been already released.
          */
         public void release() {
             if (mToken == null) {
-                throw new IllegalStateException("the session has been already released");
+                Log.w(TAG, "The session has been already released");
+                return;
             }
             try {
                 mService.releaseSession(mToken, mUserId);
@@ -553,12 +552,12 @@
          * Sets the {@link android.view.Surface} for this session.
          *
          * @param surface A {@link android.view.Surface} used to render video.
-         * @throws IllegalStateException if the session has been already released.
          * @hide
          */
         public void setSurface(Surface surface) {
             if (mToken == null) {
-                throw new IllegalStateException("the session has been already released");
+                Log.w(TAG, "The session has been already released");
+                return;
             }
             // surface can be null.
             try {
@@ -573,11 +572,11 @@
          *
          * @param volume A volume value between 0.0f to 1.0f.
          * @throws IllegalArgumentException if the volume value is out of range.
-         * @throws IllegalStateException if the session has been already released.
          */
         public void setStreamVolume(float volume) {
             if (mToken == null) {
-                throw new IllegalStateException("the session has been already released");
+                Log.w(TAG, "The session has been already released");
+                return;
             }
             try {
                 if (volume < 0.0f || volume > 1.0f) {
@@ -594,14 +593,14 @@
          *
          * @param channelUri The URI of a channel.
          * @throws IllegalArgumentException if the argument is {@code null}.
-         * @throws IllegalStateException if the session has been already released.
          */
         public void tune(Uri channelUri) {
             if (channelUri == null) {
                 throw new IllegalArgumentException("channelUri cannot be null");
             }
             if (mToken == null) {
-                throw new IllegalStateException("the session has been already released");
+                Log.w(TAG, "The session has been already released");
+                return;
             }
             try {
                 mService.tune(mToken, channelUri, mUserId);
@@ -620,8 +619,7 @@
          * @param view A view playing TV.
          * @param frame A position of the overlay view.
          * @throws IllegalArgumentException if any of the arguments is {@code null}.
-         * @throws IllegalStateException if {@code view} is not attached to a window or
-         *         if the session has been already released.
+         * @throws IllegalStateException if {@code view} is not attached to a window.
          */
         void createOverlayView(View view, Rect frame) {
             if (view == null) {
@@ -634,7 +632,8 @@
                 throw new IllegalStateException("view must be attached to a window");
             }
             if (mToken == null) {
-                throw new IllegalStateException("the session has been already released");
+                Log.w(TAG, "The session has been already released");
+                return;
             }
             try {
                 mService.createOverlayView(mToken, view.getWindowToken(), frame, mUserId);
@@ -648,14 +647,14 @@
          *
          * @param frame A new position of the overlay view.
          * @throws IllegalArgumentException if the arguments is {@code null}.
-         * @throws IllegalStateException if the session has been already released.
          */
         void relayoutOverlayView(Rect frame) {
             if (frame == null) {
                 throw new IllegalArgumentException("frame cannot be null");
             }
             if (mToken == null) {
-                throw new IllegalStateException("the session has been already released");
+                Log.w(TAG, "The session has been already released");
+                return;
             }
             try {
                 mService.relayoutOverlayView(mToken, frame, mUserId);
@@ -666,12 +665,11 @@
 
         /**
          * Removes the current overlay view.
-         *
-         * @throws IllegalStateException if the session has been already released.
          */
         void removeOverlayView() {
             if (mToken == null) {
-                throw new IllegalStateException("the session has been already released");
+                Log.w(TAG, "The session has been already released");
+                return;
             }
             try {
                 mService.removeOverlayView(mToken, mUserId);
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 2ed3d73..52db30a 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -27,6 +27,7 @@
 import android.content.pm.PackageInfoLite;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.PackageParserException;
 import android.content.res.ObbInfo;
 import android.content.res.ObbScanner;
 import android.net.Uri;
@@ -157,6 +158,7 @@
          * @return Returns PackageInfoLite object containing
          * the package info and recommended app location.
          */
+        @Override
         public PackageInfoLite getMinimalPackageInfo(final String packagePath, int flags,
                 long threshold, String abiOverride) {
             PackageInfoLite ret = new PackageInfoLite();
@@ -167,14 +169,13 @@
                 return ret;
             }
 
-            DisplayMetrics metrics = new DisplayMetrics();
-            metrics.setToDefaults();
-
-            PackageParser.ApkLite pkg = PackageParser.parseApkLite(packagePath, 0);
-            if (pkg == null) {
+            final File apkFile = new File(packagePath);
+            final PackageParser.ApkLite pkg;
+            try {
+                pkg = PackageParser.parseApkLite(apkFile, 0);
+            } catch (PackageParserException e) {
                 Slog.w(TAG, "Failed to parse package");
 
-                final File apkFile = new File(packagePath);
                 if (!apkFile.exists()) {
                     ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_URI;
                 } else {
diff --git a/packages/SystemUI/res/drawable-hdpi/recents_lower_gradient.9.png b/packages/SystemUI/res/drawable-hdpi/recents_lower_gradient.9.png
index d4fdbf3..17100f7 100644
--- a/packages/SystemUI/res/drawable-hdpi/recents_lower_gradient.9.png
+++ b/packages/SystemUI/res/drawable-hdpi/recents_lower_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/recents_status_gradient.9.png b/packages/SystemUI/res/drawable-hdpi/recents_status_gradient.9.png
new file mode 100644
index 0000000..e969d4c2
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/recents_status_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/recents_lower_gradient.9.png b/packages/SystemUI/res/drawable-mdpi/recents_lower_gradient.9.png
index 9fc1a3b..b53bd8f 100644
--- a/packages/SystemUI/res/drawable-mdpi/recents_lower_gradient.9.png
+++ b/packages/SystemUI/res/drawable-mdpi/recents_lower_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/recents_status_gradient.9.png b/packages/SystemUI/res/drawable-mdpi/recents_status_gradient.9.png
new file mode 100644
index 0000000..657f710
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/recents_status_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/recents_lower_gradient.9.png b/packages/SystemUI/res/drawable-xhdpi/recents_lower_gradient.9.png
index f38de93..09606f6 100644
--- a/packages/SystemUI/res/drawable-xhdpi/recents_lower_gradient.9.png
+++ b/packages/SystemUI/res/drawable-xhdpi/recents_lower_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/recents_status_gradient.9.png b/packages/SystemUI/res/drawable-xhdpi/recents_status_gradient.9.png
new file mode 100644
index 0000000..a444c55
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/recents_status_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/recents_lower_gradient.9.png b/packages/SystemUI/res/drawable-xxhdpi/recents_lower_gradient.9.png
index 8194605..427cad9 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/recents_lower_gradient.9.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/recents_lower_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/recents_status_gradient.9.png b/packages/SystemUI/res/drawable-xxhdpi/recents_status_gradient.9.png
new file mode 100644
index 0000000..29cf44b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/recents_status_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/recents_button_bg.xml b/packages/SystemUI/res/drawable/recents_button_bg.xml
new file mode 100644
index 0000000..a4cb088
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recents_button_bg.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:attr/colorControlHighlight" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_status_bar_scrim.xml b/packages/SystemUI/res/layout/recents_status_bar_scrim.xml
new file mode 100644
index 0000000..24928d0
--- /dev/null
+++ b/packages/SystemUI/res/layout/recents_status_bar_scrim.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<ImageView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center_horizontal|top"
+    android:scaleType="fitXY"
+    android:src="@drawable/recents_status_gradient" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml
index 23f2796..1bab67a 100644
--- a/packages/SystemUI/res/layout/recents_task_view.xml
+++ b/packages/SystemUI/res/layout/recents_task_view.xml
@@ -32,8 +32,10 @@
             android:id="@+id/application_icon"
             android:layout_width="@dimen/recents_task_view_application_icon_size"
             android:layout_height="@dimen/recents_task_view_application_icon_size"
-            android:layout_marginStart="16dp"
-            android:layout_gravity="center_vertical|start" />
+            android:layout_marginStart="8dp"
+            android:layout_gravity="center_vertical|start"
+            android:padding="8dp"
+            android:background="@drawable/recents_button_bg" />
         <TextView
             android:id="@+id/activity_description"
             android:layout_width="match_parent"
@@ -56,6 +58,7 @@
             android:layout_marginEnd="4dp"
             android:layout_gravity="center_vertical|end"
             android:padding="18dp"
+            android:background="@drawable/recents_button_bg"
             android:visibility="invisible"
             android:src="@drawable/recents_dismiss_light" />
     </com.android.systemui.recents.views.TaskBarView>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 8c1a9c7..3bd8689 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -46,9 +46,7 @@
     <color name="keyguard_overflow_content_color">#ff686868</color>
 
     <!-- The default recents task bar background color. -->
-    <color name="recents_task_bar_default_background_color">#e6444444</color>
-    <!-- The default recents task bar text color. -->
-    <color name="recents_task_bar_default_text_color">#ffeeeeee</color>
+    <color name="recents_task_bar_default_background_color">#ffe6e6e6</color>
     <!-- The recents task bar light text color to be drawn on top of dark backgrounds. -->
     <color name="recents_task_bar_light_text_color">#ffeeeeee</color>
     <!-- The recents task bar dark text color to be drawn on top of light backgrounds. -->
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index c8851dc..c64a182 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -111,9 +111,9 @@
     <!-- The duration in seconds to wait before the dismiss buttons are shown. -->
     <integer name="recents_task_bar_dismiss_delay_seconds">3</integer>
     <!-- The min animation duration for animating views that are currently visible. -->
-    <integer name="recents_filter_animate_current_views_min_duration">175</integer>
+    <integer name="recents_filter_animate_current_views_duration">250</integer>
     <!-- The min animation duration for animating views that are newly visible. -->
-    <integer name="recents_filter_animate_new_views_min_duration">125</integer>
+    <integer name="recents_filter_animate_new_views_duration">250</integer>
     <!-- The min animation duration for animating the task bar in. -->
     <integer name="recents_animate_task_bar_enter_duration">275</integer>
     <!-- The animation delay for animating the first task in. This should roughly be the animation
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 8b8c126..bbcc9c1 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -212,7 +212,7 @@
     <dimen name="glowpadview_inner_radius">15dip</dimen>
 
     <!-- The size of the application icon in the recents task view. -->
-    <dimen name="recents_task_view_application_icon_size">32dp</dimen>
+    <dimen name="recents_task_view_application_icon_size">48dp</dimen>
 
     <!-- The size of the activity icon in the recents task view. -->
     <dimen name="recents_task_view_activity_icon_size">60dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index c3ba349..41b1f75 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -152,6 +152,7 @@
                 .setContentText(mContext.getString(R.string.invalid_charger_text))
                 .setPriority(Notification.PRIORITY_MAX)
                 .setCategory(Notification.CATEGORY_SYSTEM)
+                .setVisibility(Notification.VISIBILITY_PUBLIC)
                 .setFullScreenIntent(pendingBroadcast(ACTION_SHOW_FALLBACK_CHARGER), true);
         final Notification n = nb.build();
         if (n.headsUpContentView != null) {
@@ -171,6 +172,7 @@
                 .setOngoing(true)
                 .setPriority(Notification.PRIORITY_MAX)
                 .setCategory(Notification.CATEGORY_SYSTEM)
+                .setVisibility(Notification.VISIBILITY_PUBLIC)
                 .setFullScreenIntent(pendingBroadcast(ACTION_SHOW_FALLBACK_WARNING), true);
         if (hasBatterySettings()) {
             nb.setContentIntent(pendingBroadcast(ACTION_SHOW_BATTERY_SETTINGS));
@@ -197,7 +199,8 @@
                 .setContentText(mContext.getString(R.string.battery_saver_notification_text))
                 .setOngoing(true)
                 .setShowWhen(false)
-                .setCategory(Notification.CATEGORY_SYSTEM);
+                .setCategory(Notification.CATEGORY_SYSTEM)
+                .setVisibility(Notification.VISIBILITY_PUBLIC);
         if (hasSaverSettings()) {
             nb.addAction(0,
                     mContext.getString(R.string.battery_saver_notification_action_text),
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index 1d355cd..ddea0bf 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -30,7 +30,7 @@
             // Enables the screenshot app->Recents transition
             public static final boolean EnableScreenshotAppTransition = false;
             // Enables the filtering of tasks according to their grouping
-            public static final boolean EnableTaskFiltering = false;
+            public static final boolean EnableTaskFiltering = true;
             // Enables clipping of tasks against each other
             public static final boolean EnableTaskStackClipping = true;
             // Enables the use of theme colors as the task bar background
@@ -48,7 +48,7 @@
             // For debugging, this defines the number of mock recents packages to create
             public static final int SystemServicesProxyMockPackageCount = 3;
             // For debugging, this defines the number of mock recents tasks to create
-            public static final int SystemServicesProxyMockTaskCount = 75;
+            public static final int SystemServicesProxyMockTaskCount = 100;
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index dce8f57..88ff726 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -72,6 +72,7 @@
     FrameLayout mContainerView;
     RecentsView mRecentsView;
     View mEmptyView;
+    View mStatusBarScrimView;
     View mNavBarScrimView;
     FullScreenTransitionView mFullScreenshotView;
 
@@ -180,13 +181,11 @@
                 AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_ALT_TAB, false);
         mConfig.launchedWithNoRecentTasks = !root.hasTasks();
 
-        if (mConfig.shouldAnimateNavBarScrim()) {
-            // Hide the scrim if we animate into Recents with window transitions
-            mNavBarScrimView.setVisibility(View.INVISIBLE);
-        } else {
-            // Show the scrim if we animate into Recents without window transitions
-            mNavBarScrimView.setVisibility(View.VISIBLE);
-        }
+        // Show the scrim if we animate into Recents without window transitions
+        mNavBarScrimView.setVisibility(mConfig.hasNavBarScrim() &&
+                !mConfig.shouldAnimateNavBarScrim() ? View.VISIBLE : View.INVISIBLE);
+        mStatusBarScrimView.setVisibility(mConfig.hasStatusBarScrim() &&
+                !mConfig.shouldAnimateStatusBarScrim() ? View.VISIBLE : View.INVISIBLE);
 
         // Add the default no-recents layout
         if (mConfig.launchedWithNoRecentTasks) {
@@ -325,6 +324,10 @@
         // Create the empty view
         LayoutInflater inflater = LayoutInflater.from(this);
         mEmptyView = inflater.inflate(R.layout.recents_empty, mContainerView, false);
+        mStatusBarScrimView = inflater.inflate(R.layout.recents_status_bar_scrim, mContainerView, false);
+        mStatusBarScrimView.setLayoutParams(new FrameLayout.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.TOP));
         mNavBarScrimView = inflater.inflate(R.layout.recents_nav_bar_scrim, mContainerView, false);
         mNavBarScrimView.setLayoutParams(new FrameLayout.LayoutParams(
                 ViewGroup.LayoutParams.MATCH_PARENT,
@@ -336,6 +339,7 @@
         }
 
         mContainerView = new FrameLayout(this);
+        mContainerView.addView(mStatusBarScrimView);
         mContainerView.addView(mRecentsView);
         mContainerView.addView(mEmptyView);
         if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
@@ -563,8 +567,18 @@
     }
 
     public void onEnterAnimationTriggered() {
-        // Fade in the scrim
-        if (mConfig.shouldAnimateNavBarScrim() && mConfig.hasNavBarScrim()) {
+        // Fade in the scrims
+        if (mConfig.hasStatusBarScrim() && mConfig.shouldAnimateStatusBarScrim()) {
+            mStatusBarScrimView.setVisibility(View.VISIBLE);
+            mStatusBarScrimView.setTranslationY(-mStatusBarScrimView.getMeasuredHeight());
+            mStatusBarScrimView.animate()
+                    .translationY(0)
+                    .setStartDelay(mConfig.taskBarEnterAnimDelay)
+                    .setDuration(mConfig.navBarScrimEnterDuration)
+                    .setInterpolator(mConfig.quintOutInterpolator)
+                    .start();
+        }
+        if (mConfig.hasNavBarScrim() && mConfig.shouldAnimateNavBarScrim()) {
             mNavBarScrimView.setVisibility(View.VISIBLE);
             mNavBarScrimView.setTranslationY(mNavBarScrimView.getMeasuredHeight());
             mNavBarScrimView.animate()
@@ -579,7 +593,7 @@
     @Override
     public void onExitAnimationTriggered() {
         // Fade out the scrim
-        if (mConfig.shouldAnimateNavBarScrim() && mConfig.hasNavBarScrim()) {
+        if (mConfig.hasNavBarScrim() && mConfig.shouldAnimateNavBarScrim()) {
             mNavBarScrimView.animate()
                     .translationY(mNavBarScrimView.getMeasuredHeight())
                     .setStartDelay(0)
@@ -605,14 +619,9 @@
     }
 
     @Override
-    public void onTaskLaunching(boolean isTaskInStackBounds) {
+    public void onTaskLaunching() {
         mTaskLaunched = true;
 
-        // Fade out the scrim
-        if (!isTaskInStackBounds && mConfig.hasNavBarScrim()) {
-            onExitAnimationTriggered();
-        }
-
         // Mark recents as no longer visible
         AlternateRecentsComponent.notifyVisibilityChanged(false);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index c1a8ee6..10978ca 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -47,8 +47,8 @@
     public Interpolator quintOutInterpolator;
 
     /** Filtering */
-    public int filteringCurrentViewsMinAnimDuration;
-    public int filteringNewViewsMinAnimDuration;
+    public int filteringCurrentViewsAnimDuration;
+    public int filteringNewViewsAnimDuration;
 
     /** Insets */
     public Rect systemInsets = new Rect();
@@ -81,7 +81,6 @@
 
     /** Task bar colors */
     public int taskBarViewDefaultBackgroundColor;
-    public int taskBarViewDefaultTextColor;
     public int taskBarViewLightTextColor;
     public int taskBarViewDarkTextColor;
     public int taskBarViewHighlightColor;
@@ -106,12 +105,30 @@
     public boolean developerOptionsEnabled;
 
     /** Private constructor */
-    private RecentsConfiguration() {}
+    private RecentsConfiguration(Context context) {
+        // Properties that don't have to be reloaded with each configuration change can be loaded
+        // here.
+
+        // Interpolators
+        fastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+                com.android.internal.R.interpolator.fast_out_slow_in);
+        fastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
+                com.android.internal.R.interpolator.fast_out_linear_in);
+        linearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+                com.android.internal.R.interpolator.linear_out_slow_in);
+        quintOutInterpolator = AnimationUtils.loadInterpolator(context,
+                com.android.internal.R.interpolator.decelerate_quint);
+
+        // Check if the developer options are enabled
+        ContentResolver cr = context.getContentResolver();
+        developerOptionsEnabled = Settings.Global.getInt(cr,
+                Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
+    }
 
     /** Updates the configuration to the current context */
     public static RecentsConfiguration reinitialize(Context context) {
         if (sInstance == null) {
-            sInstance = new RecentsConfiguration();
+            sInstance = new RecentsConfiguration(context);
         }
         sInstance.update(context);
         return sInstance;
@@ -132,21 +149,11 @@
         animationPxMovementPerSecond =
                 res.getDimensionPixelSize(R.dimen.recents_animation_movement_in_dps_per_second);
 
-        // Interpolators
-        fastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
-                com.android.internal.R.interpolator.fast_out_slow_in);
-        fastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
-                com.android.internal.R.interpolator.fast_out_linear_in);
-        linearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
-                com.android.internal.R.interpolator.linear_out_slow_in);
-        quintOutInterpolator = AnimationUtils.loadInterpolator(context,
-                com.android.internal.R.interpolator.decelerate_quint);
-
         // Filtering
-        filteringCurrentViewsMinAnimDuration =
-                res.getInteger(R.integer.recents_filter_animate_current_views_min_duration);
-        filteringNewViewsMinAnimDuration =
-                res.getInteger(R.integer.recents_filter_animate_new_views_min_duration);
+        filteringCurrentViewsAnimDuration =
+                res.getInteger(R.integer.recents_filter_animate_current_views_duration);
+        filteringNewViewsAnimDuration =
+                res.getInteger(R.integer.recents_filter_animate_new_views_duration);
 
         // Insets
         displayRect.set(0, 0, dm.widthPixels, dm.heightPixels);
@@ -194,8 +201,6 @@
         // Task bar colors
         taskBarViewDefaultBackgroundColor =
                 res.getColor(R.color.recents_task_bar_default_background_color);
-        taskBarViewDefaultTextColor =
-                res.getColor(R.color.recents_task_bar_default_text_color);
         taskBarViewLightTextColor =
                 res.getColor(R.color.recents_task_bar_light_text_color);
         taskBarViewDarkTextColor =
@@ -217,11 +222,6 @@
         navBarScrimEnterDuration =
                 res.getInteger(R.integer.recents_nav_bar_scrim_enter_duration);
 
-        // Check if the developer options are enabled
-        ContentResolver cr = context.getContentResolver();
-        developerOptionsEnabled = Settings.Global.getInt(cr,
-                Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
-
         if (Console.Enabled) {
             Console.log(Constants.Log.UI.MeasureAndLayout,
                     "[RecentsConfiguration|orientation]", isLandscape ? "Landscape" : "Portrait",
@@ -257,6 +257,16 @@
         return searchBarAppWidgetId >= 0;
     }
 
+    /** Returns whether the status bar scrim should be animated when shown for the first time. */
+    public boolean shouldAnimateStatusBarScrim() {
+        return launchedFromHome;
+    }
+
+    /** Returns whether the status bar scrim should be visible. */
+    public boolean hasStatusBarScrim() {
+        return !launchedWithNoRecentTasks;
+    }
+
     /** Returns whether the nav bar scrim should be animated when shown for the first time. */
     public boolean shouldAnimateNavBarScrim() {
         return true;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
index a02e1a7b..7762111 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
@@ -118,6 +118,7 @@
     TaskResourceLoadQueue mLoadQueue;
     DrawableLruCache mApplicationIconCache;
     BitmapLruCache mThumbnailCache;
+    Bitmap mDefaultThumbnail;
 
     boolean mCancelled;
     boolean mWaitingOnLoadQueue;
@@ -125,10 +126,12 @@
     /** Constructor, creates a new loading thread that loads task resources in the background */
     public TaskResourceLoader(TaskResourceLoadQueue loadQueue,
                               DrawableLruCache applicationIconCache,
-                              BitmapLruCache thumbnailCache) {
+                              BitmapLruCache thumbnailCache,
+                              Bitmap defaultThumbnail) {
         mLoadQueue = loadQueue;
         mApplicationIconCache = applicationIconCache;
         mThumbnailCache = thumbnailCache;
+        mDefaultThumbnail = defaultThumbnail;
         mMainThreadHandler = new Handler();
         mLoadThread = new HandlerThread("Recents-TaskResourceLoader");
         mLoadThread.setPriority(Thread.NORM_PRIORITY - 1);
@@ -238,6 +241,7 @@
                                 loadThumbnail = thumbnail;
                                 mThumbnailCache.put(t.key, thumbnail);
                             } else {
+                                loadThumbnail = mDefaultThumbnail;
                                 Console.logError(mContext,
                                         "Failed to load task top thumbnail for: " +
                                                 t.key.baseIntent.getComponent().getPackageName());
@@ -330,6 +334,7 @@
 
     BitmapDrawable mDefaultApplicationIcon;
     Bitmap mDefaultThumbnail;
+    Bitmap mLoadingThumbnail;
 
     /** Private Constructor */
     private RecentsTaskLoader(Context context) {
@@ -356,18 +361,22 @@
         mLoadQueue = new TaskResourceLoadQueue();
         mApplicationIconCache = new DrawableLruCache(iconCacheSize);
         mThumbnailCache = new BitmapLruCache(thumbnailCacheSize);
-        mLoader = new TaskResourceLoader(mLoadQueue, mApplicationIconCache, mThumbnailCache);
+        mLoader = new TaskResourceLoader(mLoadQueue, mApplicationIconCache, mThumbnailCache,
+                mDefaultThumbnail);
 
         // Create the default assets
         Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
         icon.eraseColor(0x00000000);
         mDefaultThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
-        mDefaultThumbnail.eraseColor(0x00000000);
+        mDefaultThumbnail.eraseColor(0xFFffffff);
+        mLoadingThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+        mLoadingThumbnail.eraseColor(0x00000000);
         mDefaultApplicationIcon = new BitmapDrawable(context.getResources(), icon);
         if (Console.Enabled) {
             Console.log(Constants.Log.App.TaskDataLoader,
                     "[RecentsTaskLoader|defaultBitmaps]",
-                    "icon: " + mDefaultApplicationIcon + " thumbnail: " + mDefaultThumbnail, Console.AnsiRed);
+                    "icon: " + mDefaultApplicationIcon +
+                    " default thumbnail: " + mDefaultThumbnail, Console.AnsiRed);
         }
     }
 
@@ -394,7 +403,7 @@
 
         SystemServicesProxy ssp = mSystemServicesProxy;
         List<ActivityManager.RecentTaskInfo> tasks =
-                ssp.getRecentTasks(100, UserHandle.CURRENT.getIdentifier());
+                ssp.getRecentTasks(50, UserHandle.CURRENT.getIdentifier());
         Collections.reverse(tasks);
         if (Console.Enabled) {
             Console.log(Constants.Log.App.TimeSystemCalls,
@@ -544,7 +553,7 @@
             requiresLoad = true;
         }
         if (thumbnail == null) {
-            thumbnail = mDefaultThumbnail;
+            thumbnail = mLoadingThumbnail;
             requiresLoad = true;
         }
         if (requiresLoad) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java b/packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java
index c861d2c..cadfc56 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Paint;
 import android.graphics.Rect;
 import android.view.View;
 import android.view.ViewGroup;
@@ -49,8 +50,8 @@
     FullScreenTransitionViewCallbacks mCb;
 
     ImageView mScreenshotView;
-
     Rect mClipRect = new Rect();
+    Paint mLayerPaint = new Paint();
 
     boolean mIsAnimating;
     AnimatorSet mEnterAnimation;
@@ -159,7 +160,7 @@
         int clipBottom = mConfig.systemInsets.top + (int) (ctx.taskRect.height() / scale);
 
         // Enable the HW Layers on the screenshot view
-        mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+        mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
 
         // Compose the animation
         mEnterAnimation = new AnimatorSet();
@@ -173,7 +174,7 @@
                 // Mark that we are no longer animating
                 mIsAnimating = false;
                 // Disable the HW Layers on this view
-                setLayerType(View.LAYER_TYPE_NONE, null);
+                setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
 
                 if (Console.Enabled) {
                     Console.logTraceTime(Constants.Log.App.TimeRecentsScreenshotTransition,
@@ -217,7 +218,7 @@
                     // Mark that we are no longer animating
                     mIsAnimating = false;
                     // Disable the HW Layers on the screenshot view
-                    mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, null);
+                    mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
                 }
             });
             mEnterAnimation.setDuration(475);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 3e6879d..7248758 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -54,7 +54,7 @@
 
     /** The RecentsView callbacks */
     public interface RecentsViewCallbacks {
-        public void onTaskLaunching(boolean isTaskInStackBounds);
+        public void onTaskLaunching();
         public void onExitAnimationTriggered();
     }
 
@@ -389,13 +389,46 @@
                                final TaskStack stack, final Task task) {
         // Notify any callbacks of the launching of a new task
         if (mCb != null) {
-            boolean isTaskInStackBounds = false;
-            if (stackView != null && tv != null) {
-                isTaskInStackBounds = stackView.isTaskInStackBounds(tv);
-            }
-            mCb.onTaskLaunching(isTaskInStackBounds);
+            mCb.onTaskLaunching();
         }
 
+        // Upfront the processing of the thumbnail
+        TaskViewTransform transform;
+        View sourceView = tv;
+        int offsetX = 0;
+        int offsetY = 0;
+        int stackScroll = stackView.getStackScroll();
+        if (tv == null) {
+            // If there is no actual task view, then use the stack view as the source view
+            // and then offset to the expected transform rect, but bound this to just
+            // outside the display rect (to ensure we don't animate from too far away)
+            sourceView = stackView;
+            transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll);
+            offsetX = transform.rect.left;
+            offsetY = Math.min(transform.rect.top, mConfig.displayRect.height());
+        } else {
+            transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll);
+        }
+
+        // Compute the thumbnail to scale up from
+        ActivityOptions opts = null;
+        int thumbnailWidth = transform.rect.width();
+        int thumbnailHeight = transform.rect.height();
+        if (task.thumbnail != null && thumbnailWidth > 0 && thumbnailHeight > 0 &&
+                task.thumbnail.getWidth() > 0 && task.thumbnail.getHeight() > 0) {
+            // Resize the thumbnail to the size of the view that we are animating from
+            Bitmap b = Bitmap.createBitmap(thumbnailWidth, thumbnailHeight,
+                    Bitmap.Config.ARGB_8888);
+            Canvas c = new Canvas(b);
+            c.drawBitmap(task.thumbnail,
+                    new Rect(0, 0, task.thumbnail.getWidth(), task.thumbnail.getHeight()),
+                    new Rect(0, 0, thumbnailWidth, thumbnailHeight), null);
+            c.setBitmap(null);
+            opts = ActivityOptions.makeThumbnailScaleUpAnimation(sourceView,
+                    b, offsetX, offsetY);
+        }
+
+        final ActivityOptions launchOpts = opts;
         final Runnable launchRunnable = new Runnable() {
             @Override
             public void run() {
@@ -404,45 +437,10 @@
                             Constants.Log.App.TimeRecentsLaunchKey, "preStartActivity");
                 }
 
-                TaskViewTransform transform;
-                View sourceView = tv;
-                int offsetX = 0;
-                int offsetY = 0;
-                int stackScroll = stackView.getStackScroll();
-                if (tv == null) {
-                    // If there is no actual task view, then use the stack view as the source view
-                    // and then offset to the expected transform rect, but bound this to just
-                    // outside the display rect (to ensure we don't animate from too far away)
-                    sourceView = stackView;
-                    transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll);
-                    offsetX = transform.rect.left;
-                    offsetY = Math.min(transform.rect.top, mConfig.displayRect.height());
-                } else {
-                    transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll);
-                }
-
-                // Compute the thumbnail to scale up from
-                ActivityOptions opts = null;
-                int thumbnailWidth = transform.rect.width();
-                int thumbnailHeight = transform.rect.height();
-                if (task.thumbnail != null && thumbnailWidth > 0 && thumbnailHeight > 0 &&
-                        task.thumbnail.getWidth() > 0 && task.thumbnail.getHeight() > 0) {
-                    // Resize the thumbnail to the size of the view that we are animating from
-                    Bitmap b = Bitmap.createBitmap(thumbnailWidth, thumbnailHeight,
-                            Bitmap.Config.ARGB_8888);
-                    Canvas c = new Canvas(b);
-                    c.drawBitmap(task.thumbnail,
-                            new Rect(0, 0, task.thumbnail.getWidth(), task.thumbnail.getHeight()),
-                            new Rect(0, 0, thumbnailWidth, thumbnailHeight), null);
-                    c.setBitmap(null);
-                    opts = ActivityOptions.makeThumbnailScaleUpAnimation(sourceView,
-                            b, offsetX, offsetY);
-                }
-
                 if (task.isActive) {
                     // Bring an active task to the foreground
                     RecentsTaskLoader.getInstance().getSystemServicesProxy()
-                            .moveTaskToFront(task.key.id, opts);
+                            .moveTaskToFront(task.key.id, launchOpts);
                 } else {
                     // Launch the activity anew with the desired animation
                     Intent i = new Intent(task.key.baseIntent);
@@ -451,8 +449,8 @@
                             | Intent.FLAG_ACTIVITY_NEW_TASK);
                     try {
                         UserHandle taskUser = new UserHandle(task.userId);
-                        if (opts != null) {
-                            getContext().startActivityAsUser(i, opts.toBundle(), taskUser);
+                        if (launchOpts != null) {
+                            getContext().startActivityAsUser(i, launchOpts.toBundle(), taskUser);
                         } else {
                             getContext().startActivityAsUser(i, taskUser);
                         }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
index 82d6220..1ef58ad 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
@@ -23,6 +23,7 @@
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.RippleDrawable;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewPropertyAnimator;
@@ -50,6 +51,7 @@
     Drawable mLightDismissDrawable;
     Drawable mDarkDismissDrawable;
 
+    Paint mLayerPaint = new Paint();
     static Paint sHighlightPaint;
 
     public TaskBarView(Context context) {
@@ -91,6 +93,13 @@
         mApplicationIcon = (ImageView) findViewById(R.id.application_icon);
         mActivityDescription = (TextView) findViewById(R.id.activity_description);
         mDismissButton = (ImageView) findViewById(R.id.dismiss_task);
+
+        // Hide the backgrounds if they are ripple drawables
+        if (!Constants.DebugFlags.App.EnableTaskFiltering) {
+            if (mApplicationIcon.getBackground() instanceof RippleDrawable) {
+                mApplicationIcon.setBackground(null);
+            }
+        }
     }
 
     @Override
@@ -142,16 +151,14 @@
         mActivityDescription.setText(t.activityLabel);
         // Try and apply the system ui tint
         int tint = t.colorPrimary;
-        if (Constants.DebugFlags.App.EnableTaskBarThemeColors && tint != 0) {
-            setBackgroundColor(tint);
-            mActivityDescription.setTextColor(Utilities.getIdealColorForBackgroundColor(tint,
-                    mConfig.taskBarViewLightTextColor, mConfig.taskBarViewDarkTextColor));
-            mDismissButton.setImageDrawable(Utilities.getIdealResourceForBackgroundColor(tint,
-                    mLightDismissDrawable, mDarkDismissDrawable));
-        } else {
-            setBackgroundColor(mConfig.taskBarViewDefaultBackgroundColor);
-            mActivityDescription.setTextColor(mConfig.taskBarViewDefaultTextColor);
+        if (!Constants.DebugFlags.App.EnableTaskBarThemeColors || tint == 0) {
+            tint = mConfig.taskBarViewDefaultBackgroundColor;
         }
+        setBackgroundColor(tint);
+        mActivityDescription.setTextColor(Utilities.getIdealColorForBackgroundColor(tint,
+                mConfig.taskBarViewLightTextColor, mConfig.taskBarViewDarkTextColor));
+        mDismissButton.setImageDrawable(Utilities.getIdealResourceForBackgroundColor(tint,
+                mLightDismissDrawable, mDarkDismissDrawable));
     }
 
     /** Unbinds the bar view from the task */
@@ -237,11 +244,11 @@
 
     /** Enable the hw layers on this task view */
     void enableHwLayers() {
-        mDismissButton.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+        mDismissButton.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
     }
 
     /** Disable the hw layers on this task view */
     void disableHwLayers() {
-        mDismissButton.setLayerType(View.LAYER_TYPE_NONE, null);
+        mDismissButton.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index e0a12b7..55f9335 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -187,12 +187,20 @@
         return null;
     }
 
-    /** Update/get the transform */
+    /** Update/get the transform (creates a new TaskViewTransform) */
     public TaskViewTransform getStackTransform(int indexInStack, int stackScroll) {
         TaskViewTransform transform = new TaskViewTransform();
+        return getStackTransform(indexInStack, stackScroll, transform);
+    }
 
+    /** Update/get the transform */
+    public TaskViewTransform getStackTransform(int indexInStack, int stackScroll,
+                                               TaskViewTransform transformOut) {
         // Return early if we have an invalid index
-        if (indexInStack < 0) return transform;
+        if (indexInStack < 0) {
+            transformOut.reset();
+            return transformOut;
+        }
 
         // Map the items to an continuous position relative to the specified scroll
         int numPeekCards = Constants.Values.TaskStackView.StackPeekNumCards;
@@ -209,35 +217,35 @@
         float scale = Math.max(minScale, Math.min(1f, minScale + 
             ((boundedT + (numPeekCards + 1)) * scaleInc)));
         float scaleYOffset = ((1f - scale) * mTaskRect.height()) / 2;
-        transform.scale = scale;
+        transformOut.scale = scale;
 
         // Set the y translation
         if (boundedT < 0f) {
-            transform.translationY = (int) ((Math.max(-numPeekCards, boundedT) /
+            transformOut.translationY = (int) ((Math.max(-numPeekCards, boundedT) /
                     numPeekCards) * peekHeight - scaleYOffset);
         } else {
-            transform.translationY = (int) (boundedT * overlapHeight - scaleYOffset);
+            transformOut.translationY = (int) (boundedT * overlapHeight - scaleYOffset);
         }
 
         // Set the z translation
         int minZ = mConfig.taskViewTranslationZMinPx;
         int incZ = mConfig.taskViewTranslationZIncrementPx;
-        transform.translationZ = (int) Math.max(minZ, minZ + ((boundedT + numPeekCards) * incZ));
+        transformOut.translationZ = (int) Math.max(minZ, minZ + ((boundedT + numPeekCards) * incZ));
 
         // Set the alphas
-        transform.dismissAlpha = Math.max(-1f, Math.min(0f, t + 1)) + 1f;
+        transformOut.dismissAlpha = Math.max(-1f, Math.min(0f, t + 1)) + 1f;
 
         // Update the rect and visibility
-        transform.rect.set(mTaskRect);
+        transformOut.rect.set(mTaskRect);
         if (t < -(numPeekCards + 1)) {
-            transform.visible = false;
+            transformOut.visible = false;
         } else {
-            transform.rect.offset(0, transform.translationY);
-            Utilities.scaleRectAboutCenter(transform.rect, transform.scale);
-            transform.visible = Rect.intersects(mRect, transform.rect);
+            transformOut.rect.offset(0, transformOut.translationY);
+            Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
+            transformOut.visible = Rect.intersects(mRect, transformOut.rect);
         }
-        transform.t = t;
-        return transform;
+        transformOut.t = t;
+        return transformOut;
     }
 
     /**
@@ -250,14 +258,25 @@
                                        boolean boundTranslationsToRect) {
         // XXX: Optimization: Use binary search to find the visible range
 
+        int taskTransformCount = taskTransforms.size();
         int taskCount = tasks.size();
         int firstVisibleIndex = -1;
         int lastVisibleIndex = -1;
-        taskTransforms.clear();
-        taskTransforms.ensureCapacity(taskCount);
+
+        // We can reuse the task transforms where possible to reduce object allocation
+        if (taskTransformCount < taskCount) {
+            // If there are less transforms than tasks, then add as many transforms as necessary
+            for (int i = taskTransformCount; i < taskCount; i++) {
+                taskTransforms.add(new TaskViewTransform());
+            }
+        } else if (taskTransformCount > taskCount) {
+            // If there are more transforms than tasks, then just subset the transform list
+            taskTransforms.subList(0, taskCount);
+        }
+
+        // Update the stack transforms
         for (int i = 0; i < taskCount; i++) {
-            TaskViewTransform transform = getStackTransform(i, stackScroll);
-            taskTransforms.add(transform);
+            TaskViewTransform transform = getStackTransform(i, stackScroll, taskTransforms.get(i));
             if (transform.visible) {
                 if (firstVisibleIndex < 0) {
                     firstVisibleIndex = i;
@@ -954,10 +973,11 @@
     int getExitTransformsForFilterAnimation(ArrayList<Task> curTasks,
                         ArrayList<TaskViewTransform> curTaskTransforms,
                         ArrayList<Task> tasks, ArrayList<TaskViewTransform> taskTransforms,
-                        HashMap<TaskView, Pair<Integer, TaskViewTransform>> childViewTransformsOut,
+                        HashMap<TaskView, TaskViewTransform> childViewTransformsOut,
                         ArrayList<TaskView> childrenToRemoveOut) {
         // Animate all of the existing views out of view (if they are not in the visible range in
         // the new stack) or to their final positions in the new stack
+        int offset = 0;
         int movement = 0;
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
@@ -982,10 +1002,12 @@
                 movement = Math.max(movement, Math.abs(toTransform.translationY -
                         (int) tv.getTranslationY()));
             }
-            childViewTransformsOut.put(tv, new Pair(0, toTransform));
+
+            toTransform.startDelay = offset * Constants.Values.TaskStackView.FilterStartDelay;
+            childViewTransformsOut.put(tv, toTransform);
+            offset++;
         }
-        return Utilities.calculateTranslationAnimationDuration(movement,
-                mConfig.filteringCurrentViewsMinAnimDuration);
+        return mConfig.filteringCurrentViewsAnimDuration;
     }
 
     /**
@@ -994,7 +1016,7 @@
      */
     int getEnterTransformsForFilterAnimation(ArrayList<Task> tasks,
                          ArrayList<TaskViewTransform> taskTransforms,
-                         HashMap<TaskView, Pair<Integer, TaskViewTransform>> childViewTransformsOut) {
+                         HashMap<TaskView, TaskViewTransform> childViewTransformsOut) {
         int offset = 0;
         int movement = 0;
         int taskCount = tasks.size();
@@ -1012,9 +1034,8 @@
                     tv.prepareTaskTransformForFilterTaskHidden(fromTransform);
                     tv.updateViewPropertiesToTaskTransform(fromTransform, 0);
 
-                    int startDelay = offset *
-                            Constants.Values.TaskStackView.FilterStartDelay;
-                    childViewTransformsOut.put(tv, new Pair(startDelay, toTransform));
+                    toTransform.startDelay = offset * Constants.Values.TaskStackView.FilterStartDelay;
+                    childViewTransformsOut.put(tv, toTransform);
 
                     // Use the movement of the new views to calculate the duration of the animation
                     movement = Math.max(movement,
@@ -1023,8 +1044,7 @@
                 }
             }
         }
-        return Utilities.calculateTranslationAnimationDuration(movement,
-                mConfig.filteringNewViewsMinAnimDuration);
+        return mConfig.filteringNewViewsAnimDuration;
     }
 
     /** Orchestrates the animations of the current child views and any new views. */
@@ -1035,8 +1055,8 @@
         // Calculate the transforms to animate out all the existing views if they are not in the
         // new visible range (or to their final positions in the stack if they are)
         final ArrayList<TaskView> childrenToRemove = new ArrayList<TaskView>();
-        final HashMap<TaskView, Pair<Integer, TaskViewTransform>> childViewTransforms =
-                new HashMap<TaskView, Pair<Integer, TaskViewTransform>>();
+        final HashMap<TaskView, TaskViewTransform> childViewTransforms =
+                new HashMap<TaskView, TaskViewTransform>();
         int duration = getExitTransformsForFilterAnimation(curTasks, curTaskTransforms, tasks,
                 taskTransforms, childViewTransforms, childrenToRemove);
 
@@ -1051,10 +1071,9 @@
 
         // Animate all the views to their final transforms
         for (final TaskView tv : childViewTransforms.keySet()) {
-            Pair<Integer, TaskViewTransform> t = childViewTransforms.get(tv);
+            TaskViewTransform t = childViewTransforms.get(tv);
             tv.animate().cancel();
             tv.animate()
-                    .setStartDelay(t.first)
                     .withEndAction(new Runnable() {
                         @Override
                         public void run() {
@@ -1071,15 +1090,14 @@
                                     int duration = getEnterTransformsForFilterAnimation(tasks,
                                             taskTransforms, childViewTransforms);
                                     for (final TaskView tv : childViewTransforms.keySet()) {
-                                        Pair<Integer, TaskViewTransform> t = childViewTransforms.get(tv);
-                                        tv.animate().setStartDelay(t.first);
-                                        tv.updateViewPropertiesToTaskTransform(t.second, duration);
+                                        TaskViewTransform t = childViewTransforms.get(tv);
+                                        tv.updateViewPropertiesToTaskTransform(t, duration);
                                     }
                                 }
                             }
                         }
                     });
-            tv.updateViewPropertiesToTaskTransform(t.second, duration);
+            tv.updateViewPropertiesToTaskTransform(t, duration);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 09dc1c8..cfba74c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Outline;
+import android.graphics.Paint;
 import android.graphics.Path;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -63,6 +64,7 @@
     Point mLastTouchDown = new Point();
     Path mRoundedRectClipPath = new Path();
     Rect mTmpRect = new Rect();
+    Paint mLayerPaint = new Paint();
 
     TaskThumbnailView mThumbnailView;
     TaskBarView mBarView;
@@ -200,7 +202,7 @@
             if (useLayers) {
                 anim.withLayer();
             }
-            anim.setStartDelay(0)
+            anim.setStartDelay(toTransform.startDelay)
                 .setDuration(duration)
                 .setInterpolator(mConfig.fastOutSlowInInterpolator)
                 .start();
@@ -246,6 +248,7 @@
         // Fade the view out and slide it away
         toTransform.alpha = 0f;
         toTransform.translationY += 200;
+        toTransform.translationZ = 0;
     }
 
     /**
@@ -440,13 +443,13 @@
 
     /** Enable the hw layers on this task view */
     void enableHwLayers() {
-        mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+        mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
         mBarView.enableHwLayers();
     }
 
     /** Disable the hw layers on this task view */
     void disableHwLayers() {
-        mThumbnailView.setLayerType(View.LAYER_TYPE_NONE, null);
+        mThumbnailView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
         mBarView.disableHwLayers();
     }
 
@@ -583,19 +586,25 @@
     }
 
     @Override
-    public void onClick(View v) {
-        if (v == mBarView.mApplicationIcon) {
-            mCb.onTaskIconClicked(this);
-        } else if (v == mBarView.mDismissButton) {
-            // Animate out the view and call the callback
-            final TaskView tv = this;
-            startDeleteTaskAnimation(new Runnable() {
-                @Override
-                public void run() {
-                    mCb.onTaskDismissed(tv);
+    public void onClick(final View v) {
+        // We purposely post the handler delayed to allow for the touch feedback to draw
+        final TaskView tv = this;
+        postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                if (v == mBarView.mApplicationIcon) {
+                    mCb.onTaskIconClicked(tv);
+                } else if (v == mBarView.mDismissButton) {
+                    // Animate out the view and call the callback
+                    startDeleteTaskAnimation(new Runnable() {
+                        @Override
+                        public void run() {
+                            mCb.onTaskDismissed(tv);
+                        }
+                    });
                 }
-            });
-        }
+            }
+        }, 125);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
index 6c420e1..b351b03 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
@@ -21,6 +21,7 @@
 
 /* The transform state for a task view */
 public class TaskViewTransform {
+    public int startDelay = 0;
     public int translationY = 0;
     public int translationZ = 0;
     public float scale = 1f;
@@ -28,13 +29,14 @@
     public float dismissAlpha = 1f;
     public boolean visible = false;
     public Rect rect = new Rect();
-    float t;
+    float t = 0f;
 
     public TaskViewTransform() {
         // Do nothing
     }
 
     public TaskViewTransform(TaskViewTransform o) {
+        startDelay = o.startDelay;
         translationY = o.translationY;
         translationZ = o.translationZ;
         scale = o.scale;
@@ -45,6 +47,19 @@
         t = o.t;
     }
 
+    /** Resets the current transform */
+    public void reset() {
+        startDelay = 0;
+        translationY = 0;
+        translationZ = 0;
+        scale = 1f;
+        alpha = 1f;
+        dismissAlpha = 1f;
+        visible = false;
+        rect.setEmpty();
+        t = 0f;
+    }
+
     /** Convenience functions to compare against current property values */
     public boolean hasAlphaChangedFrom(float v) {
         return (Float.compare(alpha, v) != 0);
@@ -64,8 +79,8 @@
 
     @Override
     public String toString() {
-        return "TaskViewTransform y: " + translationY + " z: " + translationZ + " scale: " + scale +
-                " alpha: " + alpha + " visible: " + visible + " rect: " + rect +
+        return "TaskViewTransform delay: " + startDelay + " y: " + translationY + " z: " + translationZ +
+                " scale: " + scale + " alpha: " + alpha + " visible: " + visible + " rect: " + rect +
                 " dismissAlpha: " + dismissAlpha;
     }
 }
diff --git a/rs/java/android/renderscript/Element.java b/rs/java/android/renderscript/Element.java
index 55b671d..c6b5b0d 100644
--- a/rs/java/android/renderscript/Element.java
+++ b/rs/java/android/renderscript/Element.java
@@ -140,17 +140,17 @@
         MATRIX_3X3 (17, 36),
         MATRIX_2X2 (18, 16),
 
-        RS_ELEMENT (1000, 4),
-        RS_TYPE (1001, 4),
-        RS_ALLOCATION (1002, 4),
-        RS_SAMPLER (1003, 4),
-        RS_SCRIPT (1004, 4),
-        RS_MESH (1005, 4),
-        RS_PROGRAM_FRAGMENT (1006, 4),
-        RS_PROGRAM_VERTEX (1007, 4),
-        RS_PROGRAM_RASTER (1008, 4),
-        RS_PROGRAM_STORE (1009, 4),
-        RS_FONT (1010, 4);
+        RS_ELEMENT (1000),
+        RS_TYPE (1001),
+        RS_ALLOCATION (1002),
+        RS_SAMPLER (1003),
+        RS_SCRIPT (1004),
+        RS_MESH (1005),
+        RS_PROGRAM_FRAGMENT (1006),
+        RS_PROGRAM_VERTEX (1007),
+        RS_PROGRAM_RASTER (1008),
+        RS_PROGRAM_STORE (1009),
+        RS_FONT (1010);
 
         int mID;
         int mSize;
@@ -158,6 +158,14 @@
             mID = id;
             mSize = size;
         }
+
+        DataType(int id) {
+            mID = id;
+            mSize = 4;
+            if (RenderScript.sPointerSize == 8) {
+                mSize = 32;
+            }
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index aeb195f..912a181 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -18,6 +18,7 @@
 
 import android.database.ContentObserver;
 import android.os.BatteryStats;
+
 import com.android.internal.app.IBatteryStats;
 import com.android.server.am.BatteryStatsService;
 import com.android.server.lights.Light;
@@ -29,6 +30,7 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.os.BatteryManager;
+import android.os.BatteryManagerInternal;
 import android.os.BatteryProperties;
 import android.os.Binder;
 import android.os.FileUtils;
@@ -83,7 +85,7 @@
  * service asynchronously itself.
  * </p>
  */
-public final class BatteryService extends Binder {
+public final class BatteryService extends SystemService {
     private static final String TAG = BatteryService.class.getSimpleName();
 
     private static final boolean DEBUG = false;
@@ -140,10 +142,12 @@
 
     private boolean mSentLowBatteryBroadcast = false;
 
-    public BatteryService(Context context, LightsManager lightsManager) {
+    public BatteryService(Context context) {
+        super(context);
+
         mContext = context;
         mHandler = new Handler(true /*async*/);
-        mLed = new Led(context, lightsManager);
+        mLed = new Led(context, getLocalService(LightsManager.class));
         mBatteryStats = BatteryStatsService.getService();
 
         mCriticalBatteryLevel = mContext.getResources().getInteger(
@@ -160,7 +164,10 @@
             mInvalidChargerObserver.startObserving(
                     "DEVPATH=/devices/virtual/switch/invalid_charger");
         }
+    }
 
+    @Override
+    public void onStart() {
         IBinder b = ServiceManager.getService("batteryproperties");
         final IBatteryPropertiesRegistrar batteryPropertiesRegistrar =
                 IBatteryPropertiesRegistrar.Stub.asInterface(b);
@@ -169,28 +176,34 @@
         } catch (RemoteException e) {
             // Should never happen.
         }
+
+        publishBinderService("battery", new BinderService());
+        publishLocalService(BatteryManagerInternal.class, new LocalService());
     }
 
-    void systemReady() {
-        // check our power situation now that it is safe to display the shutdown dialog.
-        synchronized (mLock) {
-            ContentObserver obs = new ContentObserver(mHandler) {
-                @Override
-                public void onChange(boolean selfChange) {
-                    synchronized (mLock) {
-                        updateBatteryWarningLevelLocked();
+    @Override
+    public void onBootPhase(int phase) {
+        if (phase == PHASE_ACTIVITY_MANAGER_READY) {
+            // check our power situation now that it is safe to display the shutdown dialog.
+            synchronized (mLock) {
+                ContentObserver obs = new ContentObserver(mHandler) {
+                    @Override
+                    public void onChange(boolean selfChange) {
+                        synchronized (mLock) {
+                            updateBatteryWarningLevelLocked();
+                        }
                     }
-                }
-            };
-            final ContentResolver resolver = mContext.getContentResolver();
-            resolver.registerContentObserver(Settings.Global.getUriFor(
-                    Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
-                    false, obs, UserHandle.USER_ALL);
-            updateBatteryWarningLevelLocked();
+                };
+                final ContentResolver resolver = mContext.getContentResolver();
+                resolver.registerContentObserver(Settings.Global.getUriFor(
+                        Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
+                        false, obs, UserHandle.USER_ALL);
+                updateBatteryWarningLevelLocked();
+            }
         }
     }
 
-    void updateBatteryWarningLevelLocked() {
+    private void updateBatteryWarningLevelLocked() {
         final ContentResolver resolver = mContext.getContentResolver();
         int defWarnLevel = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_lowBatteryWarningLevel);
@@ -207,15 +220,6 @@
         processValuesLocked(true);
     }
 
-    /**
-     * Returns true if the device is plugged into any of the specified plug types.
-     */
-    public boolean isPowered(int plugTypeSet) {
-        synchronized (mLock) {
-            return isPoweredLocked(plugTypeSet);
-        }
-    }
-
     private boolean isPoweredLocked(int plugTypeSet) {
         // assume we are powered if battery state is unknown so
         // the "stay on while plugged in" option will work.
@@ -234,34 +238,7 @@
         return false;
     }
 
-    /**
-     * Returns the current plug type.
-     */
-    public int getPlugType() {
-        synchronized (mLock) {
-            return mPlugType;
-        }
-    }
-
-    /**
-     * Returns battery level as a percentage.
-     */
-    public int getBatteryLevel() {
-        synchronized (mLock) {
-            return mBatteryProps.batteryLevel;
-        }
-    }
-
-    /**
-     * Returns whether we currently consider the battery level to be low.
-     */
-    public boolean getBatteryLevelLow() {
-        synchronized (mLock) {
-            return mBatteryLevelLow;
-        }
-    }
-
-    public boolean shouldSendBatteryLowLocked() {
+    private boolean shouldSendBatteryLowLocked() {
         final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE;
         final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE;
 
@@ -277,15 +254,6 @@
                 && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
     }
 
-    /**
-     * Returns a non-zero value if an  unsupported charger is attached.
-     */
-    public int getInvalidCharger() {
-        synchronized (mLock) {
-            return mInvalidCharger;
-        }
-    }
-
     private void shutdownIfNoPowerLocked() {
         // shut down gracefully if our battery is critically low and we are not powered.
         // wait until the system has booted before attempting to display the shutdown dialog.
@@ -640,17 +608,7 @@
         }
     }
 
-    @Override
-    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
-                != PackageManager.PERMISSION_GRANTED) {
-
-            pw.println("Permission Denial: can't dump Battery service from from pid="
-                    + Binder.getCallingPid()
-                    + ", uid=" + Binder.getCallingUid());
-            return;
-        }
-
+    private void dumpInternal(PrintWriter pw, String[] args) {
         synchronized (mLock) {
             if (args == null || args.length == 0 || "-a".equals(args[0])) {
                 pw.println("Current Battery Service state:");
@@ -801,4 +759,57 @@
             }
        }
     }
+
+    private final class BinderService extends Binder {
+        @Override
+        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+                    != PackageManager.PERMISSION_GRANTED) {
+
+                pw.println("Permission Denial: can't dump Battery service from from pid="
+                        + Binder.getCallingPid()
+                        + ", uid=" + Binder.getCallingUid());
+                return;
+            }
+
+            dumpInternal(pw, args);
+        }
+    }
+
+    private final class LocalService extends BatteryManagerInternal {
+        @Override
+        public boolean isPowered(int plugTypeSet) {
+            synchronized (mLock) {
+                return isPoweredLocked(plugTypeSet);
+            }
+        }
+
+        @Override
+        public int getPlugType() {
+            synchronized (mLock) {
+                return mPlugType;
+            }
+        }
+
+        @Override
+        public int getBatteryLevel() {
+            synchronized (mLock) {
+                return mBatteryProps.batteryLevel;
+            }
+        }
+
+        @Override
+        public boolean getBatteryLevelLow() {
+            synchronized (mLock) {
+                return mBatteryLevelLow;
+            }
+        }
+
+        @Override
+        public int getInvalidCharger() {
+            synchronized (mLock) {
+                return mInvalidCharger;
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 9d92421..1bd837b 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -89,7 +89,7 @@
 
         @Override
         public String toString() {
-            return "{pkgForDebug=" + pkgForDebug + " callerUid=" + callerUid +
+            return "{pkgForDebug=" + pkgForDebug + " callerUid=" + callerUid + " subId=" + subId +
                     " events=" + Integer.toHexString(events) + "}";
         }
     }
@@ -208,11 +208,13 @@
             String action = intent.getAction();
             Slog.d(TAG, "mBroadcastReceiver: action=" + action);
             if (Intent.ACTION_USER_SWITCHED.equals(action)) {
-                mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHED,
-                       intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0), 0));
+                int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+                if (DBG) Slog.d(TAG, "onReceive: userHandle=" + userHandle);
+                mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHED, userHandle, 0));
             } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_SUBSCRIPTION_CHANGED)) {
                 mDefaultSubId = intent.getLongExtra(PhoneConstants.SUBSCRIPTION_KEY,
                         SubscriptionManager.getDefaultSubId());
+                if (DBG) Slog.d(TAG, "onReceive: mDefaultSubId=" + mDefaultSubId);
                 mHandler.sendMessage(mHandler.obtainMessage(MSG_UPDATE_DEFAULT_SUB, 0, 0));
             }
         }
@@ -340,18 +342,19 @@
                     // the received subId value update the isLegacyApp field
                     if ((r.subId <= 0) || (r.subId == SubscriptionManager.INVALID_SUB_ID)) {
                         r.subId = mDefaultSubId;
-                        r.isLegacyApp = true; // FIXME: is this needed ??
+                        r.isLegacyApp = true; // r.subId is to be update when default changes.
                     }
                     if (r.subId == SubscriptionManager.DEFAULT_SUB_ID) {
                         r.subId = mDefaultSubId;
+                        r.isLegacyApp = true; // r.subId is to be update when default changes.
                         if (DBG) Slog.i(TAG, "listen: DEFAULT_SUB_ID");
                     }
                     mRecords.add(r);
-                    if (DBG) Slog.i(TAG, "listen: add new record=" + r);
+                    if (DBG) Slog.i(TAG, "listen: add new record");
                 }
                 int phoneId = SubscriptionManager.getPhoneId(subId);
-                int send = events & (events ^ r.events);
                 r.events = events;
+                if (DBG) Slog.i(TAG, "listen: set events record=" + r);
                 if (notifyNow && validatePhoneId(phoneId)) {
                     if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
                         try {
@@ -1063,6 +1066,7 @@
             pw.println("  mDataConnectionLinkProperties=" + mDataConnectionLinkProperties);
             pw.println("  mDataConnectionNetworkCapabilities=" +
                     mDataConnectionNetworkCapabilities);
+            pw.println("  mDefaultSubId=" + mDefaultSubId);
             pw.println("  mCellLocation=" + mCellLocation);
             pw.println("  mCellInfo=" + mCellInfo);
             pw.println("  mDcRtInfo=" + mDcRtInfo);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 697e1f2..34c1ecd 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2194,6 +2194,11 @@
         LocalServices.addService(ActivityManagerInternal.class, new LocalService());
     }
 
+    public void initPowerManagement() {
+        mStackSupervisor.initPowerManagement();
+        mBatteryStatsService.initPowerManagement();
+    }
+
     @Override
     public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
             throws RemoteException {
@@ -7625,14 +7630,24 @@
         }
     }
 
-    private boolean isLockTaskAuthorized(ComponentName name) {
+    private boolean isLockTaskAuthorized(String pkg) {
         final DevicePolicyManager dpm = (DevicePolicyManager)
                 mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
-        return dpm != null && dpm.isLockTaskPermitted(name);
+        try {
+            int uid = mContext.getPackageManager().getPackageUid(pkg,
+                    Binder.getCallingUserHandle().getIdentifier());
+            return (uid == Binder.getCallingUid()) && dpm != null && dpm.isLockTaskPermitted(pkg);
+        } catch (NameNotFoundException e) {
+            return false;
+        }
     }
 
     private void startLockTaskMode(TaskRecord task) {
-        if (!isLockTaskAuthorized(task.intent.getComponent())) {
+        final String pkg;
+        synchronized (this) {
+            pkg = task.intent.getComponent().getPackageName();
+        }
+        if (!isLockTaskAuthorized(pkg)) {
             return;
         }
         long ident = Binder.clearCallingIdentity();
@@ -7641,6 +7656,9 @@
                 // Since we lost lock on task, make sure it is still there.
                 task = mStackSupervisor.anyTaskForIdLocked(task.taskId);
                 if (task != null) {
+                    if ((mFocusedActivity == null) || (task != mFocusedActivity.task)) {
+                        throw new IllegalArgumentException("Invalid task, not in foreground");
+                    }
                     mStackSupervisor.setLockTaskModeLocked(task);
                 }
             }
@@ -7651,25 +7669,25 @@
 
     @Override
     public void startLockTaskMode(int taskId) {
+        final TaskRecord task;
         long ident = Binder.clearCallingIdentity();
         try {
-            final TaskRecord task;
             synchronized (this) {
                 task = mStackSupervisor.anyTaskForIdLocked(taskId);
             }
-            if (task != null) {
-                startLockTaskMode(task);
-            }
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
+        if (task != null) {
+            startLockTaskMode(task);
+        }
     }
 
     @Override
     public void startLockTaskMode(IBinder token) {
+        final TaskRecord task;
         long ident = Binder.clearCallingIdentity();
         try {
-            final TaskRecord task;
             synchronized (this) {
                 final ActivityRecord r = ActivityRecord.forToken(token);
                 if (r == null) {
@@ -7677,24 +7695,27 @@
                 }
                 task = r.task;
             }
-            if (task != null) {
-                startLockTaskMode(task);
-            }
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
+        if (task != null) {
+            startLockTaskMode(task);
+        }
     }
 
     @Override
     public void stopLockTaskMode() {
-        // Check if the calling task is eligible to use lock task
-        final int uid = Binder.getCallingUid();
+        // Verify that the user matches the package of the intent for the TaskRecord
+        // we are locked to.  This will ensure the same caller for startLockTaskMode and
+        // stopLockTaskMode.
         try {
-            final String name = AppGlobals.getPackageManager().getNameForUid(uid);
-            if (!isLockTaskAuthorized(new ComponentName(name, name))) {
-                return;
+            String pkg = mStackSupervisor.mLockTaskModeTask.intent.getPackage();
+            int uid = mContext.getPackageManager().getPackageUid(pkg,
+                    Binder.getCallingUserHandle().getIdentifier());
+            if (uid != Binder.getCallingUid()) {
+                throw new SecurityException("Invalid uid, expected " + uid);
             }
-        } catch (RemoteException e) {
+        } catch (NameNotFoundException e) {
             Log.d(TAG, "stopLockTaskMode " + e);
             return;
         }
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 30446c7..dd9cae9 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -567,7 +567,10 @@
     }
 
     boolean isPersistable() {
-        return (info.flags & ActivityInfo.FLAG_PERSISTABLE) != 0;
+        return (info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY ||
+                info.persistableMode == ActivityInfo.PERSIST_ACROSS_REBOOTS) &&
+                (intent == null ||
+                        (intent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) == 0);
     }
 
     void makeFinishing() {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 66e9eb3e..9264186 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -46,6 +46,8 @@
 import android.app.IActivityManager.WaitResult;
 import android.app.ResultInfo;
 import android.app.StatusBarManager;
+import android.app.admin.DevicePolicyManager;
+import android.app.admin.IDevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.IIntentSender;
@@ -143,6 +145,7 @@
     /** Status Bar Service **/
     private IBinder mToken = new Binder();
     private IStatusBarService mStatusBarService;
+    private IDevicePolicyManager mDevicePolicyManager;
 
     // For debugging to make sure the caller when acquiring/releasing our
     // wake lock is the system process.
@@ -227,14 +230,14 @@
      * receivers to launch an activity and get that to run before the device
      * goes back to sleep.
      */
-    final PowerManager.WakeLock mLaunchingActivity;
+    PowerManager.WakeLock mLaunchingActivity;
 
     /**
      * Set when the system is going to sleep, until we have
      * successfully paused the current activity and released our wake lock.
      * At that point the system is allowed to actually sleep.
      */
-    final PowerManager.WakeLock mGoingToSleep;
+    PowerManager.WakeLock mGoingToSleep;
 
     /** Stack id of the front stack when user switched, indexed by userId. */
     SparseIntArray mUserStackInFront = new SparseIntArray(2);
@@ -251,16 +254,20 @@
 
     /** If non-null then the task specified remains in front and no other tasks may be started
      * until the task exits or #stopLockTaskMode() is called. */
-    private TaskRecord mLockTaskModeTask;
+    TaskRecord mLockTaskModeTask;
 
     public ActivityStackSupervisor(ActivityManagerService service) {
         mService = service;
+        mHandler = new ActivityStackSupervisorHandler(mService.mHandler.getLooper());
+    }
+
+    /**
+     * At the time when the constructor runs, the power manager has not yet been
+     * initialized.  So we initialize our wakelocks afterwards.
+     */
+    void initPowerManagement() {
         PowerManager pm = (PowerManager)mService.mContext.getSystemService(Context.POWER_SERVICE);
         mGoingToSleep = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Sleep");
-        mHandler = new ActivityStackSupervisorHandler(mService.mHandler.getLooper());
-        if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) {
-            throw new IllegalStateException("Calling must be system uid");
-        }
         mLaunchingActivity =
                 pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Launch");
         mLaunchingActivity.setReferenceCounted(false);
@@ -281,6 +288,19 @@
         }
     }
 
+    private IDevicePolicyManager getDevicePolicyManager() {
+        synchronized (mService) {
+            if (mDevicePolicyManager == null) {
+                mDevicePolicyManager = IDevicePolicyManager.Stub.asInterface(
+                    ServiceManager.checkService(Context.DEVICE_POLICY_SERVICE));
+                if (mDevicePolicyManager == null) {
+                    Slog.w(TAG, "warning: no DEVICE_POLICY_SERVICE");
+                }
+            }
+            return mDevicePolicyManager;
+        }
+    }
+
     void setWindowManager(WindowManagerService wm) {
         synchronized (mService) {
             mWindowManager = wm;
@@ -2985,12 +3005,15 @@
     }
 
     void setLockTaskModeLocked(TaskRecord task) {
-        final Message lockTaskMsg = Message.obtain();
         if (task == null) {
-            // Take out of lock task mode.
-            mLockTaskModeTask = null;
-            lockTaskMsg.what = LOCK_TASK_END_MSG;
-            mHandler.sendMessage(lockTaskMsg);
+            // Take out of lock task mode if necessary
+            if (mLockTaskModeTask != null) {
+                final Message lockTaskMsg = Message.obtain();
+                lockTaskMsg.arg1 = mLockTaskModeTask.userId;
+                lockTaskMsg.what = LOCK_TASK_END_MSG;
+                mLockTaskModeTask = null;
+                mHandler.sendMessage(lockTaskMsg);
+            }
             return;
         }
         if (isLockTaskModeViolation(task)) {
@@ -3000,6 +3023,10 @@
         mLockTaskModeTask = task;
         findTaskToMoveToFrontLocked(task, 0, null);
         resumeTopActivitiesLocked();
+
+        final Message lockTaskMsg = Message.obtain();
+        lockTaskMsg.obj = mLockTaskModeTask.intent.getComponent().getPackageName();
+        lockTaskMsg.arg1 = mLockTaskModeTask.userId;
         lockTaskMsg.what = LOCK_TASK_START_MSG;
         mHandler.sendMessage(lockTaskMsg);
     }
@@ -3108,6 +3135,11 @@
                                 (StatusBarManager.DISABLE_MASK ^ StatusBarManager.DISABLE_BACK,
                                 mToken, mService.mContext.getPackageName());
                         }
+                        if (getDevicePolicyManager() != null) {
+                            getDevicePolicyManager().notifyLockTaskModeChanged(true,
+                                    (String)msg.obj,
+                                    msg.arg1);
+                        }
                     } catch (RemoteException ex) {
                         throw new RuntimeException(ex);
                     }
@@ -3120,6 +3152,10 @@
                                (StatusBarManager.DISABLE_NONE,
                                mToken, mService.mContext.getPackageName());
                        }
+                        if (getDevicePolicyManager() != null) {
+                            getDevicePolicyManager().notifyLockTaskModeChanged(false, null,
+                                    msg.arg1);
+                        }
                     } catch (RemoteException ex) {
                         throw new RuntimeException(ex);
                     }
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index eb253eb..f403d08 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -74,12 +74,19 @@
         mStats.setRadioScanningTimeout(mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_radioScanningTimeout)
                 * 1000L);
+    }
+
+    /**
+     * At the time when the constructor runs, the power manager has not yet been
+     * initialized.  So we initialize the low power observer later.
+     */
+    public void initPowerManagement() {
         mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
         mPowerManagerInternal.registerLowPowerModeObserver(this);
         mStats.noteLowPowerMode(mPowerManagerInternal.getLowPowerModeEnabled());
         (new WakeupReasonThread()).start();
-     }
-    
+    }
+
     public void shutdown() {
         Slog.w("BatteryStats", "Writing battery stats before shutdown...");
         synchronized (mStats) {
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index bb289fa..c79b33d 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -45,7 +45,7 @@
     static final boolean DEBUG = false;
 
     /** When in slow mode don't write tasks out faster than this */
-    private static final long INTER_TASK_DELAY_MS = 60000;
+    private static final long INTER_TASK_DELAY_MS = 10000;
     private static final long DEBUG_INTER_TASK_DELAY_MS = 5000;
 
     private static final String RECENTS_FILENAME = "_task";
@@ -69,6 +69,7 @@
     TaskPersister(File systemDir, ActivityStackSupervisor stackSupervisor) {
         sTasksDir = new File(systemDir, TASKS_DIRNAME);
         if (!sTasksDir.exists()) {
+            if (DEBUG) Slog.d(TAG, "Creating tasks directory " + sTasksDir);
             if (!sTasksDir.mkdir()) {
                 Slog.e(TAG, "Failure creating tasks directory " + sTasksDir);
             }
@@ -76,6 +77,7 @@
 
         sImagesDir = new File(systemDir, IMAGES_DIRNAME);
         if (!sImagesDir.exists()) {
+            if (DEBUG) Slog.d(TAG, "Creating images directory " + sTasksDir);
             if (!sImagesDir.mkdir()) {
                 Slog.e(TAG, "Failure creating images directory " + sImagesDir);
             }
@@ -172,14 +174,15 @@
                                     TaskRecord.restoreFromXml(in, mStackSupervisor);
                             if (DEBUG) Slog.d(TAG, "restoreTasksLocked: restored task=" + task);
                             if (task != null) {
+                                task.isPersistable = true;
                                 tasks.add(task);
                                 final int taskId = task.taskId;
                                 recoveredTaskIds.add(taskId);
                                 mStackSupervisor.setNextTaskId(taskId);
                             }
                         } else {
-                            Slog.e(TAG, "restoreTasksLocked Unknown xml event=" + event + " name="
-                                    + name);
+                            Slog.wtf(TAG, "restoreTasksLocked Unknown xml event=" + event +
+                                    " name=" + name);
                         }
                     }
                     XmlUtils.skipCurrentTag(in);
@@ -195,6 +198,7 @@
                     }
                 }
                 if (!DEBUG && deleteFile) {
+                    if (DEBUG) Slog.d(TAG, "Deleting file=" + taskFile.getName());
                     taskFile.delete();
                 }
             }
@@ -209,7 +213,7 @@
         Arrays.sort(tasksArray, new Comparator<TaskRecord>() {
             @Override
             public int compare(TaskRecord lhs, TaskRecord rhs) {
-                final long diff = lhs.mLastTimeMoved - rhs.mLastTimeMoved;
+                final long diff = rhs.mLastTimeMoved - lhs.mLastTimeMoved;
                 if (diff < 0) {
                     return -1;
                 } else if (diff > 0) {
@@ -233,8 +237,7 @@
                 try {
                     taskId = Integer.valueOf(filename.substring(0, taskIdEnd));
                 } catch (Exception e) {
-                    if (DEBUG) Slog.d(TAG, "removeObsoleteFile: Can't parse file=" +
-                            file.getName());
+                    Slog.wtf(TAG, "removeObsoleteFile: Can't parse file=" + file.getName());
                     file.delete();
                     continue;
                 }
@@ -288,15 +291,18 @@
                 synchronized(mService) {
                     final ArrayList<TaskRecord> tasks = mService.mRecentTasks;
                     persistentTaskIds.clear();
+                    if (DEBUG) Slog.d(TAG, "mRecents=" + tasks);
                     for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
                         task = tasks.get(taskNdx);
                         if (DEBUG) Slog.d(TAG, "LazyTaskWriter: task=" + task + " persistable=" +
                                 task.isPersistable + " needsPersisting=" + task.needsPersisting);
-                        if (task.isPersistable) {
+                        if (task.isPersistable && !task.stack.isHomeStack()) {
+                            if (DEBUG) Slog.d(TAG, "adding to persistentTaskIds task=" + task);
                             persistentTaskIds.add(task.taskId);
 
                             if (task.needsPersisting) {
                                 try {
+                                    if (DEBUG) Slog.d(TAG, "Saving task=" + task);
                                     stringWriter = saveToXml(task);
                                     break;
                                 } catch (IOException e) {
@@ -305,6 +311,8 @@
                                     task.needsPersisting = false;
                                 }
                             }
+                        } else {
+                            if (DEBUG) Slog.d(TAG, "omitting from persistentTaskIds task=" + task);
                         }
                     }
                 }
@@ -330,6 +338,8 @@
                     // Made it through the entire list and didn't find anything new that needed
                     // persisting.
                     if (!DEBUG) {
+                        if (DEBUG) Slog.d(TAG, "Calling removeObsoleteFiles persistentTaskIds=" +
+                                persistentTaskIds);
                         removeObsoleteFiles(persistentTaskIds);
                     }
 
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 81a0b36..1cde41f 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -652,8 +652,9 @@
         final int numActivities = activities.size();
         for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
             final ActivityRecord r = activities.get(activityNdx);
-            if (!r.isPersistable() || (activityNdx > 0 &&
-                    (r.intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0)) {
+            if (r.info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY || !r.isPersistable() ||
+                    ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) &&
+                            activityNdx > 0) {
                 // Stop at first non-persistable or first break in task (CLEAR_WHEN_TASK_RESET).
                 break;
             }
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index a77443d..c7f4f6a 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -119,7 +119,7 @@
     public int height;
 
     /**
-     * The refresh rate of the display.
+     * The refresh rate of the display, in frames per second.
      */
     public float refreshRate;
 
@@ -144,6 +144,20 @@
     public float yDpi;
 
     /**
+     * This is a positive value indicating the phase offset of the VSYNC events provided by
+     * Choreographer relative to the display refresh.  For example, if Choreographer reports
+     * that the refresh occurred at time N, it actually occurred at (N - appVsyncOffsetNanos).
+     */
+    public long appVsyncOffsetNanos;
+
+    /**
+     * This is how far in advance a buffer must be queued for presentation at
+     * a given time.  If you want a buffer to appear on the screen at
+     * time N, you must submit the buffer before (N - bufferDeadlineNanos).
+     */
+    public long presentationDeadlineNanos;
+
+    /**
      * Display flags.
      */
     public int flags;
@@ -219,6 +233,8 @@
                 && densityDpi == other.densityDpi
                 && xDpi == other.xDpi
                 && yDpi == other.yDpi
+                && appVsyncOffsetNanos == other.appVsyncOffsetNanos
+                && presentationDeadlineNanos == other.presentationDeadlineNanos
                 && flags == other.flags
                 && touch == other.touch
                 && rotation == other.rotation
@@ -242,6 +258,8 @@
         densityDpi = other.densityDpi;
         xDpi = other.xDpi;
         yDpi = other.yDpi;
+        appVsyncOffsetNanos = other.appVsyncOffsetNanos;
+        presentationDeadlineNanos = other.presentationDeadlineNanos;
         flags = other.flags;
         touch = other.touch;
         rotation = other.rotation;
@@ -261,6 +279,8 @@
         sb.append(", ").append(refreshRate).append(" fps, ");
         sb.append("density ").append(densityDpi);
         sb.append(", ").append(xDpi).append(" x ").append(yDpi).append(" dpi");
+        sb.append(", appVsyncOff ").append(appVsyncOffsetNanos);
+        sb.append(", presDeadline ").append(presentationDeadlineNanos);
         sb.append(", touch ").append(touchToString(touch));
         sb.append(", rotation ").append(rotation);
         sb.append(", type ").append(Display.typeToString(type));
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index e80aecd..098537c 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -161,6 +161,8 @@
                 mInfo.width = mPhys.width;
                 mInfo.height = mPhys.height;
                 mInfo.refreshRate = mPhys.refreshRate;
+                mInfo.appVsyncOffsetNanos = mPhys.appVsyncOffsetNanos;
+                mInfo.presentationDeadlineNanos = mPhys.presentationDeadlineNanos;
                 mInfo.state = mState;
 
                 // Assume that all built-in displays that have secure output (eg. HDCP) also
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index d61a35b..ed619d9 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -213,6 +213,8 @@
             mBaseDisplayInfo.logicalDensityDpi = deviceInfo.densityDpi;
             mBaseDisplayInfo.physicalXDpi = deviceInfo.xDpi;
             mBaseDisplayInfo.physicalYDpi = deviceInfo.yDpi;
+            mBaseDisplayInfo.appVsyncOffsetNanos = deviceInfo.appVsyncOffsetNanos;
+            mBaseDisplayInfo.presentationDeadlineNanos = deviceInfo.presentationDeadlineNanos;
             mBaseDisplayInfo.state = deviceInfo.state;
             mBaseDisplayInfo.smallestNominalAppWidth = deviceInfo.width;
             mBaseDisplayInfo.smallestNominalAppHeight = deviceInfo.height;
diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
index bfd8372c..3b23b6a 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -191,6 +191,7 @@
         private final int mWidth;
         private final int mHeight;
         private final float mRefreshRate;
+        private final long mDisplayPresentationDeadlineNanos;
         private final int mDensityDpi;
         private final boolean mSecure;
 
@@ -200,7 +201,7 @@
         private DisplayDeviceInfo mInfo;
 
         public OverlayDisplayDevice(IBinder displayToken, String name,
-                int width, int height, float refreshRate,
+                int width, int height, float refreshRate, long presentationDeadlineNanos,
                 int densityDpi, boolean secure, int state,
                 SurfaceTexture surfaceTexture) {
             super(OverlayDisplayAdapter.this, displayToken);
@@ -208,6 +209,7 @@
             mWidth = width;
             mHeight = height;
             mRefreshRate = refreshRate;
+            mDisplayPresentationDeadlineNanos = presentationDeadlineNanos;
             mDensityDpi = densityDpi;
             mSecure = secure;
             mState = state;
@@ -249,6 +251,8 @@
                 mInfo.densityDpi = mDensityDpi;
                 mInfo.xDpi = mDensityDpi;
                 mInfo.yDpi = mDensityDpi;
+                mInfo.presentationDeadlineNanos = mDisplayPresentationDeadlineNanos +
+                        1000000000L / (int) mRefreshRate;   // display's deadline + 1 frame
                 mInfo.flags = DisplayDeviceInfo.FLAG_PRESENTATION;
                 if (mSecure) {
                     mInfo.flags |= DisplayDeviceInfo.FLAG_SECURE;
@@ -297,12 +301,13 @@
 
         // Called on the UI thread.
         @Override
-        public void onWindowCreated(SurfaceTexture surfaceTexture, float refreshRate, int state) {
+        public void onWindowCreated(SurfaceTexture surfaceTexture, float refreshRate,
+                long presentationDeadlineNanos, int state) {
             synchronized (getSyncRoot()) {
                 IBinder displayToken = SurfaceControl.createDisplay(mName, mSecure);
                 mDevice = new OverlayDisplayDevice(displayToken, mName,
-                        mWidth, mHeight, refreshRate, mDensityDpi, mSecure,
-                        state, surfaceTexture);
+                        mWidth, mHeight, refreshRate, presentationDeadlineNanos,
+                        mDensityDpi, mSecure, state, surfaceTexture);
 
                 sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_ADDED);
             }
diff --git a/services/core/java/com/android/server/display/OverlayDisplayWindow.java b/services/core/java/com/android/server/display/OverlayDisplayWindow.java
index 06891f3..9ca5fda 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayWindow.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayWindow.java
@@ -303,7 +303,7 @@
         public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture,
                 int width, int height) {
             mListener.onWindowCreated(surfaceTexture, mDefaultDisplayInfo.refreshRate,
-                    mDefaultDisplayInfo.state);
+                    mDefaultDisplayInfo.presentationDeadlineNanos, mDefaultDisplayInfo.state);
         }
 
         @Override
@@ -373,7 +373,7 @@
      */
     public interface Listener {
         public void onWindowCreated(SurfaceTexture surfaceTexture,
-                float refreshRate, int state);
+                float refreshRate, long presentationDeadlineNanos, int state);
         public void onWindowDestroyed();
         public void onStateChanged(int state);
     }
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 14ef5a9..ec14cf1 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -171,6 +171,7 @@
                 mInfo.densityDpi = mDensityDpi;
                 mInfo.xDpi = mDensityDpi;
                 mInfo.yDpi = mDensityDpi;
+                mInfo.presentationDeadlineNanos = 1000000000L / (int) mInfo.refreshRate; // 1 frame
                 mInfo.flags = 0;
                 if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC) == 0) {
                     mInfo.flags |= DisplayDeviceInfo.FLAG_PRIVATE
diff --git a/services/core/java/com/android/server/display/WifiDisplayAdapter.java b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
index cd57941..a05bf2c 100644
--- a/services/core/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
@@ -127,7 +127,7 @@
         pw.println("mPendingStatusChangeBroadcast=" + mPendingStatusChangeBroadcast);
         pw.println("mPendingNotificationUpdate=" + mPendingNotificationUpdate);
         pw.println("mSupportsProtectedBuffers=" + mSupportsProtectedBuffers);
- 
+
         // Try to dump the controller state.
         if (mDisplayController == null) {
             pw.println("mDisplayController=null");
@@ -729,6 +729,7 @@
                 mInfo.width = mWidth;
                 mInfo.height = mHeight;
                 mInfo.refreshRate = mRefreshRate;
+                mInfo.presentationDeadlineNanos = 1000000000L / (int) mRefreshRate; // 1 frame
                 mInfo.flags = mFlags;
                 mInfo.type = Display.TYPE_WIFI;
                 mInfo.address = mAddress;
diff --git a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
index 23cf40b..74eaf2a 100644
--- a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
+++ b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
@@ -72,9 +72,9 @@
         }
 
         if (!mSource.isInPresetInstallationMode()) {
-            int prevActiveInput = mSource.getActiveInput();
+            int prevActiveInput = mSource.getActivePortId();
             mSource.updateActiveDevice(deviceLogicalAddress, routingPath);
-            if (prevActiveInput != mSource.getActiveInput()) {
+            if (prevActiveInput != mSource.getActivePortId()) {
                 // TODO: change port input here.
             }
             invokeCallback(HdmiCec.RESULT_SUCCESS);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 08d7786..f86d655 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -372,15 +372,28 @@
     }
 
     /**
-     * Returns the ID of the active HDMI port. The active input is the port that has the active
-     * routing path connected directly or indirectly under the device hierarchy.
+     * Returns the ID of the active HDMI port. The active port is the one that has the active
+     * routing path connected to it directly or indirectly under the device hierarchy.
      */
-    int getActiveInput() {
+    int getActivePortId() {
         synchronized (mLock) {
             return mService.pathToPortId(mActiveRoutingPath);
         }
     }
 
+    /**
+     * Update the active port.
+     *
+     * @param portId the new active port id
+     */
+    void setActivePortId(int portId) {
+        synchronized (mLock) {
+            // We update active routing path instead, since we get the active port id from
+            // the active routing path.
+            mActiveRoutingPath = mService.portIdToPath(portId);
+        }
+    }
+
     void updateActiveDevice(int logicalAddress, int physicalAddress) {
         synchronized (mLock) {
             mActiveSource = logicalAddress;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 92ddd3d..353f603 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -52,6 +52,8 @@
 
     HdmiCecLocalDeviceTv(HdmiControlService service) {
         super(service, HdmiCec.DEVICE_TV);
+
+        // TODO: load system audio mode and set it to mSystemAudioMode.
     }
 
     @Override
@@ -85,6 +87,60 @@
         addAndStartAction(new DeviceSelectAction(this, targetDevice, callback));
     }
 
+    /**
+     * Performs the action routing control.
+     *
+     * @param portId new HDMI port to route to
+     * @param callback callback object to report the result with
+     */
+    void portSelect(int portId, IHdmiControlCallback callback) {
+        assertRunOnServiceThread();
+        if (isInPresetInstallationMode()) {
+            invokeCallback(callback, HdmiCec.RESULT_INCORRECT_MODE);
+            return;
+        }
+        // Make sure this call does not stem from <Active Source> message reception, in
+        // which case the two ports will be the same.
+        if (portId == getActivePortId()) {
+            invokeCallback(callback, HdmiCec.RESULT_SUCCESS);
+            return;
+        }
+        setActivePortId(portId);
+
+        // TODO: Return immediately if the operation is triggered by <Text/Image View On>
+        // TODO: Handle invalid port id / active input which should be treated as an
+        //        internal tuner.
+
+        removeAction(RoutingControlAction.class);
+
+        int oldPath = mService.portIdToPath(mService.portIdToPath(getActivePortId()));
+        int newPath = mService.portIdToPath(portId);
+        HdmiCecMessage routingChange =
+                HdmiCecMessageBuilder.buildRoutingChange(mAddress, oldPath, newPath);
+        mService.sendCecCommand(routingChange);
+        addAndStartAction(new RoutingControlAction(this, newPath, callback));
+    }
+
+    /**
+     * Sends key to a target CEC device.
+     *
+     * @param keyCode key code to send. Defined in {@link KeyEvent}.
+     * @param isPressed true if this is keypress event
+     */
+    void sendKeyEvent(int keyCode, boolean isPressed) {
+        assertRunOnServiceThread();
+        List<SendKeyAction> action = getActions(SendKeyAction.class);
+        if (!action.isEmpty()) {
+            action.get(0).processKeyEvent(keyCode, isPressed);
+        } else {
+            if (isPressed) {
+                addAndStartAction(new SendKeyAction(this, getActiveSource(), keyCode));
+            } else {
+                Slog.w(TAG, "Discard key release event");
+            }
+        }
+    }
+
     private static void invokeCallback(IHdmiControlCallback callback, int result) {
         try {
             callback.onComplete(result);
@@ -174,6 +230,15 @@
                         }
 
                         addAndStartAction(new HotplugDetectionAction(HdmiCecLocalDeviceTv.this));
+
+                        // If there is AVR, initiate System Audio Auto initiation action,
+                        // which turns on and off system audio according to last system
+                        // audio setting.
+                        HdmiCecDeviceInfo avrInfo = getAvrDeviceInfo();
+                        if (avrInfo != null) {
+                            addAndStartAction(new SystemAudioAutoInitiationAction(
+                                    HdmiCecLocalDeviceTv.this, avrInfo.getLogicalAddress()));
+                        }
                     }
                 });
         addAndStartAction(action);
@@ -456,4 +521,10 @@
             hotplugActions.get(0).pollAllDevicesNow();
         }
     }
+
+    boolean canChangeSystemAudio() {
+        // TODO: implement this.
+        // return true if no system audio control sequence is running.
+        return false;
+    }
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
index 8dbfd85..361a063 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
@@ -377,6 +377,17 @@
         return buildCommand(src, dest, HdmiCec.MESSAGE_USER_CONTROL_RELEASED);
     }
 
+    /**
+     * Build &lt;Give System Audio Mode Status&gt; command.
+     *
+     * @param src source address of command
+     * @param dest destination address of command
+     * @return newly created {@link HdmiCecMessage}
+     */
+    static HdmiCecMessage buildGiveSystemAudioModeStatus(int src, int dest) {
+        return buildCommand(src, dest, HdmiCec.MESSAGE_GIVE_SYSTEM_AUDIO_MODE_STATUS);
+    }
+
     /***** Please ADD new buildXXX() methods above. ******/
 
     /**
diff --git a/services/core/java/com/android/server/hdmi/HdmiConstants.java b/services/core/java/com/android/server/hdmi/HdmiConstants.java
index 9c60e55..5294506 100644
--- a/services/core/java/com/android/server/hdmi/HdmiConstants.java
+++ b/services/core/java/com/android/server/hdmi/HdmiConstants.java
@@ -95,5 +95,7 @@
     static final int POLL_ITERATION_IN_ORDER = 0x10000;
     static final int POLL_ITERATION_REVERSE_ORDER = 0x20000;
 
+    static final int UNKNOWN_VOLUME = -1;
+
     private HdmiConstants() { /* cannot be instantiated */ }
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 10da756..fddb833 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -507,7 +507,7 @@
                 public void run() {
                     HdmiCecLocalDeviceTv tv = tv();
                     if (tv == null) {
-                        Slog.w(TAG, "Local playback device not available");
+                        Slog.w(TAG, "Local tv device not available");
                         invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
                         return;
                     }
@@ -517,6 +517,41 @@
         }
 
         @Override
+        public void portSelect(final int portId, final IHdmiControlCallback callback) {
+            enforceAccessPermission();
+            runOnServiceThread(new Runnable() {
+                @Override
+                public void run() {
+                    HdmiCecLocalDeviceTv tv = tv();
+                    if (tv == null) {
+                        Slog.w(TAG, "Local tv device not available");
+                        invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
+                        return;
+                    }
+                    tv.portSelect(portId, callback);
+                }
+            });
+        }
+
+        @Override
+        public void sendKeyEvent(final int keyCode, final boolean isPressed) {
+            enforceAccessPermission();
+            runOnServiceThread(new Runnable() {
+                @Override
+                public void run() {
+                    // TODO: sendKeyEvent is for TV device only for now. Allow other
+                    //       local devices of different types to use this as well.
+                    HdmiCecLocalDeviceTv tv = tv();
+                    if (tv == null) {
+                        Slog.w(TAG, "Local tv device not available");
+                        return;
+                    }
+                    tv.sendKeyEvent(keyCode, isPressed);
+                }
+            });
+        }
+
+        @Override
         public void oneTouchPlay(final IHdmiControlCallback callback) {
             enforceAccessPermission();
             runOnServiceThread(new Runnable() {
@@ -572,16 +607,6 @@
         }
 
         @Override
-        public void portSelect(int portId, IHdmiControlCallback callback) {
-            // TODO: Implement this
-        }
-
-        @Override
-        public void sendKeyEvent(int keyCode, boolean isPressed) {
-            // TODO: Implement this
-        }
-
-        @Override
         public List<HdmiPortInfo> getPortInfo() {
             enforceAccessPermission();
             return mPortInfo;
diff --git a/services/core/java/com/android/server/hdmi/RoutingControlAction.java b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
index 2eec846..0d657b2 100644
--- a/services/core/java/com/android/server/hdmi/RoutingControlAction.java
+++ b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
@@ -196,7 +196,7 @@
 
     // Called whenever an HDMI input of the TV shall become the active input.
     private boolean maybeChangeActiveInput(int path) {
-        if (localDevice().getActiveInput() == localDevice().pathToPortId(path)) {
+        if (localDevice().getActivePortId() == localDevice().pathToPortId(path)) {
             return false;
         }
         // TODO: Remember the currently active input
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java
new file mode 100644
index 0000000..e4d82ef
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java
@@ -0,0 +1,120 @@
+/*
+ * 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 com.android.server.hdmi;
+
+import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecMessage;
+
+import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
+
+/**
+ * Action to initiate system audio once AVR is detected on Device discovery action.
+ */
+final class SystemAudioAutoInitiationAction extends FeatureAction {
+    private final int mAvrAddress;
+
+    // State that waits for <System Audio Mode Status> once send
+    // <Give System Audio Mode Status> to AV Receiver.
+    private static final int STATE_WAITING_FOR_SYSTEM_AUDIO_MODE_STATUS = 1;
+
+    SystemAudioAutoInitiationAction(HdmiCecLocalDevice source, int avrAddress) {
+        super(source);
+        mAvrAddress = avrAddress;
+    }
+
+    @Override
+    boolean start() {
+        mState = STATE_WAITING_FOR_SYSTEM_AUDIO_MODE_STATUS;
+
+        addTimer(mState, TIMEOUT_MS);
+        sendGiveSystemAudioModeStatus();
+        return true;
+    }
+
+    private void sendGiveSystemAudioModeStatus() {
+        sendCommand(HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(getSourceAddress(),
+                mAvrAddress), new SendMessageCallback() {
+            @Override
+            public void onSendCompleted(int error) {
+                if (error != HdmiConstants.SEND_RESULT_SUCCESS) {
+                    tv().setSystemAudioMode(false);
+                    finish();
+                }
+            }
+        });
+    }
+
+    @Override
+    boolean processCommand(HdmiCecMessage cmd) {
+        if (mState != STATE_WAITING_FOR_SYSTEM_AUDIO_MODE_STATUS) {
+            return false;
+        }
+
+        switch (cmd.getOpcode()) {
+            case HdmiCec.MESSAGE_SYSTEM_AUDIO_MODE_STATUS:
+                handleSystemAudioModeStatusMessage();
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    private void handleSystemAudioModeStatusMessage() {
+        // If the last setting is system audio, turn on system audio whatever AVR status is.
+        if (tv().getSystemAudioMode()) {
+            if (canChangeSystemAudio()) {
+                addAndStartAction(new SystemAudioActionFromTv(tv(), mAvrAddress, true));
+            }
+        } else {
+            // If the last setting is non-system audio, turn off system audio mode
+            // and update system audio status (volume or mute).
+            tv().setSystemAudioMode(false);
+            if (canChangeSystemAudio()) {
+                addAndStartAction(new SystemAudioStatusAction(tv(), mAvrAddress));
+            }
+        }
+        finish();
+    }
+
+    @Override
+    void handleTimerEvent(int state) {
+        if (mState != state) {
+            return;
+        }
+
+        switch (mState) {
+            case STATE_WAITING_FOR_SYSTEM_AUDIO_MODE_STATUS:
+                handleSystemAudioModeStatusTimeout();
+                break;
+        }
+    }
+
+    private void handleSystemAudioModeStatusTimeout() {
+        if (tv().getSystemAudioMode()) {
+            if (canChangeSystemAudio()) {
+                addAndStartAction(new SystemAudioActionFromTv(tv(), mAvrAddress, true));
+            }
+        } else {
+            tv().setSystemAudioMode(false);
+        }
+        finish();
+    }
+
+    private boolean canChangeSystemAudio() {
+        return tv().canChangeSystemAudio();
+    }
+}
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java b/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java
new file mode 100644
index 0000000..75e4fef
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java
@@ -0,0 +1,123 @@
+/*
+ * 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 com.android.server.hdmi;
+
+import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecMessage;
+import android.util.Slog;
+
+import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
+
+/**
+ * Action to update audio status (volume or mute) of audio amplifier
+ */
+// TODO: refactor SystemAudioMode so that it uses this class instead of internal state.
+final class SystemAudioStatusAction extends FeatureAction {
+    private static final String TAG = "SystemAudioStatusAction";
+
+    // State that waits for <ReportAudioStatus>.
+    private static final int STATE_WAIT_FOR_REPORT_AUDIO_STATUS = 1;
+
+    private final int mAvrAddress;
+
+    SystemAudioStatusAction(HdmiCecLocalDevice source, int avrAddress) {
+        super(source);
+        mAvrAddress = avrAddress;
+    }
+
+    @Override
+    boolean start() {
+        mState = STATE_WAIT_FOR_REPORT_AUDIO_STATUS;
+        addTimer(mState, TIMEOUT_MS);
+        sendGiveAudioStatus();
+        return true;
+    }
+
+    private void sendGiveAudioStatus() {
+        sendCommand(HdmiCecMessageBuilder.buildGiveAudioStatus(getSourceAddress(), mAvrAddress),
+                new SendMessageCallback() {
+            @Override
+            public void onSendCompleted(int error) {
+                if (error != HdmiConstants.SEND_RESULT_SUCCESS) {
+                    handleSendGiveAudioStatusFailure();
+                }
+            }
+        });
+    }
+
+    private void handleSendGiveAudioStatusFailure() {
+        // Inform to all application that the audio status (volumn, mute) of
+        // the audio amplifier is unknown.
+        tv().setAudioStatus(false, HdmiConstants.UNKNOWN_VOLUME);
+
+        int uiCommand = tv().getSystemAudioMode()
+                ? HdmiConstants.UI_COMMAND_RESTORE_VOLUME_FUNCTION  // SystemAudioMode: ON
+                : HdmiConstants.UI_COMMAND_MUTE_FUNCTION;           // SystemAudioMode: OFF
+        sendUserControlPressedAndReleased(uiCommand);
+        finish();
+    }
+
+    private void sendUserControlPressedAndReleased(int uiCommand) {
+        sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(
+                getSourceAddress(), mAvrAddress, uiCommand));
+        sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(
+                getSourceAddress(), mAvrAddress));
+    }
+
+    @Override
+    boolean processCommand(HdmiCecMessage cmd) {
+        if (mState != STATE_WAIT_FOR_REPORT_AUDIO_STATUS) {
+            return false;
+        }
+
+        switch (cmd.getOpcode()) {
+            case HdmiCec.MESSAGE_REPORT_AUDIO_STATUS:
+                handleReportAudioStatus(cmd);
+                return true;
+        }
+
+        return false;
+    }
+
+    private void handleReportAudioStatus(HdmiCecMessage cmd) {
+        byte[] params = cmd.getParams();
+        if (params.length > 0) {
+            boolean mute = (params[0] & 0x80) == 0x80;
+            int volume = params[0] & 0x7F;
+            tv().setAudioStatus(mute, volume);
+
+            if ((tv().getSystemAudioMode() && mute) || (!tv().getSystemAudioMode() && !mute)) {
+                // Toggle AVR's mute status to match with the system audio status.
+                sendUserControlPressedAndReleased(HdmiConstants.UI_COMMAND_MUTE);
+            }
+            finish();
+        } else {
+            Slog.e(TAG, "Invalid <Report Audio Status> message:" + cmd);
+            handleSendGiveAudioStatusFailure();
+            return;
+        }
+    }
+
+    @Override
+    void handleTimerEvent(int state) {
+        if (mState != state) {
+            return;
+        }
+
+        handleSendGiveAudioStatusFailure();
+    }
+}
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 0e9a9cc..cab2728 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -22,6 +22,7 @@
 import java.util.Iterator;
 import java.util.List;
 
+import android.app.AppGlobals;
 import android.app.job.JobInfo;
 import android.app.job.JobScheduler;
 import android.app.job.JobService;
@@ -31,8 +32,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ServiceInfo;
 import android.os.Binder;
 import android.os.Handler;
@@ -616,10 +617,13 @@
         // job that runs one of the app's services, as well as verifying that the
         // named service properly requires the BIND_JOB_SERVICE permission
         private void enforceValidJobRequest(int uid, JobInfo job) {
-            final PackageManager pm = getContext().getPackageManager();
+            final IPackageManager pm = AppGlobals.getPackageManager();
             final ComponentName service = job.getService();
             try {
-                ServiceInfo si = pm.getServiceInfo(service, 0);
+                ServiceInfo si = pm.getServiceInfo(service, 0, UserHandle.getUserId(uid));
+                if (si == null) {
+                    throw new IllegalArgumentException("No such service " + service);
+                }
                 if (si.applicationInfo.uid != uid) {
                     throw new IllegalArgumentException("uid " + uid +
                             " cannot schedule job in " + service.getPackageName());
@@ -628,8 +632,8 @@
                     throw new IllegalArgumentException("Scheduled service " + service
                             + " does not require android.permission.BIND_JOB_SERVICE permission");
                 }
-            } catch (NameNotFoundException e) {
-                throw new IllegalArgumentException("No such service: " + service);
+            } catch (RemoteException e) {
+                // Can't happen; the Package Manager is in this same process
             }
         }
 
diff --git a/services/core/java/com/android/server/job/controllers/BatteryController.java b/services/core/java/com/android/server/job/controllers/BatteryController.java
index 4aef2d31..538a252 100644
--- a/services/core/java/com/android/server/job/controllers/BatteryController.java
+++ b/services/core/java/com/android/server/job/controllers/BatteryController.java
@@ -19,19 +19,16 @@
 import android.app.AlarmManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.BatteryManager;
-import android.os.BatteryProperty;
-import android.os.RemoteException;
-import android.os.ServiceManager;
+import android.os.BatteryManagerInternal;
 import android.os.SystemClock;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.BatteryService;
+import com.android.server.LocalServices;
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.StateChangedListener;
 
@@ -158,14 +155,10 @@
             mContext.registerReceiver(this, filter);
 
             // Initialise tracker state.
-            BatteryService batteryService = (BatteryService) ServiceManager.getService("battery");
-            if (batteryService != null) {
-                mBatteryHealthy = !batteryService.getBatteryLevelLow();
-                mCharging = batteryService.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
-            } else {
-                // Unavailable for some reason, we default to false and let ACTION_BATTERY_[OK,LOW]
-                // sort it out.
-            }
+            BatteryManagerInternal batteryManagerInternal =
+                    LocalServices.getService(BatteryManagerInternal.class);
+            mBatteryHealthy = !batteryManagerInternal.getBatteryLevelLow();
+            mCharging = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
         }
 
         boolean isOnStablePower() {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index dd33771..89ab2ae 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -29,6 +29,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageParser.ApkLite;
+import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.Signature;
 import android.os.Build;
 import android.os.Bundle;
@@ -50,14 +51,10 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
 
-import libcore.io.IoUtils;
 import libcore.io.Libcore;
-import libcore.io.Streams;
 
 import java.io.File;
 import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
 
@@ -297,11 +294,12 @@
 
         // Verify that all staged packages are internally consistent
         for (File file : files) {
-            final ApkLite info = PackageParser.parseApkLite(file.getAbsolutePath(),
-                    PackageParser.PARSE_GET_SIGNATURES);
-            if (info == null) {
+            final ApkLite info;
+            try {
+                info = PackageParser.parseApkLite(file, PackageParser.PARSE_GET_SIGNATURES);
+            } catch (PackageParserException e) {
                 throw new InstallFailedException(INSTALL_FAILED_INVALID_APK,
-                        "Failed to parse " + file);
+                        "Failed to parse " + file + ": " + e);
             }
 
             if (!seenSplits.add(info.splitName)) {
@@ -356,11 +354,13 @@
                         "Missing existing base package for " + mPackageName);
             }
 
-            final ApkLite info = PackageParser.parseApkLite(app.sourceDir,
-                    PackageParser.PARSE_GET_SIGNATURES);
-            if (info == null) {
+            final ApkLite info;
+            try {
+                info = PackageParser.parseApkLite(new File(app.sourceDir),
+                        PackageParser.PARSE_GET_SIGNATURES);
+            } catch (PackageParserException e) {
                 throw new InstallFailedException(INSTALL_FAILED_INVALID_APK,
-                        "Failed to parse existing base " + app.sourceDir);
+                        "Failed to parse existing base " + app.sourceDir + ": " + e);
             }
 
             assertPackageConsistent("Existing base", info.packageName, info.versionCode,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 5feee50..2f40f2a 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1248,7 +1248,7 @@
         }
     }
 
-    public static final IPackageManager main(Context context, Installer installer,
+    public static final PackageManagerService main(Context context, Installer installer,
             boolean factoryTest, boolean onlyCore) {
         PackageManagerService m = new PackageManagerService(context, installer,
                 factoryTest, onlyCore);
@@ -4199,14 +4199,18 @@
         String scanPath = scanFile.getPath();
         if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanPath);
         parseFlags |= mDefParseFlags;
-        PackageParser pp = new PackageParser(scanPath);
+        PackageParser pp = new PackageParser();
         pp.setSeparateProcesses(mSeparateProcesses);
         pp.setOnlyCoreApps(mOnlyCore);
+        pp.setDisplayMetrics(mMetrics);
+
+        if ((scanMode & SCAN_TRUSTED_OVERLAY) != 0) {
+            parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY;
+        }
 
         final PackageParser.Package pkg;
         try {
-            pkg = pp.parseMonolithicPackage(scanFile, mMetrics, parseFlags,
-                (scanMode & SCAN_TRUSTED_OVERLAY) != 0);
+            pkg = pp.parseMonolithicPackage(scanFile, parseFlags);
         } catch (PackageParserException e) {
             mLastScanError = e.error;
             return null;
@@ -4637,12 +4641,7 @@
         }
 
         if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) {
-            final ArrayList<String> paths = new ArrayList<>();
-            paths.add(pkg.codePath);
-            if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
-                Collections.addAll(paths, pkg.splitCodePaths);
-            }
-
+            final Collection<String> paths = pkg.getAllCodePaths();
             for (String path : paths) {
                 try {
                     boolean isDexOptNeededInternal = DexFile.isDexOptNeededInternal(path,
@@ -4832,10 +4831,7 @@
             }
         }
         if (p != null) {
-            usesLibraryFiles.add(p.codePath);
-            if (!ArrayUtils.isEmpty(p.splitCodePaths)) {
-                Collections.addAll(usesLibraryFiles, p.splitCodePaths);
-            }
+            usesLibraryFiles.addAll(p.getAllCodePaths());
         }
     }
 
@@ -5674,7 +5670,8 @@
             try {
                 ksm.addSigningKeySetToPackage(pkg.packageName, pkg.mSigningKeys);
                 if (pkg.mKeySetMapping != null) {
-                    for (Map.Entry<String, Set<PublicKey>> entry : pkg.mKeySetMapping.entrySet()) {
+                    for (Map.Entry<String, ArraySet<PublicKey>> entry :
+                            pkg.mKeySetMapping.entrySet()) {
                         if (entry.getValue() != null) {
                             ksm.addDefinedKeySetToPackage(pkg.packageName,
                                 entry.getValue(), entry.getKey());
@@ -9713,7 +9710,7 @@
 
             return PackageManager.INSTALL_SUCCEEDED;
         }
-    };
+    }
 
     static String getAsecPackageName(String packageCid) {
         int idx = packageCid.lastIndexOf("-");
@@ -10206,13 +10203,13 @@
         int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
                 | (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)
                 | (onSd ? PackageParser.PARSE_ON_SDCARD : 0);
-        PackageParser pp = new PackageParser(tmpPackageFile.getPath());
+        PackageParser pp = new PackageParser();
         pp.setSeparateProcesses(mSeparateProcesses);
+        pp.setDisplayMetrics(mMetrics);
 
         final PackageParser.Package pkg;
         try {
-            pkg = pp.parseMonolithicPackage(tmpPackageFile, mMetrics,
-                parseFlags);
+            pkg = pp.parseMonolithicPackage(tmpPackageFile, parseFlags);
         } catch (PackageParserException e) {
             res.returnCode = e.error;
             return;
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index b941657..1839259 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -744,7 +744,7 @@
                 writeBoolean(serializer, restrictions, UserManager.ENSURE_VERIFY_APPS);
                 writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_CELL_BROADCASTS);
                 writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
-                writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_APPS);
+                writeBoolean(serializer, restrictions, UserManager.DISALLOW_APPS_CONTROL);
                 writeBoolean(serializer, restrictions, UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA);
                 writeBoolean(serializer, restrictions, UserManager.DISALLOW_UNMUTE_MICROPHONE);
                 writeBoolean(serializer, restrictions, UserManager.DISALLOW_ADJUST_VOLUME);
@@ -896,7 +896,7 @@
                         readBoolean(parser, restrictions, UserManager.ENSURE_VERIFY_APPS);
                         readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_CELL_BROADCASTS);
                         readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
-                        readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_APPS);
+                        readBoolean(parser, restrictions, UserManager.DISALLOW_APPS_CONTROL);
                         readBoolean(parser, restrictions,
                                 UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA);
                         readBoolean(parser, restrictions, UserManager.DISALLOW_UNMUTE_MICROPHONE);
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index fb4b8f0..bd80b54 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -19,10 +19,10 @@
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.os.BackgroundThread;
-import com.android.server.BatteryService;
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
+import com.android.server.am.BatteryStatsService;
 import com.android.server.lights.Light;
 import com.android.server.lights.LightsManager;
 import com.android.server.Watchdog;
@@ -42,6 +42,7 @@
 import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
 import android.net.Uri;
 import android.os.BatteryManager;
+import android.os.BatteryManagerInternal;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -163,13 +164,14 @@
     private static final int POWER_HINT_LOW_POWER_MODE = 5;
 
     private final Context mContext;
+    private final ServiceThread mHandlerThread;
+    private final PowerManagerHandler mHandler;
+
     private LightsManager mLightsManager;
-    private BatteryService mBatteryService;
+    private BatteryManagerInternal mBatteryManagerInternal;
     private DisplayManagerInternal mDisplayManagerInternal;
     private IBatteryStats mBatteryStats;
     private IAppOpsService mAppOps;
-    private ServiceThread mHandlerThread;
-    private PowerManagerHandler mHandler;
     private WindowManagerPolicy mPolicy;
     private Notifier mNotifier;
     private WirelessChargerDetector mWirelessChargerDetector;
@@ -429,6 +431,11 @@
     public PowerManagerService(Context context) {
         super(context);
         mContext = context;
+        mHandlerThread = new ServiceThread(TAG,
+                Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
+        mHandlerThread.start();
+        mHandler = new PowerManagerHandler(mHandlerThread.getLooper());
+
         synchronized (mLock) {
             mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService.WakeLocks");
             mDisplaySuspendBlocker = createSuspendBlockerLocked("PowerManagerService.Display");
@@ -451,39 +458,19 @@
     public void onStart() {
         publishBinderService(Context.POWER_SERVICE, new BinderService());
         publishLocalService(PowerManagerInternal.class, new LocalService());
-    }
-
-    /**
-     * Initialize the power manager.
-     * Must be called before any other functions within the power manager are called.
-     */
-    public void init(LightsManager ls,
-            BatteryService bs, IBatteryStats bss,
-            IAppOpsService appOps) {
-        mLightsManager = ls;
-        mBatteryService = bs;
-        mBatteryStats = bss;
-        mAppOps = appOps;
-        mDisplayManagerInternal = getLocalService(DisplayManagerInternal.class);
-        mHandlerThread = new ServiceThread(TAG,
-                Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
-        mHandlerThread.start();
-        mHandler = new PowerManagerHandler(mHandlerThread.getLooper());
 
         Watchdog.getInstance().addMonitor(this);
         Watchdog.getInstance().addThread(mHandler);
     }
 
-    void setPolicy(WindowManagerPolicy policy) {
-        synchronized (mLock) {
-            mPolicy = policy;
-        }
-    }
-
-    public void systemReady() {
+    public void systemReady(IAppOpsService appOps) {
         synchronized (mLock) {
             mSystemReady = true;
-            mDreamManager = LocalServices.getService(DreamManagerInternal.class);
+            mAppOps = appOps;
+            mDreamManager = getLocalService(DreamManagerInternal.class);
+            mDisplayManagerInternal = getLocalService(DisplayManagerInternal.class);
+            mPolicy = getLocalService(WindowManagerPolicy.class);
+            mBatteryManagerInternal = getLocalService(BatteryManagerInternal.class);
 
             PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
             mScreenBrightnessSettingMinimum = pm.getMinimumScreenBrightnessSetting();
@@ -494,6 +481,7 @@
 
             // The notifier runs on the system server's main looper so as not to interfere
             // with the animations and other critical functions of the power manager.
+            mBatteryStats = BatteryStatsService.getService();
             mNotifier = new Notifier(Looper.getMainLooper(), mContext, mBatteryStats,
                     mAppOps, createSuspendBlockerLocked("PowerManagerService.Broadcasts"),
                     mScreenOnBlocker, mPolicy);
@@ -502,6 +490,8 @@
                     createSuspendBlockerLocked("PowerManagerService.WirelessChargerDetector"),
                     mHandler);
             mSettingsObserver = new SettingsObserver(mHandler);
+
+            mLightsManager = getLocalService(LightsManager.class);
             mAttentionLight = mLightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION);
 
             // Initialize display power management.
@@ -1168,10 +1158,10 @@
             final boolean wasPowered = mIsPowered;
             final int oldPlugType = mPlugType;
             final boolean oldLevelLow = mBatteryLevelLow;
-            mIsPowered = mBatteryService.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
-            mPlugType = mBatteryService.getPlugType();
-            mBatteryLevel = mBatteryService.getBatteryLevel();
-            mBatteryLevelLow = mBatteryService.getBatteryLevelLow();
+            mIsPowered = mBatteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
+            mPlugType = mBatteryManagerInternal.getPlugType();
+            mBatteryLevel = mBatteryManagerInternal.getBatteryLevel();
+            mBatteryLevelLow = mBatteryManagerInternal.getBatteryLevelLow();
 
             if (DEBUG_SPEW) {
                 Slog.d(TAG, "updateIsPoweredLocked: wasPowered=" + wasPowered
@@ -1254,7 +1244,7 @@
             final boolean wasStayOn = mStayOn;
             if (mStayOnWhilePluggedInSetting != 0
                     && !isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked()) {
-                mStayOn = mBatteryService.isPowered(mStayOnWhilePluggedInSetting);
+                mStayOn = mBatteryManagerInternal.isPowered(mStayOnWhilePluggedInSetting);
             } else {
                 mStayOn = false;
             }
@@ -3076,10 +3066,5 @@
                 mLowPowerModeListeners.add(listener);
             }
         }
-
-        @Override
-        public void setPolicy(WindowManagerPolicy policy) {
-            PowerManagerService.this.setPolicy(policy);
-        }
     }
 }
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index e34f42b..d72ed9e 100644
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -116,6 +116,19 @@
         }
     }
 
+    private boolean checkUidChangedLocked(
+            Connection connection, int callingUid, int resolvedUserId) {
+        Integer connectionCallingUid = connection.getCallingUidLocked();
+        Integer connectionResolvedUserId = connection.getResolvedUserIdLocked();
+        if (connectionCallingUid == null || connectionResolvedUserId == null) {
+            return true;
+        }
+        if (connectionCallingUid != callingUid || connectionResolvedUserId != resolvedUserId) {
+            return true;
+        }
+        return false;
+    }
+
     /**
      * Create a TvInputHardware object with a specific deviceId. One service at a time can access
      * the object, and if more than one process attempts to create hardware with the same deviceId,
@@ -133,8 +146,7 @@
                 Slog.e(TAG, "Invalid deviceId : " + deviceId);
                 return null;
             }
-            if (connection.getCallingUidLocked() != callingUid
-                    || connection.getResolvedUserIdLocked() != resolvedUserId) {
+            if (checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
                 TvInputHardwareImpl hardware = new TvInputHardwareImpl(connection.getInfoLocked());
                 try {
                     callback.asBinder().linkToDeath(connection, 0);
@@ -160,8 +172,7 @@
                 return;
             }
             if (connection.getHardwareLocked() != hardware
-                    || connection.getCallingUidLocked() != callingUid
-                    || connection.getResolvedUserIdLocked() != resolvedUserId) {
+                    || checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
                 return;
             }
             connection.resetLocked(null, null, null, null);
@@ -226,11 +237,11 @@
             return mConfigs;
         }
 
-        public int getCallingUidLocked() {
+        public Integer getCallingUidLocked() {
             return mCallingUid;
         }
 
-        public int getResolvedUserIdLocked() {
+        public Integer getResolvedUserIdLocked() {
             return mResolvedUserId;
         }
 
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 10a67c4..cbbcc58 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -19,12 +19,15 @@
 import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.ContentProviderOperation;
+import android.content.ContentProviderResult;
 import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.OperationApplicationException;
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
@@ -66,11 +69,12 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
-
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /** This class provides a system service that manages television inputs. */
 public final class TvInputManagerService extends SystemService {
@@ -123,6 +127,44 @@
                     buildTvInputListLocked(mCurrentUserId);
                 }
             }
+
+            @Override
+            public void onPackageRemoved(String packageName, int uid) {
+                synchronized (mLock) {
+                    UserState userState = getUserStateLocked(mCurrentUserId);
+                    if (!userState.packageList.contains(packageName)) {
+                        // Not a TV input package.
+                        return;
+                    }
+                }
+
+                ArrayList<ContentProviderOperation> operations =
+                        new ArrayList<ContentProviderOperation>();
+
+                String selection = TvContract.BaseTvColumns.COLUMN_PACKAGE_NAME + "=?";
+                String[] selectionArgs = { packageName };
+
+                operations.add(ContentProviderOperation.newDelete(TvContract.Channels.CONTENT_URI)
+                        .withSelection(selection, selectionArgs).build());
+                operations.add(ContentProviderOperation.newDelete(TvContract.Programs.CONTENT_URI)
+                        .withSelection(selection, selectionArgs).build());
+                operations.add(ContentProviderOperation
+                        .newDelete(TvContract.WatchedPrograms.CONTENT_URI)
+                        .withSelection(selection, selectionArgs).build());
+
+                ContentProviderResult[] results = null;
+                try {
+                    results = mContentResolver.applyBatch(TvContract.AUTHORITY, operations);
+                } catch (RemoteException | OperationApplicationException e) {
+                    Slog.e(TAG, "error in applyBatch" + e);
+                }
+
+                if (DEBUG) {
+                    Slog.d(TAG, "onPackageRemoved(packageName=" + packageName + ", uid=" + uid
+                            + ")");
+                    Slog.d(TAG, "results=" + results);
+                }
+            }
         };
         monitor.register(mContext, null, UserHandle.ALL, true);
 
@@ -145,6 +187,7 @@
     private void buildTvInputListLocked(int userId) {
         UserState userState = getUserStateLocked(userId);
         userState.inputMap.clear();
+        userState.packageList.clear();
 
         if (DEBUG) Slog.d(TAG, "buildTvInputList");
         PackageManager pm = mContext.getPackageManager();
@@ -162,6 +205,7 @@
                 TvInputInfo info = TvInputInfo.createTvInputInfo(mContext, ri);
                 if (DEBUG) Slog.d(TAG, "add " + info.getId());
                 userState.inputMap.put(info.getId(), info);
+                userState.packageList.add(si.packageName);
             } catch (IOException | XmlPullParserException e) {
                 Slog.e(TAG, "Can't load TV input " + si.name, e);
             }
@@ -348,7 +392,7 @@
                     if (session == null) {
                         removeSessionStateLocked(sessionToken, userId);
                         sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId,
-                                null, null, sessionState.mSeq, userId);
+                                null, null, sessionState.mSeq);
                     } else {
                         try {
                             session.asBinder().linkToDeath(sessionState, 0);
@@ -364,7 +408,7 @@
                         clientState.mSessionTokens.add(sessionState.mSessionToken);
 
                         sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId,
-                                sessionToken, channels[0], sessionState.mSeq, userId);
+                                sessionToken, channels[0], sessionState.mSeq);
                     }
                     channels[0].dispose();
                 }
@@ -449,13 +493,13 @@
             Slog.e(TAG, "error in createSession", e);
             removeSessionStateLocked(sessionToken, userId);
             sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId, null, null,
-                    sessionState.mSeq, userId);
+                    sessionState.mSeq);
         }
         channels[1].dispose();
     }
 
     private void sendSessionTokenToClientLocked(ITvInputClient client, String inputId,
-            IBinder sessionToken, InputChannel channel, int seq, int userId) {
+            IBinder sessionToken, InputChannel channel, int seq) {
         try {
             client.onSessionCreated(inputId, sessionToken, channel, seq);
         } catch (RemoteException exception) {
@@ -672,7 +716,7 @@
                     }
                     // Send a null token immediately while reconnecting.
                     if (serviceState.mReconnecting == true) {
-                        sendSessionTokenToClientLocked(client, inputId, null, null, seq, userId);
+                        sendSessionTokenToClientLocked(client, inputId, null, null, seq);
                         return;
                     }
 
@@ -784,7 +828,10 @@
                         }
 
                         // Create a log entry and fill it later.
+                        String packageName = userState.inputMap.get(sessionState.mInputId)
+                                .getServiceInfo().packageName;
                         ContentValues values = new ContentValues();
+                        values.put(TvContract.WatchedPrograms.COLUMN_PACKAGE_NAME, packageName);
                         values.put(TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS,
                                 currentTime);
                         values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, 0);
@@ -931,6 +978,9 @@
         // A mapping from the TV input id to its TvInputInfo.
         private final Map<String, TvInputInfo> inputMap = new HashMap<String,TvInputInfo>();
 
+        // A list of all TV input packages.
+        private final Set<String> packageList = new HashSet<String>();
+
         // A mapping from the token of a client to its state.
         private final Map<IBinder, ClientState> clientStateMap =
                 new HashMap<IBinder, ClientState>();
@@ -1095,8 +1145,7 @@
                         if (sessionState.mSession == null) {
                             removeSessionStateLocked(sessionToken, sessionState.mUserId);
                             sendSessionTokenToClientLocked(sessionState.mClient,
-                                    sessionState.mInputId, null, null, sessionState.mSeq,
-                                    sessionState.mUserId);
+                                    sessionState.mInputId, null, null, sessionState.mSeq);
                         }
                     }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2d8a34b..d04d668 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -766,6 +766,8 @@
         mDisplaySettings = new DisplaySettings(context);
         mDisplaySettings.readSettingsLocked();
 
+        LocalServices.addService(WindowManagerPolicy.class, mPolicy);
+
         mPointerEventDispatcher = new PointerEventDispatcher(mInputManager.monitorInput(TAG));
 
         mFxSession = new SurfaceSession();
@@ -779,7 +781,6 @@
 
         mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
         mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
-        mPowerManagerInternal.setPolicy(mPolicy); // TODO: register as local service instead
         mPowerManagerInternal.registerLowPowerModeObserver(
                 new PowerManagerInternal.LowPowerModeListener() {
             @Override
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 4574caf..5cf5713 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -195,7 +195,7 @@
                 = new ArrayList<ActiveAdmin>();
 
         // This is the list of component allowed to start lock task mode.
-        final List<ComponentName> mLockTaskComponents = new ArrayList<ComponentName>();
+        final List<String> mLockTaskPackages = new ArrayList<String>();
 
         ComponentName mRestrictionsProvider;
 
@@ -1014,10 +1014,10 @@
                 out.endTag(null, "active-password");
             }
 
-            for (int i=0; i<policy.mLockTaskComponents.size(); i++) {
-                ComponentName component = policy.mLockTaskComponents.get(i);
+            for (int i=0; i<policy.mLockTaskPackages.size(); i++) {
+                String component = policy.mLockTaskPackages.get(i);
                 out.startTag(null, LOCK_TASK_COMPONENTS_XML);
-                out.attribute(null, "name", component.flattenToString());
+                out.attribute(null, "name", component);
                 out.endTag(null, LOCK_TASK_COMPONENTS_XML);
             }
 
@@ -1077,7 +1077,7 @@
 
             type = parser.next();
             int outerDepth = parser.getDepth();
-            policy.mLockTaskComponents.clear();
+            policy.mLockTaskPackages.clear();
             while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
                    && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
                 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
@@ -1131,9 +1131,7 @@
                             parser.getAttributeValue(null, "nonletter"));
                     XmlUtils.skipCurrentTag(parser);
                 } else if (LOCK_TASK_COMPONENTS_XML.equals(tag)) {
-                    policy.mLockTaskComponents.add
-                        (ComponentName.unflattenFromString
-                         (parser.getAttributeValue(null, "name")));
+                    policy.mLockTaskPackages.add(parser.getAttributeValue(null, "name"));
                     XmlUtils.skipCurrentTag(parser);
                 } else {
                     Slog.w(LOG_TAG, "Unknown tag: " + tag);
@@ -3723,38 +3721,40 @@
     }
 
     /**
-     * Sets which componets may enter lock task mode.
+     * Sets which packages may enter lock task mode.
      *
      * This function can only be called by the device owner or the profile owner.
      * @param components The list of components allowed to enter lock task mode.
      */
-    public void setLockTaskComponents(ComponentName[] components) throws SecurityException {
+    public void setLockTaskPackages(String[] packages) throws SecurityException {
         // Get the package names of the caller.
         int uid = Binder.getCallingUid();
         String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
 
-        // Check whether any of the package name is the device owner or the profile owner.
-        for (int i=0; i<packageNames.length; i++) {
-            String packageName = packageNames[i];
-            int userHandle = UserHandle.getUserId(uid);
-            String profileOwnerPackage = getProfileOwner(userHandle);
-            if (isDeviceOwner(packageName) ||
-                (profileOwnerPackage != null && profileOwnerPackage.equals(packageName))) {
+        synchronized (this) {
+            // Check whether any of the package name is the device owner or the profile owner.
+            for (int i=0; i<packageNames.length; i++) {
+                String packageName = packageNames[i];
+                int userHandle = UserHandle.getUserId(uid);
+                String profileOwnerPackage = getProfileOwner(userHandle);
+                if (isDeviceOwner(packageName) ||
+                    (profileOwnerPackage != null && profileOwnerPackage.equals(packageName))) {
 
-                // If a package name is the device owner or the profile owner,
-                // we update the component list.
-                DevicePolicyData policy = getUserData(userHandle);
-                policy.mLockTaskComponents.clear();
-                if (components != null) {
-                    for (int j=0; j<components.length; j++) {
-                        ComponentName component = components[j];
-                        policy.mLockTaskComponents.add(component);
+                    // If a package name is the device owner or the profile owner,
+                    // we update the component list.
+                    DevicePolicyData policy = getUserData(userHandle);
+                    policy.mLockTaskPackages.clear();
+                    if (packages != null) {
+                        for (int j = 0; j < packages.length; j++) {
+                            String pkg = packages[j];
+                            policy.mLockTaskPackages.add(pkg);
+                        }
                     }
-                }
 
-                // Store the settings persistently.
-                saveSettingsLocked(userHandle);
-                return;
+                    // Store the settings persistently.
+                    saveSettingsLocked(userHandle);
+                    return;
+                }
             }
         }
         throw new SecurityException();
@@ -3763,36 +3763,61 @@
     /**
      * This function returns the list of components allowed to start the task lock mode.
      */
-    public ComponentName[] getLockTaskComponents() {
-        int userHandle = UserHandle.USER_OWNER;
-        DevicePolicyData policy = getUserData(userHandle);
-        ComponentName[] tempArray = policy.mLockTaskComponents.toArray(new ComponentName[0]);
-        return tempArray;
+    public String[] getLockTaskPackages() {
+        synchronized (this) {
+            int userHandle = UserHandle.USER_OWNER;
+            DevicePolicyData policy = getUserData(userHandle);
+            return policy.mLockTaskPackages.toArray(new String[0]);
+        }
     }
 
     /**
-     * This function lets the caller know whether the given component is allowed to start the
+     * This function lets the caller know whether the given package is allowed to start the
      * lock task mode.
-     * @param component The component to check
+     * @param pkg The package to check
      */
-    public boolean isLockTaskPermitted(ComponentName component) {
+    public boolean isLockTaskPermitted(String pkg) {
         // Get current user's devicepolicy
         int uid = Binder.getCallingUid();
         int userHandle = UserHandle.getUserId(uid);
         DevicePolicyData policy = getUserData(userHandle);
-        for (int i=0; i<policy.mLockTaskComponents.size(); i++) {
-            ComponentName lockTaskComponent = policy.mLockTaskComponents.get(i);
+        synchronized (this) {
+            for (int i = 0; i < policy.mLockTaskPackages.size(); i++) {
+                String lockTaskPackage = policy.mLockTaskPackages.get(i);
 
-            // If the given component equals one of the component stored our device-owner-set
-            // list, we allow this component to start the lock task mode.
-            if (lockTaskComponent.getPackageName().equals(component.getPackageName())) {
-                return true;
+                // If the given package equals one of the packages stored our list,
+                // we allow this package to start lock task mode.
+                if (lockTaskPackage.equals(pkg)) {
+                    return true;
+                }
             }
         }
         return false;
     }
 
     @Override
+    public void notifyLockTaskModeChanged(boolean isEnabled, String pkg, int userHandle) {
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            throw new SecurityException("notifyLockTaskModeChanged can only be called by system");
+        }
+        synchronized (this) {
+            final DevicePolicyData policy = getUserData(userHandle);
+            Bundle adminExtras = new Bundle();
+            adminExtras.putBoolean(DeviceAdminReceiver.EXTRA_LOCK_TASK_ENTERING, isEnabled);
+            adminExtras.putString(DeviceAdminReceiver.EXTRA_LOCK_TASK_PACKAGE, pkg);
+            for (ActiveAdmin admin : policy.mAdminList) {
+                boolean ownsDevice = isDeviceOwner(admin.info.getPackageName());
+                boolean ownsProfile = (getProfileOwner(userHandle) != null
+                        && getProfileOwner(userHandle).equals(admin.info.getPackageName()));
+                if (ownsDevice || ownsProfile) {
+                    sendAdminCommandLocked(admin, DeviceAdminReceiver.ACTION_LOCK_TASK_CHANGED,
+                            adminExtras, null);
+                }
+            }
+        }
+    }
+
+    @Override
     public void setGlobalSetting(ComponentName who, String setting, String value) {
         final ContentResolver contentResolver = mContext.getContentResolver();
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index f946595..0d05c5f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -44,7 +44,6 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.service.dreams.DreamService;
-import android.service.fingerprint.FingerprintManager;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Log;
@@ -148,8 +147,13 @@
     private PowerManagerService mPowerManagerService;
     private ActivityManagerService mActivityManagerService;
     private DisplayManagerService mDisplayManagerService;
+    private PackageManagerService mPackageManagerService;
+    private PackageManager mPackageManager;
     private ContentResolver mContentResolver;
 
+    private boolean mOnlyCore;
+    private boolean mFirstBoot;
+
     /**
      * Called to initialize native system services.
      */
@@ -163,6 +167,7 @@
     }
 
     public SystemServer() {
+        // Check for factory test mode.
         mFactoryTestMode = FactoryTest.getMode();
     }
 
@@ -245,7 +250,7 @@
             startBootstrapServices();
             startCoreServices();
             startOtherServices();
-        } catch (RuntimeException ex) {
+        } catch (Throwable ex) {
             Slog.e("System", "******************************************");
             Slog.e("System", "************ Failure starting system services", ex);
             throw ex;
@@ -289,36 +294,84 @@
         mSystemContext.setTheme(android.R.style.Theme_DeviceDefault_Light_DarkActionBar);
     }
 
+    /**
+     * Starts the small tangle of critical services that are needed to get
+     * the system off the ground.  These services have complex mutual dependencies
+     * which is why we initialize them all in one place here.  Unless your service
+     * is also entwined in these dependencies, it should be initialized in one of
+     * the other functions.
+     */
     private void startBootstrapServices() {
         // Wait for installd to finish starting up so that it has a chance to
         // create critical directories such as /data/user with the appropriate
         // permissions.  We need this to complete before we initialize other services.
         mInstaller = mSystemServiceManager.startService(Installer.class);
 
-        // Power manager needs to be started early because other services need it.
-        // TODO: The conversion to the new pattern is incomplete.  We need to switch
-        // the power manager's dependencies over then we can use boot phases to arrange
-        // initialization order and remove the mPowerManagerService field.
-        mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);
-
         // Activity manager runs the show.
         mActivityManagerService = mSystemServiceManager.startService(
                 ActivityManagerService.Lifecycle.class).getService();
         mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
-    }
 
-    private void startCoreServices() {
+        // Power manager needs to be started early because other services need it.
+        // Native daemons may be watching for it to be registered so it must be ready
+        // to handle incoming binder calls immediately (including being able to verify
+        // the permissions for those calls).
+        mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);
+
+        // Now that the power manager has been started, let the activity manager
+        // initialize power management features.
+        mActivityManagerService.initPowerManagement();
+
         // Display manager is needed to provide display metrics before package manager
         // starts up.
         mDisplayManagerService = mSystemServiceManager.startService(DisplayManagerService.class);
+
+        // We need the default display before we can initialize the package manager.
+        mSystemServiceManager.startBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);
+
+        // Only run "core" apps if we're encrypting the device.
+        String cryptState = SystemProperties.get("vold.decrypt");
+        if (ENCRYPTING_STATE.equals(cryptState)) {
+            Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
+            mOnlyCore = true;
+        } else if (ENCRYPTED_STATE.equals(cryptState)) {
+            Slog.w(TAG, "Device encrypted - only parsing core apps");
+            mOnlyCore = true;
+        }
+
+        // Start the package manager.
+        Slog.i(TAG, "Package Manager");
+        mPackageManagerService = PackageManagerService.main(mSystemContext, mInstaller,
+                mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
+        mFirstBoot = mPackageManagerService.isFirstBoot();
+        mPackageManager = mSystemContext.getPackageManager();
+
+        // Initialize attribute cache used to cache resources from packages.
+        AttributeCache.init(mSystemContext);
+
+        // Set up the Application instance for the system process and get started.
+        mActivityManagerService.setSystemProcess();
     }
 
+    /**
+     * Starts some essential services that are not tangled up in the bootstrap process.
+     */
+    private void startCoreServices() {
+        // Manages LEDs and display backlight.
+        mSystemServiceManager.startService(LightsService.class);
+
+        // Tracks the battery level.  Requires LightService.
+        mSystemServiceManager.startService(BatteryService.class);
+    }
+
+    /**
+     * Starts a miscellaneous grab bag of stuff that has yet to be refactored
+     * and organized.
+     */
     private void startOtherServices() {
         final Context context = mSystemContext;
         AccountManagerService accountManager = null;
         ContentService contentService = null;
-        LightsManager lights = null;
-        BatteryService battery = null;
         VibratorService vibrator = null;
         IAlarmManager alarm = null;
         MountService mountService = null;
@@ -328,7 +381,6 @@
         ConnectivityService connectivity = null;
         NetworkScoreService networkScore = null;
         NsdService serviceDiscovery= null;
-        IPackageManager pm = null;
         WindowManagerService wm = null;
         BluetoothManagerService bluetooth = null;
         UsbService usb = null;
@@ -341,8 +393,6 @@
         ConsumerIrService consumerIr = null;
         AudioService audioService = null;
 
-        boolean onlyCore = false;
-        boolean firstBoot = false;
         boolean disableStorage = SystemProperties.getBoolean("config.disable_storage", false);
         boolean disableMedia = SystemProperties.getBoolean("config.disable_media", false);
         boolean disableBluetooth = SystemProperties.getBoolean("config.disable_bluetooth", false);
@@ -354,38 +404,12 @@
         boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1");
 
         try {
-            Slog.i(TAG, "Telephony Registry");
-            telephonyRegistry = new TelephonyRegistry(context);
-            ServiceManager.addService("telephony.registry", telephonyRegistry);
-
             Slog.i(TAG, "Scheduling Policy");
             ServiceManager.addService("scheduling_policy", new SchedulingPolicyService());
 
-            AttributeCache.init(context);
-
-            // We need the default display before we can initialize the package manager.
-            mSystemServiceManager.startBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);
-
-            Slog.i(TAG, "Package Manager");
-            // Only run "core" apps if we're encrypting the device.
-            String cryptState = SystemProperties.get("vold.decrypt");
-            if (ENCRYPTING_STATE.equals(cryptState)) {
-                Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
-                onlyCore = true;
-            } else if (ENCRYPTED_STATE.equals(cryptState)) {
-                Slog.w(TAG, "Device encrypted - only parsing core apps");
-                onlyCore = true;
-            }
-
-            pm = PackageManagerService.main(context, mInstaller,
-                    mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF,
-                    onlyCore);
-            try {
-                firstBoot = pm.isFirstBoot();
-            } catch (RemoteException e) {
-            }
-
-            mActivityManagerService.setSystemProcess();
+            Slog.i(TAG, "Telephony Registry");
+            telephonyRegistry = new TelephonyRegistry(context);
+            ServiceManager.addService("telephony.registry", telephonyRegistry);
 
             Slog.i(TAG, "Entropy Mixer");
             ServiceManager.addService("entropy", new EntropyMixer(context));
@@ -413,24 +437,10 @@
             Slog.i(TAG, "System Content Providers");
             mActivityManagerService.installSystemProviders();
 
-            mSystemServiceManager.startService(LightsService.class);
-            lights = LocalServices.getService(LightsManager.class);
-
-            Slog.i(TAG, "Battery Service");
-            battery = new BatteryService(context, lights);
-            ServiceManager.addService("battery", battery);
-
             Slog.i(TAG, "Vibrator Service");
             vibrator = new VibratorService(context);
             ServiceManager.addService("vibrator", vibrator);
 
-            // TODO: use boot phase
-            // only initialize the power service after we have started the
-            // lights service, content providers and the battery service.
-            mPowerManagerService.init(lights, battery,
-                    BatteryStatsService.getService(),
-                    mActivityManagerService.getAppOpsService());
-
             Slog.i(TAG, "Consumer IR Service");
             consumerIr = new ConsumerIrService(context);
             ServiceManager.addService(Context.CONSUMER_IR_SERVICE, consumerIr);
@@ -449,7 +459,7 @@
             Slog.i(TAG, "Window Manager");
             wm = WindowManagerService.main(context, inputManager,
                     mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
-                    !firstBoot, onlyCore);
+                    !mFirstBoot, mOnlyCore);
             ServiceManager.addService(Context.WINDOW_SERVICE, wm);
             ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
 
@@ -523,7 +533,7 @@
         }
 
         try {
-            pm.performBootDexOpt();
+            mPackageManagerService.performBootDexOpt();
         } catch (Throwable e) {
             reportWtf("performing boot dexopt", e);
         }
@@ -561,13 +571,9 @@
                     reportWtf("starting LockSettingsService service", e);
                 }
 
-                try {
-                    // Always start the Device Policy Manager, so that the API is compatible with
-                    // API8.
-                    mSystemServiceManager.startService(DevicePolicyManagerService.Lifecycle.class);
-                } catch (Throwable e) {
-                    reportWtf("starting DevicePolicyService", e);
-                }
+                // Always start the Device Policy Manager, so that the API is compatible with
+                // API8.
+                mSystemServiceManager.startService(DevicePolicyManagerService.Lifecycle.class);
             }
 
             if (!disableSystemUI) {
@@ -638,39 +644,17 @@
                     reportWtf("starting NetworkPolicy Service", e);
                 }
 
-                try {
-                    mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
-                } catch (Throwable e) {
-                    reportWtf("starting Wi-Fi P2pService", e);
-                }
+                mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
 
-                try {
-                    mSystemServiceManager.startService(WIFI_PASSPOINT_SERVICE_CLASS);
-                } catch (Throwable e) {
-                    reportWtf("starting Wi-Fi PasspointService", e);
-                }
+                mSystemServiceManager.startService(WIFI_PASSPOINT_SERVICE_CLASS);
 
-                try {
-                    mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
-                } catch (Throwable e) {
-                    reportWtf("starting Wi-Fi Service", e);
-                }
+                mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
 
-                try {
-                    Slog.i(TAG, "Wi-Fi Scanning Service");
-                    mSystemServiceManager.startService(
+                mSystemServiceManager.startService(
                             "com.android.server.wifi.WifiScanningService");
 
-                } catch (Throwable e) {
-                    reportWtf("starting Wi-Fi Scanning Service", e);
-                }
-
                 if (!isEmulator) {
-                    try {
-                        mSystemServiceManager.startService(ETHERNET_SERVICE_CLASS);
-                    } catch (Throwable e) {
-                        reportWtf("starting Ethernet Service", e);
-                    }
+                    mSystemServiceManager.startService(ETHERNET_SERVICE_CLASS);
                 } else {
                     // Don't start the Ethernet service on the emulator because
                     // it interferes with qemu's SLIRP emulation, which uses
@@ -714,7 +698,7 @@
              * AppWidget Provider. Make sure MountService is completely started
              * first before continuing.
              */
-            if (mountService != null && !onlyCore) {
+            if (mountService != null && !mOnlyCore) {
                 mountService.waitForAsecScan();
             }
 
@@ -812,14 +796,11 @@
             }
 
             if (!disableNonCoreServices) {
-                try {
-                    if (pm.hasSystemFeature(PackageManager.FEATURE_USB_HOST) ||
-                            pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY)) {
-                        // Manage USB host and device support
-                        mSystemServiceManager.startService(USB_SERVICE_CLASS);
-                    }
-                } catch (Throwable e) {
-                    reportWtf("starting UsbService", e);
+                if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)
+                        || mPackageManager.hasSystemFeature(
+                                PackageManager.FEATURE_USB_ACCESSORY)) {
+                    // Manage USB host and device support
+                    mSystemServiceManager.startService(USB_SERVICE_CLASS);
                 }
 
                 try {
@@ -839,20 +820,12 @@
             mSystemServiceManager.startService(JobSchedulerService.class);
 
             if (!disableNonCoreServices) {
-                try {
-                    if (pm.hasSystemFeature(PackageManager.FEATURE_BACKUP)) {
-                        mSystemServiceManager.startService(BACKUP_MANAGER_SERVICE_CLASS);
-                    }
-                } catch (Throwable e) {
-                    Slog.e(TAG, "Failure starting Backup Service", e);
+                if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_BACKUP)) {
+                    mSystemServiceManager.startService(BACKUP_MANAGER_SERVICE_CLASS);
                 }
 
-                try {
-                    if (pm.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)) {
-                        mSystemServiceManager.startService(APPWIDGET_SERVICE_CLASS);
-                    }
-                } catch (Throwable e) {
-                    reportWtf("starting AppWidget Service", e);
+                if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)) {
+                    mSystemServiceManager.startService(APPWIDGET_SERVICE_CLASS);
                 }
 
                 try {
@@ -862,13 +835,8 @@
                     reportWtf("starting Recognition Service", e);
                 }
 
-                try {
-                    if (pm.hasSystemFeature(PackageManager.FEATURE_VOICE_RECOGNIZERS)) {
-                        Slog.i(TAG, "Voice Recognition Service");
-                        mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS);
-                    }
-                } catch (Throwable e) {
-                    reportWtf("starting Voice Recognition Service", e);
+                if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_VOICE_RECOGNIZERS)) {
+                    mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS);
                 }
             }
 
@@ -934,38 +902,17 @@
                 }
             }
 
-            try {
-                if (pm.hasSystemFeature(PackageManager.FEATURE_PRINTING)) {
-                    mSystemServiceManager.startService(PRINT_MANAGER_SERVICE_CLASS);
-                }
-            } catch (Throwable e) {
-                reportWtf("starting Print Service", e);
+            if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_PRINTING)) {
+                mSystemServiceManager.startService(PRINT_MANAGER_SERVICE_CLASS);
             }
 
-            try {
-                mSystemServiceManager.startService(RestrictionsManagerService.class);
-            } catch (Throwable e) {
-                reportWtf("starting RestrictionsManagerService", e);
-            }
+            mSystemServiceManager.startService(RestrictionsManagerService.class);
 
-            try {
-                mSystemServiceManager.startService(MediaSessionService.class);
-            } catch (Throwable e) {
-                reportWtf("starting MediaSessionService", e);
-            }
+            mSystemServiceManager.startService(MediaSessionService.class);
 
-            try {
-                mSystemServiceManager.startService(HdmiControlService.class);
-            } catch (Throwable e) {
-                reportWtf("starting HdmiControlService", e);
-            }
+            mSystemServiceManager.startService(HdmiControlService.class);
 
-            try {
-                Slog.i(TAG, "TvInputManagerService");
-                mSystemServiceManager.startService(TvInputManagerService.class);
-            } catch (Throwable e) {
-                reportWtf("starting TvInputManagerService", e);
-            }
+            mSystemServiceManager.startService(TvInputManagerService.class);
 
             if (!disableNonCoreServices) {
                 try {
@@ -976,19 +923,9 @@
                     reportWtf("starting MediaRouterService", e);
                 }
 
-                try {
-                    Slog.i(TAG, "Trust Manager");
-                    mSystemServiceManager.startService(TrustManagerService.class);
-                } catch (Throwable e) {
-                    Slog.e(TAG, "Failure starting TrustManagerService", e);
-                }
+                mSystemServiceManager.startService(TrustManagerService.class);
 
-                try {
-                    Slog.i(TAG, "Fingerprint Manager");
-                    mSystemServiceManager.startService(FingerprintService.class);
-                } catch (Throwable e) {
-                    Slog.e(TAG, "Failure starting FingerprintService", e);
-                }
+                mSystemServiceManager.startService(FingerprintService.class);
 
                 try {
                     Slog.i(TAG, "BackgroundDexOptService");
@@ -999,12 +936,7 @@
 
             }
 
-            try {
-                Slog.i(TAG, "LauncherAppsService");
-                mSystemServiceManager.startService(LauncherAppsService.class);
-            } catch (Throwable t) {
-                reportWtf("starting LauncherAppsService", t);
-            }
+            mSystemServiceManager.startService(LauncherAppsService.class);
         }
 
         // Before things start rolling, be sure we have decided whether
@@ -1061,27 +993,26 @@
 
         try {
             // TODO: use boot phase
-            mPowerManagerService.systemReady();
+            mPowerManagerService.systemReady(mActivityManagerService.getAppOpsService());
         } catch (Throwable e) {
             reportWtf("making Power Manager Service ready", e);
         }
 
         try {
-            pm.systemReady();
+            mPackageManagerService.systemReady();
         } catch (Throwable e) {
             reportWtf("making Package Manager Service ready", e);
         }
 
         try {
             // TODO: use boot phase and communicate these flags some other way
-            mDisplayManagerService.systemReady(safeMode, onlyCore);
+            mDisplayManagerService.systemReady(safeMode, mOnlyCore);
         } catch (Throwable e) {
             reportWtf("making Display Manager Service ready", e);
         }
 
         // These are needed to propagate to the runnable below.
         final MountService mountServiceF = mountService;
-        final BatteryService batteryF = battery;
         final NetworkManagementService networkManagementF = networkManagement;
         final NetworkStatsService networkStatsF = networkStats;
         final NetworkPolicyManagerService networkPolicyF = networkPolicy;
@@ -1130,11 +1061,6 @@
                     reportWtf("making Mount Service ready", e);
                 }
                 try {
-                    if (batteryF != null) batteryF.systemReady();
-                } catch (Throwable e) {
-                    reportWtf("making Battery Service ready", e);
-                }
-                try {
                     if (networkScoreF != null) networkScoreF.systemReady();
                 } catch (Throwable e) {
                     reportWtf("making Network Score Service ready", e);
diff --git a/telecomm/java/android/telecomm/TelecommManager.java b/telecomm/java/android/telecomm/TelecommManager.java
index a97e7e4..989c2cd 100644
--- a/telecomm/java/android/telecomm/TelecommManager.java
+++ b/telecomm/java/android/telecomm/TelecommManager.java
@@ -16,7 +16,9 @@
 
 package android.telecomm;
 
+import android.content.ComponentName;
 import android.content.Context;
+import android.os.RemoteException;
 
 import com.android.internal.telecomm.ITelecommService;
 
@@ -45,4 +47,14 @@
     public static TelecommManager from(Context context) {
         return (TelecommManager) context.getSystemService(Context.TELECOMM_SERVICE);
     }
+
+    /** {@hide} */
+    public ComponentName getSystemPhoneApplication() {
+        try {
+            return mService.getSystemPhoneApplication();
+        } catch (RemoteException e) {
+            Log.e(TAG, e, "Error calling ITelecommService#getSystemPhoneApplication");
+            return null;
+        }
+    }
 }
diff --git a/telephony/java/android/telephony/SubInfoRecord.java b/telephony/java/android/telephony/SubInfoRecord.java
index 670def7..ced8e2f 100644
--- a/telephony/java/android/telephony/SubInfoRecord.java
+++ b/telephony/java/android/telephony/SubInfoRecord.java
@@ -105,4 +105,11 @@
         return 0;
     }
 
+    public String toString() {
+        return "{mSubId=" + mSubId + ", mIccId=" + mIccId + " mSlotId=" + mSlotId
+                + " mDisplayName=" + mDisplayName + " mNameSource=" + mNameSource
+                + " mColor=" + mColor + " mNumber=" + mNumber
+                + " mDispalyNumberFormat=" + mDispalyNumberFormat + " mDataRoaming=" + mDataRoaming
+                + " mSimIconRes=" + mSimIconRes + "}";
+    }
 }
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 859a890..79e9fd5 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -697,12 +697,16 @@
     public static void putPhoneIdAndSubIdExtra(Intent intent, int phoneId) {
         long [] subId = SubscriptionManager.getSubId(phoneId);
         if ((subId != null) && (subId.length >= 1)) {
-            if (VDBG) logd("putPhoneIdAndSubIdExtra: phoneId=" + phoneId + " subId=" + subId);
-            intent.putExtra(PhoneConstants.SLOT_KEY, phoneId); //FIXME: RENAME TO PHONE_ID_KEY ??
-            intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId[0]);
+            putPhoneIdAndSubIdExtra(intent, phoneId, subId[0]);
         } else {
             logd("putPhoneIdAndSubIdExtra: no valid subs");
         }
     }
+
+    public static void putPhoneIdAndSubIdExtra(Intent intent, int phoneId, long subId) {
+        if (VDBG) logd("putPhoneIdAndSubIdExtra: phoneId=" + phoneId + " subId=" + subId);
+        intent.putExtra(PhoneConstants.SLOT_KEY, phoneId); //FIXME: RENAME TO PHONE_ID_KEY ??
+        intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+    }
 }
 
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 3fde36e..124a8ec 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -3035,28 +3035,6 @@
 
     /** @hide */
     @SystemApi
-    public int enableApnType(String type) {
-        try {
-            return getITelephony().enableApnType(type);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#enableApnType", e);
-        }
-        return PhoneConstants.APN_REQUEST_FAILED;
-    }
-
-    /** @hide */
-    @SystemApi
-    public int disableApnType(String type) {
-        try {
-            return getITelephony().disableApnType(type);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#disableApnType", e);
-        }
-        return PhoneConstants.APN_REQUEST_FAILED;
-    }
-
-    /** @hide */
-    @SystemApi
     public boolean enableDataConnectivity() {
         try {
             return getITelephony().enableDataConnectivity();
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 407da87..beee616 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -310,27 +310,6 @@
      */
     void disableLocationUpdatesUsingSubId(long subId);
 
-
-    /**
-     * Enable a specific APN type.
-     */
-    int enableApnType(String type);
-
-    /**
-     * Disable a specific APN type.
-     */
-    int disableApnType(String type);
-
-    /**
-     * Enable a specific APN type with subscription.
-     */
-    int enableApnTypeUsingSub(long subId, String type);
-
-    /**
-     * Disable a specific APN type with subscription.
-     */
-    int disableApnTypeUsingSub(long subId, String type);
-
     /**
      * Allow mobile data connections.
      */
diff --git a/tests/VectorDrawableTest/AndroidManifest.xml b/tests/VectorDrawableTest/AndroidManifest.xml
index db2efc3..890214f 100644
--- a/tests/VectorDrawableTest/AndroidManifest.xml
+++ b/tests/VectorDrawableTest/AndroidManifest.xml
@@ -33,6 +33,16 @@
             </intent-filter>
         </activity>
         <activity
+            android:name="BitmapDrawableDupe"
+            android:label="Bitmap Performance of clones" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="com.android.test.dynamic.TEST" />
+            </intent-filter>
+
+        </activity>
+        <activity
             android:name="VectorDrawableAnimation"
             android:label="VectorTestAnimation" >
             <intent-filter>
diff --git a/tests/VectorDrawableTest/res/drawable-nodpi/bitmap_drawable01.jpg b/tests/VectorDrawableTest/res/drawable-nodpi/bitmap_drawable01.jpg
new file mode 100644
index 0000000..dc8c197
--- /dev/null
+++ b/tests/VectorDrawableTest/res/drawable-nodpi/bitmap_drawable01.jpg
Binary files differ
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/BitmapDrawableDupe.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/BitmapDrawableDupe.java
new file mode 100644
index 0000000..36c8f2b
--- /dev/null
+++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/BitmapDrawableDupe.java
@@ -0,0 +1,78 @@
+/*
+ * 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 com.android.test.dynamic;
+
+import android.app.Activity;
+import android.content.res.Resources;
+import android.graphics.drawable.BitmapDrawable;
+import android.os.Bundle;
+import android.widget.TextView;
+import android.widget.Button;
+import android.widget.GridLayout;
+import android.widget.ScrollView;
+
+import java.text.DecimalFormat;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class BitmapDrawableDupe extends Activity {
+    private static final String LOGCAT = "VectorDrawable1";
+    protected int[] icon = {
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+    };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        ScrollView scrollView = new ScrollView(this);
+        GridLayout container = new GridLayout(this);
+        scrollView.addView(container);
+        container.setColumnCount(5);
+        container.setBackgroundColor(0xFF888888);
+
+        DecimalFormat df = new DecimalFormat("#.##");
+        long time =  android.os.SystemClock.elapsedRealtimeNanos();
+        for (int i = 0; i < icon.length; i++) {
+            Button button = new Button(this);
+            button.setWidth(200);
+            button.setBackgroundResource(icon[i]);
+            container.addView(button);
+        }
+
+        setContentView(scrollView);
+        time =  android.os.SystemClock.elapsedRealtimeNanos()-time;
+        TextView t = new TextView(this);
+        t.setText("avgS=" + df.format(time / (icon.length * 1000000.)) + " ms");
+        container.addView(t);
+    }
+}
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index f10904c..3d93bbe 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -589,11 +589,11 @@
                 if (bundle->getVerbose()) {
                     printf("trying overlaySet Key=%s\n",overlaySet->keyAt(overlayIndex).string());
                 }
-                size_t baseIndex = UNKNOWN_ERROR;
+                ssize_t baseIndex = -1;
                 if (baseSet->get() != NULL) {
                     baseIndex = (*baseSet)->indexOfKey(overlaySet->keyAt(overlayIndex));
                 }
-                if (baseIndex < UNKNOWN_ERROR) {
+                if (baseIndex >= 0) {
                     // look for same flavor.  For a given file (strings.xml, for example)
                     // there may be a locale specific or other flavors - we want to match
                     // the same flavor.
@@ -619,10 +619,10 @@
                     for (size_t overlayGroupIndex = 0;
                             overlayGroupIndex<overlayGroupSize;
                             overlayGroupIndex++) {
-                        size_t baseFileIndex =
+                        ssize_t baseFileIndex =
                                 baseGroup->getFiles().indexOfKey(overlayFiles.
                                 keyAt(overlayGroupIndex));
-                        if (baseFileIndex < UNKNOWN_ERROR) {
+                        if (baseFileIndex >= 0) {
                             if (bundle->getVerbose()) {
                                 printf("found a match (" ZD ") for overlay file %s, for flavor %s\n",
                                         (ZD_TYPE) baseFileIndex,
@@ -1363,7 +1363,11 @@
 
             if (split->isBase()) {
                 resFile = flattenedTable;
-                finalResTable.add(flattenedTable->getData(), flattenedTable->getSize());
+                err = finalResTable.add(flattenedTable->getData(), flattenedTable->getSize());
+                if (err != NO_ERROR) {
+                    fprintf(stderr, "Generated resource table is corrupt.\n");
+                    return err;
+                }
             } else {
                 sp<AaptFile> generatedManifest = new AaptFile(String8("AndroidManifest.xml"),
                         AaptGroupEntry(), String8());
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index efbba40..1a9f1b9 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -2292,8 +2292,14 @@
     if (resId != 0 || !createIfNotFound) {
         return resId;
     }
-    String16 value("false");
 
+    if (mAssetsPackage != package) {
+        mCurrentXmlPos.warning("creating resource for external package %s: %s/%s.",
+                String8(package).string(), String8(type).string(), String8(name).string());
+        mCurrentXmlPos.printf("This will be an error in a future version of AAPT.");
+    }
+
+    String16 value("false");
     status_t status = addEntry(mCurrentXmlPos, package, type, name, value, NULL, NULL, true);
     if (status == NO_ERROR) {
         resId = getResId(package, type, name);
@@ -3062,8 +3068,9 @@
             for (size_t i = 0; i < N; ++i) {
                 if (!validResources[i]) {
                     sp<ConfigList> c = t->getOrderedConfigs().itemAt(i);
-                    fprintf(stderr, "%s: no entries written for %s/%s\n", log_prefix,
-                            String8(typeName).string(), String8(c->getName()).string());
+                    fprintf(stderr, "%s: no entries written for %s/%s (0x%08x)\n", log_prefix,
+                            String8(typeName).string(), String8(c->getName()).string(),
+                            Res_MAKEID(p->getAssignedId() - 1, ti, i));
                     missing_entry = true;
                 }
             }