Merge "Allow PO to set DO restrictions if it's on user 0"
diff --git a/api/current.txt b/api/current.txt
index 1ed2f7e..894f016 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -728,6 +728,7 @@
     field public static final int label = 16842753; // 0x1010001
     field public static final int labelFor = 16843718; // 0x10103c6
     field public static final int labelTextSize = 16843317; // 0x1010235
+    field public static final int languageTag = 16844041; // 0x1010509
     field public static final int largeHeap = 16843610; // 0x101035a
     field public static final int largeScreens = 16843398; // 0x1010286
     field public static final int largestWidthLimitDp = 16843622; // 0x1010366
@@ -7780,8 +7781,7 @@
     method public abstract int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public abstract deprecated void clearWallpaper() throws java.io.IOException;
     method public abstract android.content.Context createConfigurationContext(android.content.res.Configuration);
-    method public abstract android.content.Context createCredentialEncryptedContext(android.content.Context);
-    method public abstract android.content.Context createDeviceEncryptedContext(android.content.Context);
+    method public abstract android.content.Context createDeviceEncryptedStorageContext();
     method public abstract android.content.Context createDisplayContext(android.view.Display);
     method public abstract android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract java.lang.String[] databaseList();
@@ -7837,8 +7837,7 @@
     method public abstract deprecated int getWallpaperDesiredMinimumHeight();
     method public abstract deprecated int getWallpaperDesiredMinimumWidth();
     method public abstract void grantUriPermission(java.lang.String, android.net.Uri, int);
-    method public abstract boolean isCredentialEncrypted();
-    method public abstract boolean isDeviceEncrypted();
+    method public abstract boolean isDeviceEncryptedStorage();
     method public boolean isRestricted();
     method public final android.content.res.TypedArray obtainStyledAttributes(int[]);
     method public final android.content.res.TypedArray obtainStyledAttributes(int, int[]) throws android.content.res.Resources.NotFoundException;
@@ -7970,8 +7969,7 @@
     method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public deprecated void clearWallpaper() throws java.io.IOException;
     method public android.content.Context createConfigurationContext(android.content.res.Configuration);
-    method public android.content.Context createCredentialEncryptedContext(android.content.Context);
-    method public android.content.Context createDeviceEncryptedContext(android.content.Context);
+    method public android.content.Context createDeviceEncryptedStorageContext();
     method public android.content.Context createDisplayContext(android.view.Display);
     method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public java.lang.String[] databaseList();
@@ -8021,8 +8019,7 @@
     method public deprecated int getWallpaperDesiredMinimumHeight();
     method public deprecated int getWallpaperDesiredMinimumWidth();
     method public void grantUriPermission(java.lang.String, android.net.Uri, int);
-    method public boolean isCredentialEncrypted();
-    method public boolean isDeviceEncrypted();
+    method public boolean isDeviceEncryptedStorage();
     method public java.io.FileInputStream openFileInput(java.lang.String) throws java.io.FileNotFoundException;
     method public java.io.FileOutputStream openFileOutput(java.lang.String, int) throws java.io.FileNotFoundException;
     method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory);
@@ -11545,6 +11542,7 @@
     field public static final int PRIVATE = 34; // 0x22
     field public static final int RAW10 = 37; // 0x25
     field public static final int RAW12 = 38; // 0x26
+    field public static final int RAW_PRIVATE = 36; // 0x24
     field public static final int RAW_SENSOR = 32; // 0x20
     field public static final int RGB_565 = 4; // 0x4
     field public static final int UNKNOWN = 0; // 0x0
@@ -20305,6 +20303,8 @@
     field public static final java.lang.String MIMETYPE_TEXT_CEA_608 = "text/cea-608";
     field public static final java.lang.String MIMETYPE_TEXT_VTT = "text/vtt";
     field public static final java.lang.String MIMETYPE_VIDEO_AVC = "video/avc";
+    field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_AVC = "video/dolby-avc";
+    field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_HEVC = "video/dolby-hevc";
     field public static final java.lang.String MIMETYPE_VIDEO_H263 = "video/3gpp";
     field public static final java.lang.String MIMETYPE_VIDEO_HEVC = "video/hevc";
     field public static final java.lang.String MIMETYPE_VIDEO_MPEG2 = "video/mpeg2";
@@ -20736,6 +20736,7 @@
   public static class MediaRouter.RouteInfo {
     method public android.media.MediaRouter.RouteCategory getCategory();
     method public java.lang.CharSequence getDescription();
+    method public int getDeviceType();
     method public android.media.MediaRouter.RouteGroup getGroup();
     method public android.graphics.drawable.Drawable getIconDrawable();
     method public java.lang.CharSequence getName();
@@ -20754,6 +20755,10 @@
     method public void requestSetVolume(int);
     method public void requestUpdateVolume(int);
     method public void setTag(java.lang.Object);
+    field public static final int DEVICE_TYPE_BLUETOOTH = 3; // 0x3
+    field public static final int DEVICE_TYPE_SPEAKER = 2; // 0x2
+    field public static final int DEVICE_TYPE_TV = 1; // 0x1
+    field public static final int DEVICE_TYPE_UNKNOWN = 0; // 0x0
     field public static final int PLAYBACK_TYPE_LOCAL = 0; // 0x0
     field public static final int PLAYBACK_TYPE_REMOTE = 1; // 0x1
     field public static final int PLAYBACK_VOLUME_FIXED = 0; // 0x0
@@ -28095,7 +28100,9 @@
     method public boolean isSystemUser();
     method public boolean isUserAGoat();
     method public boolean isUserRunning(android.os.UserHandle);
+    method public boolean isUserRunningAndLocked();
     method public boolean isUserRunningAndLocked(android.os.UserHandle);
+    method public boolean isUserRunningAndUnlocked();
     method public boolean isUserRunningAndUnlocked(android.os.UserHandle);
     method public boolean isUserRunningOrStopping(android.os.UserHandle);
     method public deprecated boolean setRestrictionsChallenge(java.lang.String);
@@ -35218,6 +35225,7 @@
     field public static final java.lang.String KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL = "voicemail_notification_persistent_bool";
     field public static final java.lang.String KEY_VOICE_PRIVACY_DISABLE_UI_BOOL = "voice_privacy_disable_ui_bool";
     field public static final java.lang.String KEY_VOLTE_REPLACEMENT_RAT_INT = "volte_replacement_rat_int";
+    field public static final java.lang.String KEY_VVM_CELLULAR_DATA_REQUIRED_BOOLEAN = "vvm_cellular_data_required";
     field public static final java.lang.String KEY_VVM_DESTINATION_NUMBER_STRING = "vvm_destination_number_string";
     field public static final java.lang.String KEY_VVM_PORT_NUMBER_INT = "vvm_port_number_int";
     field public static final java.lang.String KEY_VVM_TYPE_STRING = "vvm_type_string";
@@ -36248,8 +36256,7 @@
     method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public void clearWallpaper();
     method public android.content.Context createConfigurationContext(android.content.res.Configuration);
-    method public android.content.Context createCredentialEncryptedContext(android.content.Context);
-    method public android.content.Context createDeviceEncryptedContext(android.content.Context);
+    method public android.content.Context createDeviceEncryptedStorageContext();
     method public android.content.Context createDisplayContext(android.view.Display);
     method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public java.lang.String[] databaseList();
@@ -36298,8 +36305,7 @@
     method public int getWallpaperDesiredMinimumHeight();
     method public int getWallpaperDesiredMinimumWidth();
     method public void grantUriPermission(java.lang.String, android.net.Uri, int);
-    method public boolean isCredentialEncrypted();
-    method public boolean isDeviceEncrypted();
+    method public boolean isDeviceEncryptedStorage();
     method public java.io.FileInputStream openFileInput(java.lang.String) throws java.io.FileNotFoundException;
     method public java.io.FileOutputStream openFileOutput(java.lang.String, int) throws java.io.FileNotFoundException;
     method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory);
@@ -40245,6 +40251,7 @@
     field public static final int AXIS_RX = 12; // 0xc
     field public static final int AXIS_RY = 13; // 0xd
     field public static final int AXIS_RZ = 14; // 0xe
+    field public static final int AXIS_SCROLL = 26; // 0x1a
     field public static final int AXIS_SIZE = 3; // 0x3
     field public static final int AXIS_THROTTLE = 19; // 0x13
     field public static final int AXIS_TILT = 25; // 0x19
@@ -40553,7 +40560,6 @@
     method public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo();
     method public void createContextMenu(android.view.ContextMenu);
     method public void destroyDrawingCache();
-    method public final boolean didLayoutParamsChange();
     method public android.view.WindowInsets dispatchApplyWindowInsets(android.view.WindowInsets);
     method public void dispatchConfigurationChanged(android.content.res.Configuration);
     method public void dispatchDisplayHint(int);
@@ -40779,7 +40785,6 @@
     method public boolean isOpaque();
     method protected boolean isPaddingOffsetRequired();
     method public boolean isPaddingRelative();
-    method public final boolean isPartialLayoutRequested();
     method public boolean isPressed();
     method public boolean isSaveEnabled();
     method public boolean isSaveFromParentEnabled();
@@ -41391,7 +41396,6 @@
     method protected void dispatchThawSelfOnly(android.util.SparseArray<android.os.Parcelable>);
     method protected boolean drawChild(android.graphics.Canvas, android.view.View, long);
     method public void endViewTransition(android.view.View);
-    method public int findDependentLayoutAxes(android.view.View, int);
     method public android.view.View focusSearch(android.view.View, int);
     method public void focusableViewAvailable(android.view.View);
     method public boolean gatherTransparentRegion(android.graphics.Region);
@@ -41458,8 +41462,6 @@
     method public void requestChildFocus(android.view.View, android.view.View);
     method public boolean requestChildRectangleOnScreen(android.view.View, android.graphics.Rect, boolean);
     method public void requestDisallowInterceptTouchEvent(boolean);
-    method public void requestLayoutForChild(android.view.View);
-    method public void requestPartialLayoutForChild(android.view.View);
     method public boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
     method public void requestTransparentRegion(android.view.View);
     method public void scheduleLayoutAnimation();
@@ -41574,7 +41576,6 @@
     method public abstract void childHasTransientStateChanged(android.view.View, boolean);
     method public abstract void clearChildFocus(android.view.View);
     method public abstract void createContextMenu(android.view.ContextMenu);
-    method public abstract int findDependentLayoutAxes(android.view.View, int);
     method public abstract android.view.View focusSearch(android.view.View, int);
     method public abstract void focusableViewAvailable(android.view.View);
     method public abstract boolean getChildVisibleRect(android.view.View, android.graphics.Rect, android.graphics.Point);
@@ -41604,16 +41605,12 @@
     method public abstract void requestDisallowInterceptTouchEvent(boolean);
     method public abstract void requestFitSystemWindows();
     method public abstract void requestLayout();
-    method public abstract void requestLayoutForChild(android.view.View);
     method public abstract boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
     method public abstract void requestTransparentRegion(android.view.View);
     method public abstract boolean showContextMenuForChild(android.view.View);
     method public abstract boolean showContextMenuForChild(android.view.View, float, float);
     method public abstract android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback);
     method public abstract android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback, int);
-    field public static final int FLAG_LAYOUT_AXIS_ANY = 3; // 0x3
-    field public static final int FLAG_LAYOUT_AXIS_HORIZONTAL = 1; // 0x1
-    field public static final int FLAG_LAYOUT_AXIS_VERTICAL = 2; // 0x2
   }
 
   public class ViewPropertyAnimator {
@@ -41844,6 +41841,7 @@
     method public abstract void setContentView(int);
     method public abstract void setContentView(android.view.View);
     method public abstract void setContentView(android.view.View, android.view.ViewGroup.LayoutParams);
+    method public abstract void setDecorCaptionShade(int);
     method protected void setDefaultWindowFormat(int);
     method public void setDimAmount(float);
     method public void setElevation(float);
@@ -41864,6 +41862,8 @@
     method public void setMediaController(android.media.session.MediaController);
     method public abstract void setNavigationBarColor(int);
     method public void setReenterTransition(android.transition.Transition);
+    method public abstract void setResizingCaptionDrawable(android.graphics.drawable.Drawable);
+    method public final void setRestrictedCaptionAreaListener(android.view.Window.RestrictedCaptionAreaListener);
     method public void setReturnTransition(android.transition.Transition);
     method public void setSharedElementEnterTransition(android.transition.Transition);
     method public void setSharedElementExitTransition(android.transition.Transition);
@@ -41892,6 +41892,9 @@
     method public abstract void takeKeyEvents(boolean);
     method public abstract void takeSurface(android.view.SurfaceHolder.Callback2);
     method public abstract void togglePanel(int, android.view.KeyEvent);
+    field public static final int DECOR_CAPTION_SHADE_AUTO = 0; // 0x0
+    field public static final int DECOR_CAPTION_SHADE_DARK = 2; // 0x2
+    field public static final int DECOR_CAPTION_SHADE_LIGHT = 1; // 0x1
     field protected static final deprecated int DEFAULT_FEATURES = 65; // 0x41
     field public static final int FEATURE_ACTION_BAR = 8; // 0x8
     field public static final int FEATURE_ACTION_BAR_OVERLAY = 9; // 0x9
@@ -41946,6 +41949,10 @@
     method public abstract android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback, int);
   }
 
+  public static abstract interface Window.RestrictedCaptionAreaListener {
+    method public abstract void onRestrictedCaptionAreaChanged(android.graphics.Rect);
+  }
+
   public final class WindowAnimationFrameStats extends android.view.FrameStats implements android.os.Parcelable {
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
@@ -43210,7 +43217,8 @@
     method public java.lang.String getExtraValue();
     method public java.lang.String getExtraValueOf(java.lang.String);
     method public int getIconResId();
-    method public java.lang.String getLocale();
+    method public java.lang.String getLanguageTag();
+    method public deprecated java.lang.String getLocale();
     method public java.lang.String getMode();
     method public int getNameResId();
     method public boolean isAsciiCapable();
@@ -43225,6 +43233,7 @@
     method public android.view.inputmethod.InputMethodSubtype build();
     method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setIsAsciiCapable(boolean);
     method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setIsAuxiliary(boolean);
+    method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setLanguageTag(java.lang.String);
     method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setOverridesImplicitlyEnabledSubtype(boolean);
     method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeExtraValue(java.lang.String);
     method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeIconResId(int);
@@ -43288,7 +43297,8 @@
     method public java.lang.CharSequence getDisplayName(android.content.Context, java.lang.String, android.content.pm.ApplicationInfo);
     method public java.lang.String getExtraValue();
     method public java.lang.String getExtraValueOf(java.lang.String);
-    method public java.lang.String getLocale();
+    method public java.lang.String getLanguageTag();
+    method public deprecated java.lang.String getLocale();
     method public int getNameResId();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.view.textservice.SpellCheckerSubtype> CREATOR;
diff --git a/api/system-current.txt b/api/system-current.txt
index f3f06c9..1dbc408 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -822,6 +822,7 @@
     field public static final int label = 16842753; // 0x1010001
     field public static final int labelFor = 16843718; // 0x10103c6
     field public static final int labelTextSize = 16843317; // 0x1010235
+    field public static final int languageTag = 16844041; // 0x1010509
     field public static final int largeHeap = 16843610; // 0x101035a
     field public static final int largeScreens = 16843398; // 0x1010286
     field public static final int largestWidthLimitDp = 16843622; // 0x1010366
@@ -8024,8 +8025,8 @@
     method public abstract int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public abstract deprecated void clearWallpaper() throws java.io.IOException;
     method public abstract android.content.Context createConfigurationContext(android.content.res.Configuration);
-    method public abstract android.content.Context createCredentialEncryptedContext(android.content.Context);
-    method public abstract android.content.Context createDeviceEncryptedContext(android.content.Context);
+    method public abstract android.content.Context createCredentialEncryptedStorageContext();
+    method public abstract android.content.Context createDeviceEncryptedStorageContext();
     method public abstract android.content.Context createDisplayContext(android.view.Display);
     method public abstract android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract java.lang.String[] databaseList();
@@ -8081,8 +8082,8 @@
     method public abstract deprecated int getWallpaperDesiredMinimumHeight();
     method public abstract deprecated int getWallpaperDesiredMinimumWidth();
     method public abstract void grantUriPermission(java.lang.String, android.net.Uri, int);
-    method public abstract boolean isCredentialEncrypted();
-    method public abstract boolean isDeviceEncrypted();
+    method public abstract boolean isCredentialEncryptedStorage();
+    method public abstract boolean isDeviceEncryptedStorage();
     method public boolean isRestricted();
     method public final android.content.res.TypedArray obtainStyledAttributes(int[]);
     method public final android.content.res.TypedArray obtainStyledAttributes(int, int[]) throws android.content.res.Resources.NotFoundException;
@@ -8222,8 +8223,8 @@
     method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public deprecated void clearWallpaper() throws java.io.IOException;
     method public android.content.Context createConfigurationContext(android.content.res.Configuration);
-    method public android.content.Context createCredentialEncryptedContext(android.content.Context);
-    method public android.content.Context createDeviceEncryptedContext(android.content.Context);
+    method public android.content.Context createCredentialEncryptedStorageContext();
+    method public android.content.Context createDeviceEncryptedStorageContext();
     method public android.content.Context createDisplayContext(android.view.Display);
     method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public java.lang.String[] databaseList();
@@ -8273,8 +8274,8 @@
     method public deprecated int getWallpaperDesiredMinimumHeight();
     method public deprecated int getWallpaperDesiredMinimumWidth();
     method public void grantUriPermission(java.lang.String, android.net.Uri, int);
-    method public boolean isCredentialEncrypted();
-    method public boolean isDeviceEncrypted();
+    method public boolean isCredentialEncryptedStorage();
+    method public boolean isDeviceEncryptedStorage();
     method public java.io.FileInputStream openFileInput(java.lang.String) throws java.io.FileNotFoundException;
     method public java.io.FileOutputStream openFileOutput(java.lang.String, int) throws java.io.FileNotFoundException;
     method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory);
@@ -11888,6 +11889,7 @@
     field public static final int PRIVATE = 34; // 0x22
     field public static final int RAW10 = 37; // 0x25
     field public static final int RAW12 = 38; // 0x26
+    field public static final int RAW_PRIVATE = 36; // 0x24
     field public static final int RAW_SENSOR = 32; // 0x20
     field public static final int RGB_565 = 4; // 0x4
     field public static final int UNKNOWN = 0; // 0x0
@@ -21592,6 +21594,8 @@
     field public static final java.lang.String MIMETYPE_TEXT_CEA_608 = "text/cea-608";
     field public static final java.lang.String MIMETYPE_TEXT_VTT = "text/vtt";
     field public static final java.lang.String MIMETYPE_VIDEO_AVC = "video/avc";
+    field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_AVC = "video/dolby-avc";
+    field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_HEVC = "video/dolby-hevc";
     field public static final java.lang.String MIMETYPE_VIDEO_H263 = "video/3gpp";
     field public static final java.lang.String MIMETYPE_VIDEO_HEVC = "video/hevc";
     field public static final java.lang.String MIMETYPE_VIDEO_MPEG2 = "video/mpeg2";
@@ -22025,6 +22029,7 @@
   public static class MediaRouter.RouteInfo {
     method public android.media.MediaRouter.RouteCategory getCategory();
     method public java.lang.CharSequence getDescription();
+    method public int getDeviceType();
     method public android.media.MediaRouter.RouteGroup getGroup();
     method public android.graphics.drawable.Drawable getIconDrawable();
     method public java.lang.CharSequence getName();
@@ -22043,6 +22048,10 @@
     method public void requestSetVolume(int);
     method public void requestUpdateVolume(int);
     method public void setTag(java.lang.Object);
+    field public static final int DEVICE_TYPE_BLUETOOTH = 3; // 0x3
+    field public static final int DEVICE_TYPE_SPEAKER = 2; // 0x2
+    field public static final int DEVICE_TYPE_TV = 1; // 0x1
+    field public static final int DEVICE_TYPE_UNKNOWN = 0; // 0x0
     field public static final int PLAYBACK_TYPE_LOCAL = 0; // 0x0
     field public static final int PLAYBACK_TYPE_REMOTE = 1; // 0x1
     field public static final int PLAYBACK_VOLUME_FIXED = 0; // 0x0
@@ -30085,7 +30094,9 @@
     method public boolean isSystemUser();
     method public boolean isUserAGoat();
     method public boolean isUserRunning(android.os.UserHandle);
+    method public boolean isUserRunningAndLocked();
     method public boolean isUserRunningAndLocked(android.os.UserHandle);
+    method public boolean isUserRunningAndUnlocked();
     method public boolean isUserRunningAndUnlocked(android.os.UserHandle);
     method public boolean isUserRunningOrStopping(android.os.UserHandle);
     method public deprecated boolean setRestrictionsChallenge(java.lang.String);
@@ -37487,6 +37498,7 @@
     field public static final java.lang.String KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL = "voicemail_notification_persistent_bool";
     field public static final java.lang.String KEY_VOICE_PRIVACY_DISABLE_UI_BOOL = "voice_privacy_disable_ui_bool";
     field public static final java.lang.String KEY_VOLTE_REPLACEMENT_RAT_INT = "volte_replacement_rat_int";
+    field public static final java.lang.String KEY_VVM_CELLULAR_DATA_REQUIRED_BOOLEAN = "vvm_cellular_data_required";
     field public static final java.lang.String KEY_VVM_DESTINATION_NUMBER_STRING = "vvm_destination_number_string";
     field public static final java.lang.String KEY_VVM_PORT_NUMBER_INT = "vvm_port_number_int";
     field public static final java.lang.String KEY_VVM_TYPE_STRING = "vvm_type_string";
@@ -38571,8 +38583,8 @@
     method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public void clearWallpaper();
     method public android.content.Context createConfigurationContext(android.content.res.Configuration);
-    method public android.content.Context createCredentialEncryptedContext(android.content.Context);
-    method public android.content.Context createDeviceEncryptedContext(android.content.Context);
+    method public android.content.Context createCredentialEncryptedStorageContext();
+    method public android.content.Context createDeviceEncryptedStorageContext();
     method public android.content.Context createDisplayContext(android.view.Display);
     method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public java.lang.String[] databaseList();
@@ -38621,8 +38633,8 @@
     method public int getWallpaperDesiredMinimumHeight();
     method public int getWallpaperDesiredMinimumWidth();
     method public void grantUriPermission(java.lang.String, android.net.Uri, int);
-    method public boolean isCredentialEncrypted();
-    method public boolean isDeviceEncrypted();
+    method public boolean isCredentialEncryptedStorage();
+    method public boolean isDeviceEncryptedStorage();
     method public java.io.FileInputStream openFileInput(java.lang.String) throws java.io.FileNotFoundException;
     method public java.io.FileOutputStream openFileOutput(java.lang.String, int) throws java.io.FileNotFoundException;
     method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory);
@@ -42577,6 +42589,7 @@
     field public static final int AXIS_RX = 12; // 0xc
     field public static final int AXIS_RY = 13; // 0xd
     field public static final int AXIS_RZ = 14; // 0xe
+    field public static final int AXIS_SCROLL = 26; // 0x1a
     field public static final int AXIS_SIZE = 3; // 0x3
     field public static final int AXIS_THROTTLE = 19; // 0x13
     field public static final int AXIS_TILT = 25; // 0x19
@@ -42885,7 +42898,6 @@
     method public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo();
     method public void createContextMenu(android.view.ContextMenu);
     method public void destroyDrawingCache();
-    method public final boolean didLayoutParamsChange();
     method public android.view.WindowInsets dispatchApplyWindowInsets(android.view.WindowInsets);
     method public void dispatchConfigurationChanged(android.content.res.Configuration);
     method public void dispatchDisplayHint(int);
@@ -43111,7 +43123,6 @@
     method public boolean isOpaque();
     method protected boolean isPaddingOffsetRequired();
     method public boolean isPaddingRelative();
-    method public final boolean isPartialLayoutRequested();
     method public boolean isPressed();
     method public boolean isSaveEnabled();
     method public boolean isSaveFromParentEnabled();
@@ -43723,7 +43734,6 @@
     method protected void dispatchThawSelfOnly(android.util.SparseArray<android.os.Parcelable>);
     method protected boolean drawChild(android.graphics.Canvas, android.view.View, long);
     method public void endViewTransition(android.view.View);
-    method public int findDependentLayoutAxes(android.view.View, int);
     method public android.view.View focusSearch(android.view.View, int);
     method public void focusableViewAvailable(android.view.View);
     method public boolean gatherTransparentRegion(android.graphics.Region);
@@ -43790,8 +43800,6 @@
     method public void requestChildFocus(android.view.View, android.view.View);
     method public boolean requestChildRectangleOnScreen(android.view.View, android.graphics.Rect, boolean);
     method public void requestDisallowInterceptTouchEvent(boolean);
-    method public void requestLayoutForChild(android.view.View);
-    method public void requestPartialLayoutForChild(android.view.View);
     method public boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
     method public void requestTransparentRegion(android.view.View);
     method public void scheduleLayoutAnimation();
@@ -43906,7 +43914,6 @@
     method public abstract void childHasTransientStateChanged(android.view.View, boolean);
     method public abstract void clearChildFocus(android.view.View);
     method public abstract void createContextMenu(android.view.ContextMenu);
-    method public abstract int findDependentLayoutAxes(android.view.View, int);
     method public abstract android.view.View focusSearch(android.view.View, int);
     method public abstract void focusableViewAvailable(android.view.View);
     method public abstract boolean getChildVisibleRect(android.view.View, android.graphics.Rect, android.graphics.Point);
@@ -43936,16 +43943,12 @@
     method public abstract void requestDisallowInterceptTouchEvent(boolean);
     method public abstract void requestFitSystemWindows();
     method public abstract void requestLayout();
-    method public abstract void requestLayoutForChild(android.view.View);
     method public abstract boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
     method public abstract void requestTransparentRegion(android.view.View);
     method public abstract boolean showContextMenuForChild(android.view.View);
     method public abstract boolean showContextMenuForChild(android.view.View, float, float);
     method public abstract android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback);
     method public abstract android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback, int);
-    field public static final int FLAG_LAYOUT_AXIS_ANY = 3; // 0x3
-    field public static final int FLAG_LAYOUT_AXIS_HORIZONTAL = 1; // 0x1
-    field public static final int FLAG_LAYOUT_AXIS_VERTICAL = 2; // 0x2
   }
 
   public class ViewPropertyAnimator {
@@ -44176,6 +44179,7 @@
     method public abstract void setContentView(int);
     method public abstract void setContentView(android.view.View);
     method public abstract void setContentView(android.view.View, android.view.ViewGroup.LayoutParams);
+    method public abstract void setDecorCaptionShade(int);
     method protected void setDefaultWindowFormat(int);
     method public void setDimAmount(float);
     method public void setDisableWallpaperTouchEvents(boolean);
@@ -44197,6 +44201,8 @@
     method public void setMediaController(android.media.session.MediaController);
     method public abstract void setNavigationBarColor(int);
     method public void setReenterTransition(android.transition.Transition);
+    method public abstract void setResizingCaptionDrawable(android.graphics.drawable.Drawable);
+    method public final void setRestrictedCaptionAreaListener(android.view.Window.RestrictedCaptionAreaListener);
     method public void setReturnTransition(android.transition.Transition);
     method public void setSharedElementEnterTransition(android.transition.Transition);
     method public void setSharedElementExitTransition(android.transition.Transition);
@@ -44225,6 +44231,9 @@
     method public abstract void takeKeyEvents(boolean);
     method public abstract void takeSurface(android.view.SurfaceHolder.Callback2);
     method public abstract void togglePanel(int, android.view.KeyEvent);
+    field public static final int DECOR_CAPTION_SHADE_AUTO = 0; // 0x0
+    field public static final int DECOR_CAPTION_SHADE_DARK = 2; // 0x2
+    field public static final int DECOR_CAPTION_SHADE_LIGHT = 1; // 0x1
     field protected static final deprecated int DEFAULT_FEATURES = 65; // 0x41
     field public static final int FEATURE_ACTION_BAR = 8; // 0x8
     field public static final int FEATURE_ACTION_BAR_OVERLAY = 9; // 0x9
@@ -44279,6 +44288,10 @@
     method public abstract android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback, int);
   }
 
+  public static abstract interface Window.RestrictedCaptionAreaListener {
+    method public abstract void onRestrictedCaptionAreaChanged(android.graphics.Rect);
+  }
+
   public final class WindowAnimationFrameStats extends android.view.FrameStats implements android.os.Parcelable {
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
@@ -45545,7 +45558,8 @@
     method public java.lang.String getExtraValue();
     method public java.lang.String getExtraValueOf(java.lang.String);
     method public int getIconResId();
-    method public java.lang.String getLocale();
+    method public java.lang.String getLanguageTag();
+    method public deprecated java.lang.String getLocale();
     method public java.lang.String getMode();
     method public int getNameResId();
     method public boolean isAsciiCapable();
@@ -45560,6 +45574,7 @@
     method public android.view.inputmethod.InputMethodSubtype build();
     method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setIsAsciiCapable(boolean);
     method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setIsAuxiliary(boolean);
+    method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setLanguageTag(java.lang.String);
     method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setOverridesImplicitlyEnabledSubtype(boolean);
     method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeExtraValue(java.lang.String);
     method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeIconResId(int);
@@ -45623,7 +45638,8 @@
     method public java.lang.CharSequence getDisplayName(android.content.Context, java.lang.String, android.content.pm.ApplicationInfo);
     method public java.lang.String getExtraValue();
     method public java.lang.String getExtraValueOf(java.lang.String);
-    method public java.lang.String getLocale();
+    method public java.lang.String getLanguageTag();
+    method public deprecated java.lang.String getLocale();
     method public int getNameResId();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.view.textservice.SpellCheckerSubtype> CREATOR;
diff --git a/api/test-current.txt b/api/test-current.txt
index fe902bd..9f03c8c 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -728,6 +728,7 @@
     field public static final int label = 16842753; // 0x1010001
     field public static final int labelFor = 16843718; // 0x10103c6
     field public static final int labelTextSize = 16843317; // 0x1010235
+    field public static final int languageTag = 16844041; // 0x1010509
     field public static final int largeHeap = 16843610; // 0x101035a
     field public static final int largeScreens = 16843398; // 0x1010286
     field public static final int largestWidthLimitDp = 16843622; // 0x1010366
@@ -7780,8 +7781,7 @@
     method public abstract int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public abstract deprecated void clearWallpaper() throws java.io.IOException;
     method public abstract android.content.Context createConfigurationContext(android.content.res.Configuration);
-    method public abstract android.content.Context createCredentialEncryptedContext(android.content.Context);
-    method public abstract android.content.Context createDeviceEncryptedContext(android.content.Context);
+    method public abstract android.content.Context createDeviceEncryptedStorageContext();
     method public abstract android.content.Context createDisplayContext(android.view.Display);
     method public abstract android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract java.lang.String[] databaseList();
@@ -7837,8 +7837,7 @@
     method public abstract deprecated int getWallpaperDesiredMinimumHeight();
     method public abstract deprecated int getWallpaperDesiredMinimumWidth();
     method public abstract void grantUriPermission(java.lang.String, android.net.Uri, int);
-    method public abstract boolean isCredentialEncrypted();
-    method public abstract boolean isDeviceEncrypted();
+    method public abstract boolean isDeviceEncryptedStorage();
     method public boolean isRestricted();
     method public final android.content.res.TypedArray obtainStyledAttributes(int[]);
     method public final android.content.res.TypedArray obtainStyledAttributes(int, int[]) throws android.content.res.Resources.NotFoundException;
@@ -7970,8 +7969,7 @@
     method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public deprecated void clearWallpaper() throws java.io.IOException;
     method public android.content.Context createConfigurationContext(android.content.res.Configuration);
-    method public android.content.Context createCredentialEncryptedContext(android.content.Context);
-    method public android.content.Context createDeviceEncryptedContext(android.content.Context);
+    method public android.content.Context createDeviceEncryptedStorageContext();
     method public android.content.Context createDisplayContext(android.view.Display);
     method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public java.lang.String[] databaseList();
@@ -8021,8 +8019,7 @@
     method public deprecated int getWallpaperDesiredMinimumHeight();
     method public deprecated int getWallpaperDesiredMinimumWidth();
     method public void grantUriPermission(java.lang.String, android.net.Uri, int);
-    method public boolean isCredentialEncrypted();
-    method public boolean isDeviceEncrypted();
+    method public boolean isDeviceEncryptedStorage();
     method public java.io.FileInputStream openFileInput(java.lang.String) throws java.io.FileNotFoundException;
     method public java.io.FileOutputStream openFileOutput(java.lang.String, int) throws java.io.FileNotFoundException;
     method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory);
@@ -11545,6 +11542,7 @@
     field public static final int PRIVATE = 34; // 0x22
     field public static final int RAW10 = 37; // 0x25
     field public static final int RAW12 = 38; // 0x26
+    field public static final int RAW_PRIVATE = 36; // 0x24
     field public static final int RAW_SENSOR = 32; // 0x20
     field public static final int RGB_565 = 4; // 0x4
     field public static final int UNKNOWN = 0; // 0x0
@@ -20305,6 +20303,8 @@
     field public static final java.lang.String MIMETYPE_TEXT_CEA_608 = "text/cea-608";
     field public static final java.lang.String MIMETYPE_TEXT_VTT = "text/vtt";
     field public static final java.lang.String MIMETYPE_VIDEO_AVC = "video/avc";
+    field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_AVC = "video/dolby-avc";
+    field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_HEVC = "video/dolby-hevc";
     field public static final java.lang.String MIMETYPE_VIDEO_H263 = "video/3gpp";
     field public static final java.lang.String MIMETYPE_VIDEO_HEVC = "video/hevc";
     field public static final java.lang.String MIMETYPE_VIDEO_MPEG2 = "video/mpeg2";
@@ -20736,6 +20736,7 @@
   public static class MediaRouter.RouteInfo {
     method public android.media.MediaRouter.RouteCategory getCategory();
     method public java.lang.CharSequence getDescription();
+    method public int getDeviceType();
     method public android.media.MediaRouter.RouteGroup getGroup();
     method public android.graphics.drawable.Drawable getIconDrawable();
     method public java.lang.CharSequence getName();
@@ -20754,6 +20755,10 @@
     method public void requestSetVolume(int);
     method public void requestUpdateVolume(int);
     method public void setTag(java.lang.Object);
+    field public static final int DEVICE_TYPE_BLUETOOTH = 3; // 0x3
+    field public static final int DEVICE_TYPE_SPEAKER = 2; // 0x2
+    field public static final int DEVICE_TYPE_TV = 1; // 0x1
+    field public static final int DEVICE_TYPE_UNKNOWN = 0; // 0x0
     field public static final int PLAYBACK_TYPE_LOCAL = 0; // 0x0
     field public static final int PLAYBACK_TYPE_REMOTE = 1; // 0x1
     field public static final int PLAYBACK_VOLUME_FIXED = 0; // 0x0
@@ -28095,7 +28100,9 @@
     method public boolean isSystemUser();
     method public boolean isUserAGoat();
     method public boolean isUserRunning(android.os.UserHandle);
+    method public boolean isUserRunningAndLocked();
     method public boolean isUserRunningAndLocked(android.os.UserHandle);
+    method public boolean isUserRunningAndUnlocked();
     method public boolean isUserRunningAndUnlocked(android.os.UserHandle);
     method public boolean isUserRunningOrStopping(android.os.UserHandle);
     method public deprecated boolean setRestrictionsChallenge(java.lang.String);
@@ -35220,6 +35227,7 @@
     field public static final java.lang.String KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL = "voicemail_notification_persistent_bool";
     field public static final java.lang.String KEY_VOICE_PRIVACY_DISABLE_UI_BOOL = "voice_privacy_disable_ui_bool";
     field public static final java.lang.String KEY_VOLTE_REPLACEMENT_RAT_INT = "volte_replacement_rat_int";
+    field public static final java.lang.String KEY_VVM_CELLULAR_DATA_REQUIRED_BOOLEAN = "vvm_cellular_data_required";
     field public static final java.lang.String KEY_VVM_DESTINATION_NUMBER_STRING = "vvm_destination_number_string";
     field public static final java.lang.String KEY_VVM_PORT_NUMBER_INT = "vvm_port_number_int";
     field public static final java.lang.String KEY_VVM_TYPE_STRING = "vvm_type_string";
@@ -36250,8 +36258,7 @@
     method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public void clearWallpaper();
     method public android.content.Context createConfigurationContext(android.content.res.Configuration);
-    method public android.content.Context createCredentialEncryptedContext(android.content.Context);
-    method public android.content.Context createDeviceEncryptedContext(android.content.Context);
+    method public android.content.Context createDeviceEncryptedStorageContext();
     method public android.content.Context createDisplayContext(android.view.Display);
     method public android.content.Context createPackageContext(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public java.lang.String[] databaseList();
@@ -36300,8 +36307,7 @@
     method public int getWallpaperDesiredMinimumHeight();
     method public int getWallpaperDesiredMinimumWidth();
     method public void grantUriPermission(java.lang.String, android.net.Uri, int);
-    method public boolean isCredentialEncrypted();
-    method public boolean isDeviceEncrypted();
+    method public boolean isDeviceEncryptedStorage();
     method public java.io.FileInputStream openFileInput(java.lang.String) throws java.io.FileNotFoundException;
     method public java.io.FileOutputStream openFileOutput(java.lang.String, int) throws java.io.FileNotFoundException;
     method public android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase.CursorFactory);
@@ -40247,6 +40253,7 @@
     field public static final int AXIS_RX = 12; // 0xc
     field public static final int AXIS_RY = 13; // 0xd
     field public static final int AXIS_RZ = 14; // 0xe
+    field public static final int AXIS_SCROLL = 26; // 0x1a
     field public static final int AXIS_SIZE = 3; // 0x3
     field public static final int AXIS_THROTTLE = 19; // 0x13
     field public static final int AXIS_TILT = 25; // 0x19
@@ -40555,7 +40562,6 @@
     method public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo();
     method public void createContextMenu(android.view.ContextMenu);
     method public void destroyDrawingCache();
-    method public final boolean didLayoutParamsChange();
     method public android.view.WindowInsets dispatchApplyWindowInsets(android.view.WindowInsets);
     method public void dispatchConfigurationChanged(android.content.res.Configuration);
     method public void dispatchDisplayHint(int);
@@ -40781,7 +40787,6 @@
     method public boolean isOpaque();
     method protected boolean isPaddingOffsetRequired();
     method public boolean isPaddingRelative();
-    method public final boolean isPartialLayoutRequested();
     method public boolean isPressed();
     method public boolean isSaveEnabled();
     method public boolean isSaveFromParentEnabled();
@@ -41393,7 +41398,6 @@
     method protected void dispatchThawSelfOnly(android.util.SparseArray<android.os.Parcelable>);
     method protected boolean drawChild(android.graphics.Canvas, android.view.View, long);
     method public void endViewTransition(android.view.View);
-    method public int findDependentLayoutAxes(android.view.View, int);
     method public android.view.View focusSearch(android.view.View, int);
     method public void focusableViewAvailable(android.view.View);
     method public boolean gatherTransparentRegion(android.graphics.Region);
@@ -41460,8 +41464,6 @@
     method public void requestChildFocus(android.view.View, android.view.View);
     method public boolean requestChildRectangleOnScreen(android.view.View, android.graphics.Rect, boolean);
     method public void requestDisallowInterceptTouchEvent(boolean);
-    method public void requestLayoutForChild(android.view.View);
-    method public void requestPartialLayoutForChild(android.view.View);
     method public boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
     method public void requestTransparentRegion(android.view.View);
     method public void scheduleLayoutAnimation();
@@ -41576,7 +41578,6 @@
     method public abstract void childHasTransientStateChanged(android.view.View, boolean);
     method public abstract void clearChildFocus(android.view.View);
     method public abstract void createContextMenu(android.view.ContextMenu);
-    method public abstract int findDependentLayoutAxes(android.view.View, int);
     method public abstract android.view.View focusSearch(android.view.View, int);
     method public abstract void focusableViewAvailable(android.view.View);
     method public abstract boolean getChildVisibleRect(android.view.View, android.graphics.Rect, android.graphics.Point);
@@ -41606,16 +41607,12 @@
     method public abstract void requestDisallowInterceptTouchEvent(boolean);
     method public abstract void requestFitSystemWindows();
     method public abstract void requestLayout();
-    method public abstract void requestLayoutForChild(android.view.View);
     method public abstract boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
     method public abstract void requestTransparentRegion(android.view.View);
     method public abstract boolean showContextMenuForChild(android.view.View);
     method public abstract boolean showContextMenuForChild(android.view.View, float, float);
     method public abstract android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback);
     method public abstract android.view.ActionMode startActionModeForChild(android.view.View, android.view.ActionMode.Callback, int);
-    field public static final int FLAG_LAYOUT_AXIS_ANY = 3; // 0x3
-    field public static final int FLAG_LAYOUT_AXIS_HORIZONTAL = 1; // 0x1
-    field public static final int FLAG_LAYOUT_AXIS_VERTICAL = 2; // 0x2
   }
 
   public class ViewPropertyAnimator {
@@ -41846,6 +41843,7 @@
     method public abstract void setContentView(int);
     method public abstract void setContentView(android.view.View);
     method public abstract void setContentView(android.view.View, android.view.ViewGroup.LayoutParams);
+    method public abstract void setDecorCaptionShade(int);
     method protected void setDefaultWindowFormat(int);
     method public void setDimAmount(float);
     method public void setElevation(float);
@@ -41866,6 +41864,8 @@
     method public void setMediaController(android.media.session.MediaController);
     method public abstract void setNavigationBarColor(int);
     method public void setReenterTransition(android.transition.Transition);
+    method public abstract void setResizingCaptionDrawable(android.graphics.drawable.Drawable);
+    method public final void setRestrictedCaptionAreaListener(android.view.Window.RestrictedCaptionAreaListener);
     method public void setReturnTransition(android.transition.Transition);
     method public void setSharedElementEnterTransition(android.transition.Transition);
     method public void setSharedElementExitTransition(android.transition.Transition);
@@ -41894,6 +41894,9 @@
     method public abstract void takeKeyEvents(boolean);
     method public abstract void takeSurface(android.view.SurfaceHolder.Callback2);
     method public abstract void togglePanel(int, android.view.KeyEvent);
+    field public static final int DECOR_CAPTION_SHADE_AUTO = 0; // 0x0
+    field public static final int DECOR_CAPTION_SHADE_DARK = 2; // 0x2
+    field public static final int DECOR_CAPTION_SHADE_LIGHT = 1; // 0x1
     field protected static final deprecated int DEFAULT_FEATURES = 65; // 0x41
     field public static final int FEATURE_ACTION_BAR = 8; // 0x8
     field public static final int FEATURE_ACTION_BAR_OVERLAY = 9; // 0x9
@@ -41948,6 +41951,10 @@
     method public abstract android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback, int);
   }
 
+  public static abstract interface Window.RestrictedCaptionAreaListener {
+    method public abstract void onRestrictedCaptionAreaChanged(android.graphics.Rect);
+  }
+
   public final class WindowAnimationFrameStats extends android.view.FrameStats implements android.os.Parcelable {
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
@@ -43212,7 +43219,8 @@
     method public java.lang.String getExtraValue();
     method public java.lang.String getExtraValueOf(java.lang.String);
     method public int getIconResId();
-    method public java.lang.String getLocale();
+    method public java.lang.String getLanguageTag();
+    method public deprecated java.lang.String getLocale();
     method public java.lang.String getMode();
     method public int getNameResId();
     method public boolean isAsciiCapable();
@@ -43227,6 +43235,7 @@
     method public android.view.inputmethod.InputMethodSubtype build();
     method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setIsAsciiCapable(boolean);
     method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setIsAuxiliary(boolean);
+    method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setLanguageTag(java.lang.String);
     method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setOverridesImplicitlyEnabledSubtype(boolean);
     method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeExtraValue(java.lang.String);
     method public android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder setSubtypeIconResId(int);
@@ -43290,7 +43299,8 @@
     method public java.lang.CharSequence getDisplayName(android.content.Context, java.lang.String, android.content.pm.ApplicationInfo);
     method public java.lang.String getExtraValue();
     method public java.lang.String getExtraValueOf(java.lang.String);
-    method public java.lang.String getLocale();
+    method public java.lang.String getLanguageTag();
+    method public deprecated java.lang.String getLocale();
     method public int getNameResId();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.view.textservice.SpellCheckerSubtype> CREATOR;
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index f3242a7..4cb2619 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2718,10 +2718,10 @@
             reply.writeNoException();
             return true;
         }
-        case REMOVE_STACK_TRANSACTION: {
+        case MOVE_TASKS_TO_FULLSCREEN_STACK_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             final int stackId = data.readInt();
-            removeStack(stackId);
+            moveTasksToFullscreenStack(stackId);
             reply.writeNoException();
             return true;
         }
@@ -6358,12 +6358,12 @@
     }
 
     @Override
-    public void removeStack(int stackId) throws RemoteException {
+    public void moveTasksToFullscreenStack(int fromStackId) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
-        data.writeInt(stackId);
-        mRemote.transact(REMOVE_STACK_TRANSACTION, data, reply, 0);
+        data.writeInt(fromStackId);
+        mRemote.transact(MOVE_TASKS_TO_FULLSCREEN_STACK_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
         reply.recycle();
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java
index 1f378da..175b979 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import android.annotation.SystemApi;
+import android.os.Build;
 import android.os.Bundle;
 
 /**
@@ -28,15 +29,28 @@
 @SystemApi
 public class BroadcastOptions {
     private long mTemporaryAppWhitelistDuration;
+    private int mMinManifestReceiverApiLevel = 0;
+    private int mMaxManifestReceiverApiLevel = Build.VERSION_CODES.CUR_DEVELOPMENT;
 
     /**
      * How long to temporarily put an app on the power whitelist when executing this broadcast
      * to it.
-     * @hide
      */
-    public static final String KEY_TEMPORARY_APP_WHITELIST_DURATION
+    static final String KEY_TEMPORARY_APP_WHITELIST_DURATION
             = "android:broadcast.temporaryAppWhitelistDuration";
 
+    /**
+     * Corresponds to {@link #setMinManifestReceiverApiLevel}.
+     */
+    static final String KEY_MIN_MANIFEST_RECEIVER_API_LEVEL
+            = "android:broadcast.minManifestReceiverApiLevel";
+
+    /**
+     * Corresponds to {@link #setMaxManifestReceiverApiLevel}.
+     */
+    static final String KEY_MAX_MANIFEST_RECEIVER_API_LEVEL
+            = "android:broadcast.maxManifestReceiverApiLevel";
+
     public static BroadcastOptions makeBasic() {
         BroadcastOptions opts = new BroadcastOptions();
         return opts;
@@ -48,6 +62,9 @@
     /** @hide */
     public BroadcastOptions(Bundle opts) {
         mTemporaryAppWhitelistDuration = opts.getLong(KEY_TEMPORARY_APP_WHITELIST_DURATION);
+        mMinManifestReceiverApiLevel = opts.getInt(KEY_MIN_MANIFEST_RECEIVER_API_LEVEL, 0);
+        mMaxManifestReceiverApiLevel = opts.getInt(KEY_MAX_MANIFEST_RECEIVER_API_LEVEL,
+                Build.VERSION_CODES.CUR_DEVELOPMENT);
     }
 
     /**
@@ -68,10 +85,46 @@
     }
 
     /**
+     * Set the minimum target API level of receivers of the broadcast.  If an application
+     * is targeting an API level less than this, the broadcast will not be delivered to
+     * them.  This only applies to receivers declared in the app's AndroidManifest.xml.
+     * @hide
+     */
+    public void setMinManifestReceiverApiLevel(int apiLevel) {
+        mMinManifestReceiverApiLevel = apiLevel;
+    }
+
+    /**
+     * Return {@link #setMinManifestReceiverApiLevel}.
+     * @hide
+     */
+    public int getMinManifestReceiverApiLevel() {
+        return mMinManifestReceiverApiLevel;
+    }
+
+    /**
+     * Set the maximum target API level of receivers of the broadcast.  If an application
+     * is targeting an API level greater than this, the broadcast will not be delivered to
+     * them.  This only applies to receivers declared in the app's AndroidManifest.xml.
+     * @hide
+     */
+    public void setMaxManifestReceiverApiLevel(int apiLevel) {
+        mMaxManifestReceiverApiLevel = apiLevel;
+    }
+
+    /**
+     * Return {@link #setMaxManifestReceiverApiLevel}.
+     * @hide
+     */
+    public int getMaxManifestReceiverApiLevel() {
+        return mMaxManifestReceiverApiLevel;
+    }
+
+    /**
      * Returns the created options as a Bundle, which can be passed to
      * {@link android.content.Context#sendBroadcast(android.content.Intent)
      * Context.sendBroadcast(Intent)} and related methods.
-     * Note that the returned Bundle is still owned by the ActivityOptions
+     * Note that the returned Bundle is still owned by the BroadcastOptions
      * object; you must not modify it, but can supply it to the sendBroadcast
      * methods that take an options Bundle.
      */
@@ -80,6 +133,12 @@
         if (mTemporaryAppWhitelistDuration > 0) {
             b.putLong(KEY_TEMPORARY_APP_WHITELIST_DURATION, mTemporaryAppWhitelistDuration);
         }
+        if (mMinManifestReceiverApiLevel != 0) {
+            b.putInt(KEY_MIN_MANIFEST_RECEIVER_API_LEVEL, mMinManifestReceiverApiLevel);
+        }
+        if (mMaxManifestReceiverApiLevel != Build.VERSION_CODES.CUR_DEVELOPMENT) {
+            b.putInt(KEY_MAX_MANIFEST_RECEIVER_API_LEVEL, mMaxManifestReceiverApiLevel);
+        }
         return b.isEmpty() ? null : b;
     }
 }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 36e98f9..569ab11 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -61,6 +61,7 @@
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.storage.IMountService;
+import android.os.storage.StorageManager;
 import android.util.AndroidRuntimeException;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -1092,7 +1093,23 @@
             intent.prepareToLeaveProcess();
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, null,
-                Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, true, user.getIdentifier());
+                Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, true,
+                    user.getIdentifier());
+        } catch (RemoteException e) {
+            throw new RuntimeException("Failure from system", e);
+        }
+    }
+
+    @Override
+    @Deprecated
+    public void sendStickyBroadcastAsUser(Intent intent, UserHandle user, Bundle options) {
+        String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
+        try {
+            intent.prepareToLeaveProcess();
+            ActivityManagerNative.getDefault().broadcastIntent(
+                mMainThread.getApplicationThread(), intent, resolvedType, null,
+                Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, options, false, true,
+                user.getIdentifier());
         } catch (RemoteException e) {
             throw new RuntimeException("Failure from system", e);
         }
@@ -1744,17 +1761,21 @@
     }
 
     @Override
-    public Context createDeviceEncryptedContext(Context context) {
-        final int flags = (mFlags & ~Context.CONTEXT_STORAGE_CREDENTIAL_ENCRYPTED)
-                | Context.CONTEXT_STORAGE_DEVICE_ENCRYPTED;
+    public Context createDeviceEncryptedStorageContext() {
+        if (!StorageManager.isFileBasedEncryptionEnabled()) {
+            return null;
+        }
+
+        final int flags = (mFlags & ~Context.CONTEXT_CREDENTIAL_ENCRYPTED_STORAGE)
+                | Context.CONTEXT_DEVICE_ENCRYPTED_STORAGE;
         return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken,
                 mUser, flags, mDisplay, null, Display.INVALID_DISPLAY);
     }
 
     @Override
-    public Context createCredentialEncryptedContext(Context context) {
-        final int flags = (mFlags & ~Context.CONTEXT_STORAGE_DEVICE_ENCRYPTED)
-                | Context.CONTEXT_STORAGE_CREDENTIAL_ENCRYPTED;
+    public Context createCredentialEncryptedStorageContext() {
+        final int flags = (mFlags & ~Context.CONTEXT_DEVICE_ENCRYPTED_STORAGE)
+                | Context.CONTEXT_CREDENTIAL_ENCRYPTED_STORAGE;
         return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken,
                 mUser, flags, mDisplay, null, Display.INVALID_DISPLAY);
     }
@@ -1765,13 +1786,13 @@
     }
 
     @Override
-    public boolean isDeviceEncrypted() {
-        return (mFlags & Context.CONTEXT_STORAGE_DEVICE_ENCRYPTED) != 0;
+    public boolean isDeviceEncryptedStorage() {
+        return (mFlags & Context.CONTEXT_DEVICE_ENCRYPTED_STORAGE) != 0;
     }
 
     @Override
-    public boolean isCredentialEncrypted() {
-        return (mFlags & Context.CONTEXT_STORAGE_CREDENTIAL_ENCRYPTED) != 0;
+    public boolean isCredentialEncryptedStorage() {
+        return (mFlags & Context.CONTEXT_CREDENTIAL_ENCRYPTED_STORAGE) != 0;
     }
 
     @Override
@@ -1781,13 +1802,12 @@
 
     private File getDataDirFile() {
         if (mPackageInfo != null) {
-            if (isCredentialEncrypted()) {
+            if (isCredentialEncryptedStorage()) {
                 return mPackageInfo.getCredentialEncryptedDataDirFile();
-            } else if (isDeviceEncrypted()) {
+            } else if (isDeviceEncryptedStorage()) {
                 return mPackageInfo.getDeviceEncryptedDataDirFile();
             } else {
-                throw new RuntimeException(
-                        "Storage location is neither credential nor device encrypted");
+                return mPackageInfo.getDataDirFile();
             }
         }
         throw new RuntimeException("Not supported in system context");
@@ -1840,15 +1860,13 @@
 
         // If creator didn't specify which storage to use, use the default
         // location for application.
-        if ((flags & Context.CONTEXT_STORAGE_MASK) == 0) {
+        if ((flags & (Context.CONTEXT_CREDENTIAL_ENCRYPTED_STORAGE
+                | Context.CONTEXT_DEVICE_ENCRYPTED_STORAGE)) == 0) {
             final File dataDir = packageInfo.getDataDirFile();
             if (Objects.equals(dataDir, packageInfo.getCredentialEncryptedDataDirFile())) {
-                flags |= Context.CONTEXT_STORAGE_CREDENTIAL_ENCRYPTED;
+                flags |= Context.CONTEXT_CREDENTIAL_ENCRYPTED_STORAGE;
             } else if (Objects.equals(dataDir, packageInfo.getDeviceEncryptedDataDirFile())) {
-                flags |= Context.CONTEXT_STORAGE_DEVICE_ENCRYPTED;
-            } else {
-                throw new IllegalStateException("Storage location " + dataDir
-                        + " doesn't match either credential or device encrypted storage");
+                flags |= Context.CONTEXT_DEVICE_ENCRYPTED_STORAGE;
             }
         }
 
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 22a2d64..eb8d6bc 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -541,7 +541,7 @@
 
     public void suppressResizeConfigChanges(boolean suppress) throws RemoteException;
 
-    public void removeStack(int stackId) throws RemoteException;
+    public void moveTasksToFullscreenStack(int fromStackId) throws RemoteException;
 
     public int getAppStartMode(int uid, String packageName) throws RemoteException;
 
@@ -906,7 +906,7 @@
     int REPORT_SIZE_CONFIGURATIONS = IBinder.FIRST_CALL_TRANSACTION + 345;
     int MOVE_TASK_TO_DOCKED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 346;
     int SUPPRESS_RESIZE_CONFIG_CHANGES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 347;
-    int REMOVE_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 348;
+    int MOVE_TASKS_TO_FULLSCREEN_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 348;
     int MOVE_TOP_ACTIVITY_TO_PINNED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 349;
     int GET_APP_START_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 350;
     int UNLOCK_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 351;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 099a5fe..620ab50 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -46,6 +46,7 @@
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.SparseArray;
 import android.util.TypedValue;
 import android.view.Gravity;
 import android.view.NotificationHeaderView;
@@ -64,6 +65,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * A class that represents how a persistent notification is to be presented to
@@ -1693,11 +1695,21 @@
         bigContentView = null;
         headsUpContentView = null;
         mLargeIcon = null;
-        if (extras != null) {
-            extras.remove(Notification.EXTRA_LARGE_ICON);
-            extras.remove(Notification.EXTRA_LARGE_ICON_BIG);
-            extras.remove(Notification.EXTRA_PICTURE);
-            extras.remove(Notification.EXTRA_BIG_TEXT);
+        if (extras != null && !extras.isEmpty()) {
+            final Set<String> keyset = extras.keySet();
+            final int N = keyset.size();
+            final String[] keys = keyset.toArray(new String[N]);
+            for (int i=0; i<N; i++) {
+                final String key = keys[i];
+                final Object obj = extras.get(key);
+                if (obj != null &&
+                    (  obj instanceof Parcelable
+                    || obj instanceof Parcelable[]
+                    || obj instanceof SparseArray
+                    || obj instanceof ArrayList)) {
+                    extras.remove(key);
+                }
+            }
         }
     }
 
@@ -3318,7 +3330,6 @@
             if (mN.color != COLOR_DEFAULT) {
                 button.setTextColor(R.id.action0, mN.color);
             }
-            processLegacyAction(action, button);
             return button;
         }
 
@@ -3330,14 +3341,6 @@
             return getColorUtil() != null;
         }
 
-        private void processLegacyAction(Action action, RemoteViews button) {
-            if (!isLegacy() || getColorUtil().isGrayscaleIcon(mContext, action.getIcon())) {
-                button.setTextViewCompoundDrawablesRelativeColorFilter(R.id.action0, 0,
-                        mContext.getColor(R.color.notification_action_color_filter),
-                        PorterDuff.Mode.MULTIPLY);
-            }
-        }
-
         private CharSequence processLegacyText(CharSequence charSequence) {
             if (isLegacy()) {
                 return getColorUtil().invertCharSequenceColors(charSequence);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 6b900a8..93f137f 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -89,7 +89,6 @@
     private final Context mContext;
     private final IDevicePolicyManager mService;
 
-    // TODO Use it everywhere.
     private static final String REMOTE_EXCEPTION_MESSAGE =
             "Failed to talk with device policy manager service";
 
@@ -841,7 +840,7 @@
             try {
                 return mService.isAdminActive(admin, userId);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return false;
@@ -856,7 +855,7 @@
             try {
                 return mService.isRemovingAdmin(admin, userId);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return false;
@@ -881,7 +880,7 @@
             try {
                 return mService.getActiveAdmins(userId);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return null;
@@ -897,7 +896,7 @@
             try {
                 return mService.packageHasActiveAdmins(packageName, myUserId());
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return false;
@@ -914,7 +913,7 @@
             try {
                 mService.removeActiveAdmin(admin, myUserId());
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -933,7 +932,7 @@
             try {
                 return mService.hasGrantedPolicy(admin, usesPolicy, myUserId());
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return false;
@@ -1034,7 +1033,7 @@
             try {
                 mService.setPasswordQuality(admin, quality);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -1055,7 +1054,7 @@
             try {
                 return mService.getPasswordQuality(admin, userHandle);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return PASSWORD_QUALITY_UNSPECIFIED;
@@ -1087,7 +1086,7 @@
             try {
                 mService.setPasswordMinimumLength(admin, length);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -1108,7 +1107,7 @@
             try {
                 return mService.getPasswordMinimumLength(admin, userHandle);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return 0;
@@ -1141,7 +1140,7 @@
             try {
                 mService.setPasswordMinimumUpperCase(admin, length);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -1169,7 +1168,7 @@
             try {
                 return mService.getPasswordMinimumUpperCase(admin, userHandle);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return 0;
@@ -1202,7 +1201,7 @@
             try {
                 mService.setPasswordMinimumLowerCase(admin, length);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -1230,7 +1229,7 @@
             try {
                 return mService.getPasswordMinimumLowerCase(admin, userHandle);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return 0;
@@ -1262,7 +1261,7 @@
             try {
                 mService.setPasswordMinimumLetters(admin, length);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -1288,7 +1287,7 @@
             try {
                 return mService.getPasswordMinimumLetters(admin, userHandle);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return 0;
@@ -1320,7 +1319,7 @@
             try {
                 mService.setPasswordMinimumNumeric(admin, length);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -1347,7 +1346,7 @@
             try {
                 return mService.getPasswordMinimumNumeric(admin, userHandle);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return 0;
@@ -1379,7 +1378,7 @@
             try {
                 mService.setPasswordMinimumSymbols(admin, length);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -1405,7 +1404,7 @@
             try {
                 return mService.getPasswordMinimumSymbols(admin, userHandle);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return 0;
@@ -1437,7 +1436,7 @@
             try {
                 mService.setPasswordMinimumNonLetter(admin, length);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -1464,7 +1463,7 @@
             try {
                 return mService.getPasswordMinimumNonLetter(admin, userHandle);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return 0;
@@ -1497,7 +1496,7 @@
             try {
                 mService.setPasswordHistoryLength(admin, length);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -1529,7 +1528,7 @@
             try {
                 mService.setPasswordExpirationTimeout(admin, timeout);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -1548,7 +1547,7 @@
             try {
                 return mService.getPasswordExpirationTimeout(admin, myUserId());
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return 0;
@@ -1569,7 +1568,7 @@
             try {
                 return mService.getPasswordExpiration(admin, myUserId());
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return 0;
@@ -1592,7 +1591,7 @@
             try {
                 return mService.getPasswordHistoryLength(admin, userHandle);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return 0;
@@ -1625,7 +1624,7 @@
             try {
                 return mService.isActivePasswordSufficient(myUserId());
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return false;
@@ -1644,7 +1643,7 @@
             try {
                 return mService.getCurrentFailedPasswordAttempts(myUserId());
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return -1;
@@ -1661,7 +1660,7 @@
             try {
                 return mService.getDoNotAskCredentialsOnBoot();
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed to call getDoNotAskCredentialsOnBoot()", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return false;
@@ -1691,7 +1690,7 @@
             try {
                 mService.setMaximumFailedPasswordsForWipe(admin, num);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -1713,7 +1712,7 @@
             try {
                 return mService.getMaximumFailedPasswordsForWipe(admin, userHandle);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return 0;
@@ -1731,7 +1730,7 @@
             try {
                 return mService.getProfileWithMinimumFailedPasswordsForWipe(userHandle);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return UserHandle.USER_NULL;
@@ -1796,7 +1795,7 @@
             try {
                 return mService.resetPassword(password, flags);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return false;
@@ -1820,7 +1819,7 @@
             try {
                 mService.setMaximumTimeToLock(admin, timeMs);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -1843,7 +1842,7 @@
             try {
                 return mService.getMaximumTimeToLock(admin, userHandle);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return 0;
@@ -1862,7 +1861,7 @@
             try {
                 mService.lockNow();
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -1899,7 +1898,7 @@
             try {
                 mService.wipeData(flags);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -1969,7 +1968,7 @@
                 }
                 return mService.setGlobalProxy(admin, hostSpec, exclSpec);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return null;
@@ -1997,7 +1996,7 @@
             try {
                 mService.setRecommendedGlobalProxy(admin, proxyInfo);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -2013,7 +2012,7 @@
             try {
                 return mService.getGlobalProxyAdmin(myUserId());
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return null;
@@ -2143,7 +2142,7 @@
             try {
                 return mService.setStorageEncryption(admin, encrypt);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return ENCRYPTION_STATUS_UNSUPPORTED;
@@ -2163,7 +2162,7 @@
             try {
                 return mService.getStorageEncryption(admin, myUserId());
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return false;
@@ -2198,7 +2197,7 @@
             try {
                 return mService.getStorageEncryptionStatus(userHandle);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return ENCRYPTION_STATUS_UNSUPPORTED;
@@ -2219,7 +2218,7 @@
             try {
                 return mService.installCaCert(admin, certBuffer);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return false;
@@ -2240,7 +2239,7 @@
             } catch (CertificateException e) {
                 Log.w(TAG, "Unable to parse certificate", e);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -2268,7 +2267,7 @@
                     }
                 }
             } catch (RemoteException re) {
-                Log.w(TAG, "Failed talking with device policy service", re);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             }
         }
         return certs;
@@ -2287,7 +2286,7 @@
                 mService.uninstallCaCerts(admin, new TrustedCertificateStore().userAliases()
                         .toArray(new String[0]));
             } catch (RemoteException re) {
-                Log.w(TAG, "Failed talking with device policy service", re);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             }
         }
     }
@@ -2305,7 +2304,7 @@
                 mService.enforceCanManageCaCerts(admin);
                 return getCaCertAlias(certBuffer) != null;
             } catch (RemoteException re) {
-                Log.w(TAG, "Failed talking with device policy service", re);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             } catch (CertificateException ce) {
                 Log.w(TAG, "Could not parse certificate", ce);
             }
@@ -2333,7 +2332,7 @@
                     .getKeySpec(privKey, PKCS8EncodedKeySpec.class).getEncoded();
             return mService.installKeyPair(admin, pkcs8Key, pemCert, alias);
         } catch (RemoteException e) {
-            Log.w(TAG, "Failed talking with device policy service", e);
+            Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
         } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
             Log.w(TAG, "Failed to obtain private key material", e);
         } catch (CertificateException | IOException e) {
@@ -2391,7 +2390,7 @@
             try {
                 mService.setCertInstallerPackage(admin, installerPackage);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -2409,7 +2408,7 @@
             try {
                 return mService.getCertInstallerPackage(admin);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return null;
@@ -2434,7 +2433,7 @@
             try {
                 mService.setCameraDisabled(admin, disabled);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -2455,7 +2454,7 @@
             try {
                 return mService.getCameraDisabled(admin, userHandle);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return false;
@@ -2492,7 +2491,7 @@
             try {
                 mService.setScreenCaptureDisabled(admin, disabled);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -2513,7 +2512,7 @@
             try {
                 return mService.getScreenCaptureDisabled(admin, userHandle);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return false;
@@ -2536,7 +2535,7 @@
             try {
                 mService.setAutoTimeRequired(admin, required);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -2549,7 +2548,7 @@
             try {
                 return mService.getAutoTimeRequired();
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return false;
@@ -2590,7 +2589,7 @@
             try {
                 mService.setKeyguardDisabledFeatures(admin, which);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -2613,7 +2612,7 @@
             try {
                 return mService.getKeyguardDisabledFeatures(admin, userHandle);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return KEYGUARD_DISABLE_FEATURES_NONE;
@@ -2628,7 +2627,7 @@
             try {
                 mService.setActiveAdmin(policyReceiver, refreshing, userHandle);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -2659,10 +2658,7 @@
 
         try {
             return new DeviceAdminInfo(mContext, ri);
-        } catch (XmlPullParserException e) {
-            Log.w(TAG, "Unable to parse device policy " + cn, e);
-            return null;
-        } catch (IOException e) {
+        } catch (XmlPullParserException | IOException e) {
             Log.w(TAG, "Unable to parse device policy " + cn, e);
             return null;
         }
@@ -2676,7 +2672,7 @@
             try {
                 mService.getRemoveWarning(admin, result, myUserId());
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -2691,7 +2687,7 @@
                 mService.setActivePasswordState(quality, length, letters, uppercase, lowercase,
                         numbers, symbols, nonletter, userHandle);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -2704,7 +2700,7 @@
             try {
                 mService.reportFailedPasswordAttempt(userHandle);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -2717,7 +2713,7 @@
             try {
                 mService.reportSuccessfulPasswordAttempt(userHandle);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -2770,7 +2766,7 @@
             try {
                 return mService.setDeviceOwner(who, ownerName, userId);
             } catch (RemoteException re) {
-                Log.w(TAG, "Failed to set device owner");
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             }
         }
         return false;
@@ -2852,7 +2848,7 @@
             try {
                 return mService.getDeviceOwnerComponent(callingUserOnly);
             } catch (RemoteException re) {
-                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             }
         }
         return null;
@@ -2871,7 +2867,7 @@
             try {
                 return mService.getDeviceOwnerUserId();
             } catch (RemoteException re) {
-                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             }
         }
         return UserHandle.USER_NULL;
@@ -2891,7 +2887,7 @@
             try {
                 mService.clearDeviceOwner(packageName);
             } catch (RemoteException re) {
-                Log.w(TAG, "Failed to clear device owner");
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             }
         }
     }
@@ -2933,7 +2929,7 @@
             try {
                 return mService.getDeviceOwnerName();
             } catch (RemoteException re) {
-                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             }
         }
         return null;
@@ -2986,7 +2982,6 @@
                 mService.setActiveAdmin(admin, false, myUserId);
                 return mService.setProfileOwner(admin, ownerName, myUserId);
             } catch (RemoteException re) {
-                Log.w(TAG, "Failed to set profile owner " + re);
                 throw new IllegalArgumentException("Couldn't set profile owner.", re);
             }
         }
@@ -3008,7 +3003,7 @@
             try {
                 mService.clearProfileOwner(admin);
             } catch (RemoteException re) {
-                Log.w(TAG, "Failed to clear profile owner " + admin + re);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             }
         }
     }
@@ -3022,7 +3017,7 @@
             try {
                 return mService.hasUserSetupCompleted();
             } catch (RemoteException re) {
-                Log.w(TAG, "Failed to check whether user setup has completed");
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             }
         }
         return true;
@@ -3053,7 +3048,7 @@
                 }
                 return mService.setProfileOwner(admin, ownerName, userHandle);
             } catch (RemoteException re) {
-                Log.w(TAG, "Failed to set profile owner", re);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
                 throw new IllegalArgumentException("Couldn't set profile owner.", re);
             }
         }
@@ -3076,7 +3071,7 @@
             try {
                 return mService.setDeviceOwnerLockScreenInfo(admin, info);
             } catch (RemoteException re) {
-                Log.w(TAG, "Failed talking with device policy service", re);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             }
         }
         return false;
@@ -3090,7 +3085,7 @@
             try {
                 return mService.getDeviceOwnerLockScreenInfo();
             } catch (RemoteException re) {
-                Log.w(TAG, "Failed talking with device policy service", re);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             }
         }
         return null;
@@ -3109,7 +3104,7 @@
             try {
                 mService.setProfileEnabled(admin);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -3130,7 +3125,7 @@
             try {
                 mService.setProfileName(admin, profileName);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -3151,7 +3146,7 @@
                 return profileOwner != null
                         && profileOwner.getPackageName().equals(packageName);
             } catch (RemoteException re) {
-                Log.w(TAG, "Failed to check profile owner");
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             }
         }
         return false;
@@ -3177,7 +3172,7 @@
             try {
                 return mService.getProfileOwner(userId);
             } catch (RemoteException re) {
-                Log.w(TAG, "Failed to get profile owner");
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
                 throw new IllegalArgumentException(
                         "Requested profile owner for invalid userId", re);
             }
@@ -3196,7 +3191,7 @@
             try {
                 return mService.getProfileOwnerName(Process.myUserHandle().getIdentifier());
             } catch (RemoteException re) {
-                Log.w(TAG, "Failed to get profile owner");
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
                 throw new IllegalArgumentException(
                         "Requested profile owner for invalid userId", re);
             }
@@ -3217,7 +3212,7 @@
             try {
                 return mService.getProfileOwnerName(userId);
             } catch (RemoteException re) {
-                Log.w(TAG, "Failed to get profile owner");
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
                 throw new IllegalArgumentException(
                         "Requested profile owner for invalid userId", re);
             }
@@ -3248,7 +3243,7 @@
             try {
                 mService.addPersistentPreferredActivity(admin, filter, activity);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -3269,7 +3264,7 @@
             try {
                 mService.clearPackagePersistentPreferredActivities(admin, packageName);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -3309,7 +3304,7 @@
             try {
                 mService.setApplicationRestrictions(admin, packageName, settings);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -3339,7 +3334,7 @@
             try {
                 mService.setTrustAgentConfiguration(admin, target, configuration);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -3370,7 +3365,7 @@
             try {
                 return mService.getTrustAgentConfiguration(admin, agent, userHandle);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return new ArrayList<PersistableBundle>(); // empty list
@@ -3391,7 +3386,7 @@
             try {
                 mService.setCrossProfileCallerIdDisabled(admin, disabled);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -3410,7 +3405,7 @@
             try {
                 return mService.getCrossProfileCallerIdDisabled(admin);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return false;
@@ -3427,7 +3422,7 @@
             try {
                 return mService.getCrossProfileCallerIdDisabledForUser(userHandle.getIdentifier());
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return false;
@@ -3444,7 +3439,7 @@
                 mService.startManagedQuickContact(
                         actualLookupKey, actualContactId, directoryId, originalIntent);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -3478,7 +3473,7 @@
             try {
                 mService.setBluetoothContactSharingDisabled(admin, disabled);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -3500,7 +3495,7 @@
             try {
                 return mService.getBluetoothContactSharingDisabled(admin);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return true;
@@ -3520,7 +3515,7 @@
                 return mService.getBluetoothContactSharingDisabledForUser(userHandle
                         .getIdentifier());
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return true;
@@ -3542,7 +3537,7 @@
             try {
                 mService.addCrossProfileIntentFilter(admin, filter, flags);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -3558,7 +3553,7 @@
             try {
                 mService.clearCrossProfileIntentFilters(admin);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -3590,7 +3585,7 @@
             try {
                 return mService.setPermittedAccessibilityServices(admin, packageNames);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return false;
@@ -3610,7 +3605,7 @@
             try {
                 return mService.getPermittedAccessibilityServices(admin);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return null;
@@ -3634,7 +3629,7 @@
             try {
                 return mService.getPermittedAccessibilityServicesForUser(userId);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return null;
@@ -3668,7 +3663,7 @@
             try {
                 return mService.setPermittedInputMethods(admin, packageNames);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return false;
@@ -3689,7 +3684,7 @@
             try {
                 return mService.getPermittedInputMethods(admin);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return null;
@@ -3712,7 +3707,7 @@
             try {
                 return mService.getPermittedInputMethodsForCurrentUser();
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return null;
@@ -3732,7 +3727,7 @@
             try {
                 return mService.getKeepUninstalledPackages(admin);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return null;
@@ -3755,7 +3750,7 @@
             try {
                 mService.setKeepUninstalledPackages(admin, packageNames);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -3779,7 +3774,7 @@
         try {
             return mService.createUser(admin, name);
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not create a user", re);
+            Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
         }
         return null;
     }
@@ -3818,7 +3813,7 @@
             return mService.createAndInitializeUser(admin, name, ownerName, profileOwnerComponent,
                     adminExtras);
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not create a user", re);
+            Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
         }
         return null;
     }
@@ -3835,7 +3830,7 @@
         try {
             return mService.removeUser(admin, userHandle);
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not remove user ", re);
+            Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             return false;
         }
     }
@@ -3853,7 +3848,7 @@
         try {
             return mService.switchUser(admin, userHandle);
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not switch user ", re);
+            Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             return false;
         }
     }
@@ -3876,7 +3871,7 @@
             try {
                 return mService.getApplicationRestrictions(admin, packageName);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return null;
@@ -3898,7 +3893,7 @@
             try {
                 mService.setUserRestriction(admin, key, true);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -3919,7 +3914,7 @@
             try {
                 mService.setUserRestriction(admin, key, false);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -3946,7 +3941,7 @@
             try {
                 ret = mService.getUserRestrictions(admin, userHandle);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return ret == null ? new Bundle() : ret;
@@ -3968,7 +3963,7 @@
             try {
                 return mService.setApplicationHidden(admin, packageName, hidden);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return false;
@@ -3986,7 +3981,7 @@
             try {
                 return mService.isApplicationHidden(admin, packageName);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return false;
@@ -4004,7 +3999,7 @@
             try {
                 mService.enableSystemApp(admin, packageName);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed to install package: " + packageName);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -4023,7 +4018,7 @@
             try {
                 return mService.enableSystemAppWithIntent(admin, intent);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed to install packages matching filter: " + intent);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return 0;
@@ -4050,7 +4045,7 @@
             try {
                 mService.setAccountManagementDisabled(admin, accountType, disabled);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -4078,7 +4073,7 @@
             try {
                 return mService.getAccountTypesWithManagementDisabledAsUser(userId);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
 
@@ -4109,7 +4104,7 @@
             try {
                 mService.setLockTaskPackages(admin, packages);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -4125,7 +4120,7 @@
             try {
                 return mService.getLockTaskPackages(admin);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return null;
@@ -4141,7 +4136,7 @@
             try {
                 return mService.isLockTaskPermitted(pkg);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
         return false;
@@ -4188,7 +4183,7 @@
             try {
                 mService.setGlobalSetting(admin, setting, value);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -4216,7 +4211,7 @@
             try {
                 mService.setSecureSetting(admin, setting, value);
             } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, e);
             }
         }
     }
@@ -4237,7 +4232,7 @@
             try {
                 mService.setRestrictionsProvider(admin, provider);
             } catch (RemoteException re) {
-                Log.w(TAG, "Failed to set permission provider on device policy service");
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             }
         }
     }
@@ -4253,7 +4248,7 @@
             try {
                 mService.setMasterVolumeMuted(admin, on);
             } catch (RemoteException re) {
-                Log.w(TAG, "Failed to setMasterMute on device policy service");
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             }
         }
     }
@@ -4269,7 +4264,7 @@
             try {
                 return mService.isMasterVolumeMuted(admin);
             } catch (RemoteException re) {
-                Log.w(TAG, "Failed to get isMasterMute on device policy service");
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             }
         }
         return false;
@@ -4289,7 +4284,7 @@
             try {
                 mService.setUninstallBlocked(admin, packageName, uninstallBlocked);
             } catch (RemoteException re) {
-                Log.w(TAG, "Failed to call block uninstall on device policy service");
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             }
         }
     }
@@ -4313,7 +4308,7 @@
             try {
                 return mService.isUninstallBlocked(admin, packageName);
             } catch (RemoteException re) {
-                Log.w(TAG, "Failed to call block uninstall on device policy service");
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             }
         }
         return false;
@@ -4341,7 +4336,7 @@
             try {
                 return mService.addCrossProfileWidgetProvider(admin, packageName);
             } catch (RemoteException re) {
-                Log.w(TAG, "Error calling addCrossProfileWidgetProvider", re);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             }
         }
         return false;
@@ -4368,7 +4363,7 @@
             try {
                 return mService.removeCrossProfileWidgetProvider(admin, packageName);
             } catch (RemoteException re) {
-                Log.w(TAG, "Error calling removeCrossProfileWidgetProvider", re);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             }
         }
         return false;
@@ -4392,7 +4387,7 @@
                     return providers;
                 }
             } catch (RemoteException re) {
-                Log.w(TAG, "Error calling getCrossProfileWidgetProviders", re);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             }
         }
         return Collections.emptyList();
@@ -4408,7 +4403,7 @@
         try {
             mService.setUserIcon(admin, icon);
         } catch (RemoteException re) {
-            Log.w(TAG, "Could not set the user icon ", re);
+            Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
         }
     }
 
@@ -4428,7 +4423,7 @@
             try {
                 mService.setSystemUpdatePolicy(admin, policy);
             } catch (RemoteException re) {
-                Log.w(TAG, "Error calling setSystemUpdatePolicy", re);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             }
         }
     }
@@ -4443,7 +4438,7 @@
             try {
                 return mService.getSystemUpdatePolicy();
             } catch (RemoteException re) {
-                Log.w(TAG, "Error calling getSystemUpdatePolicy", re);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             }
         }
         return null;
@@ -4467,7 +4462,7 @@
         try {
             return mService.setKeyguardDisabled(admin, disabled);
         } catch (RemoteException re) {
-            Log.w(TAG, "Failed talking with device policy service", re);
+            Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             return false;
         }
     }
@@ -4487,7 +4482,7 @@
         try {
             return mService.setStatusBarDisabled(admin, disabled);
         } catch (RemoteException re) {
-            Log.w(TAG, "Failed talking with device policy service", re);
+            Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             return false;
         }
     }
@@ -4507,7 +4502,7 @@
             try {
                 mService.notifyPendingSystemUpdate(updateReceivedTime);
             } catch (RemoteException re) {
-                Log.w(TAG, "Could not notify device owner about pending system update", re);
+                Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             }
         }
     }
@@ -4533,7 +4528,7 @@
         try {
             mService.setPermissionPolicy(admin, policy);
         } catch (RemoteException re) {
-            Log.w(TAG, "Failed talking with device policy service", re);
+            Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
         }
     }
 
@@ -4584,7 +4579,7 @@
         try {
             return mService.setPermissionGrantState(admin, packageName, permission, grantState);
         } catch (RemoteException re) {
-            Log.w(TAG, "Failed talking with device policy service", re);
+            Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             return false;
         }
     }
@@ -4611,7 +4606,7 @@
         try {
             return mService.getPermissionGrantState(admin, packageName, permission);
         } catch (RemoteException re) {
-            Log.w(TAG, "Failed talking with device policy service", re);
+            Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             return PERMISSION_GRANT_STATE_DEFAULT;
         }
     }
@@ -4630,7 +4625,7 @@
         try {
             return mService.isProvisioningAllowed(action);
         } catch (RemoteException re) {
-            Log.w(TAG, "Failed talking with device policy service", re);
+            Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             return false;
         }
     }
@@ -4647,7 +4642,7 @@
         try {
             return mService.isManagedProfile(admin);
         } catch (RemoteException re) {
-            Log.w(TAG, "Failed talking with device policy service", re);
+            Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             return false;
         }
     }
@@ -4663,7 +4658,7 @@
         try {
             return mService.isSystemOnlyUser(admin);
         } catch (RemoteException re) {
-            Log.w(TAG, "Failed talking with device policy service", re);
+            Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             return false;
         }
     }
@@ -4680,7 +4675,7 @@
         try {
             return mService.getWifiMacAddress();
         } catch (RemoteException re) {
-            Log.w(TAG, "Failed talking with device policy service", re);
+            Log.w(TAG, REMOTE_EXCEPTION_MESSAGE, re);
             return null;
         }
     }
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClientCall.java b/core/java/android/bluetooth/BluetoothHeadsetClientCall.java
index 7b5a045..1fb7825 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClientCall.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClientCall.java
@@ -172,8 +172,12 @@
     }
 
     public String toString() {
+        return toString(false);
+    }
+
+    public String toString(boolean loggable) {
         StringBuilder builder = new StringBuilder("BluetoothHeadsetClientCall{mDevice: ");
-        builder.append(mDevice);
+        builder.append(loggable ? mDevice.hashCode() : mDevice);
         builder.append(", mId: ");
         builder.append(mId);
         builder.append(", mState: ");
@@ -189,7 +193,7 @@
             default: builder.append(mState); break;
         }
         builder.append(", mNumber: ");
-        builder.append(mNumber);
+        builder.append(loggable ? mNumber.hashCode() : mNumber);
         builder.append(", mMultiParty: ");
         builder.append(mMultiParty);
         builder.append(", mOutgoing: ");
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 1f7fd9d..38a4475 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -53,8 +53,8 @@
 import android.os.UserManager;
 import android.provider.MediaStore;
 import android.util.AttributeSet;
-import android.view.DisplayAdjustments;
 import android.view.Display;
+import android.view.DisplayAdjustments;
 import android.view.ViewDebug;
 import android.view.WindowManager;
 
@@ -2116,6 +2116,14 @@
             UserHandle user);
 
     /**
+     * @hide
+     * This is just here for sending CONNECTIVITY_ACTION.
+     */
+    @Deprecated
+    public abstract void sendStickyBroadcastAsUser(@RequiresPermission Intent intent,
+            UserHandle user, Bundle options);
+
+    /**
      * <p>Version of
      * {@link #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle)}
      * that allows you to specify the
@@ -3870,7 +3878,7 @@
      *
      * @hide
      */
-    public static final int CONTEXT_STORAGE_DEVICE_ENCRYPTED = 0x00000008;
+    public static final int CONTEXT_DEVICE_ENCRYPTED_STORAGE = 0x00000008;
 
     /**
      * Flag for use with {@link #createPackageContext}: point all file APIs at
@@ -3878,11 +3886,7 @@
      *
      * @hide
      */
-    public static final int CONTEXT_STORAGE_CREDENTIAL_ENCRYPTED = 0x00000010;
-
-    /** {@hide} */
-    public static final int CONTEXT_STORAGE_MASK = CONTEXT_STORAGE_DEVICE_ENCRYPTED
-            | CONTEXT_STORAGE_CREDENTIAL_ENCRYPTED;
+    public static final int CONTEXT_CREDENTIAL_ENCRYPTED_STORAGE = 0x00000010;
 
     /**
      * @hide Used to indicate we should tell the activity manager about the process
@@ -3987,19 +3991,23 @@
      * Return a new Context object for the current Context but whose storage
      * APIs are backed by device-encrypted storage.
      * <p>
-     * Data stored in device-encrypted storage is typically encrypted with a
-     * key tied to the physical device, and they can be accessed whenever the
-     * device has booted successfully, both <em>before and after</em> the user
-     * has entered their credentials (such as a lock pattern or PIN).
+     * Data stored in device-encrypted storage is typically encrypted with a key
+     * tied to the physical device, and it can be accessed when the device has
+     * booted successfully, both <em>before and after</em> the user has
+     * authenticated with their credentials (such as a lock pattern or PIN).
+     * Because device-encrypted data is available before user authentication,
+     * you should carefully consider what data you store using this Context.
      * <p>
      * Each call to this method returns a new instance of a Context object;
      * Context objects are not shared, however common state (ClassLoader, other
      * Resources for the same configuration) may be so the Context itself can be
      * fairly lightweight.
      *
-     * @see #isDeviceEncrypted()
+     * @return new Context or {@code null} if device-encrypted storage is not
+     *         supported or available on this device.
+     * @see #isDeviceEncryptedStorage()
      */
-    public abstract Context createDeviceEncryptedContext(Context context);
+    public abstract Context createDeviceEncryptedStorageContext();
 
     /**
      * Return a new Context object for the current Context but whose storage
@@ -4015,9 +4023,11 @@
      * Resources for the same configuration) may be so the Context itself can be
      * fairly lightweight.
      *
-     * @see #isCredentialEncrypted()
+     * @see #isCredentialEncryptedStorage()
+     * @hide
      */
-    public abstract Context createCredentialEncryptedContext(Context context);
+    @SystemApi
+    public abstract Context createCredentialEncryptedStorageContext();
 
     /**
      * Gets the display adjustments holder for this context.  This information
@@ -4045,15 +4055,17 @@
      * Indicates if the storage APIs of this Context are backed by
      * device-encrypted storage.
      *
-     * @see #createDeviceEncryptedContext(Context)
+     * @see #createDeviceEncryptedStorageContext()
      */
-    public abstract boolean isDeviceEncrypted();
+    public abstract boolean isDeviceEncryptedStorage();
 
     /**
      * Indicates if the storage APIs of this Context are backed by
      * credential-encrypted storage.
      *
-     * @see #createCredentialEncryptedContext(Context)
+     * @see #createCredentialEncryptedStorageContext()
+     * @hide
      */
-    public abstract boolean isCredentialEncrypted();
+    @SystemApi
+    public abstract boolean isCredentialEncryptedStorage();
 }
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 73d0ddc..1a3d262 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -536,6 +536,13 @@
         mBase.sendStickyBroadcastAsUser(intent, user);
     }
 
+    /** @hide */
+    @Override
+    @Deprecated
+    public void sendStickyBroadcastAsUser(Intent intent, UserHandle user, Bundle options) {
+        mBase.sendStickyBroadcastAsUser(intent, user, options);
+    }
+
     @Override
     @Deprecated
     public void sendStickyOrderedBroadcastAsUser(Intent intent,
@@ -792,22 +799,26 @@
     }
 
     @Override
-    public Context createDeviceEncryptedContext(Context context) {
-        return mBase.createDeviceEncryptedContext(context);
+    public Context createDeviceEncryptedStorageContext() {
+        return mBase.createDeviceEncryptedStorageContext();
+    }
+
+    /** {@hide} */
+    @SystemApi
+    @Override
+    public Context createCredentialEncryptedStorageContext() {
+        return mBase.createCredentialEncryptedStorageContext();
     }
 
     @Override
-    public Context createCredentialEncryptedContext(Context context) {
-        return mBase.createCredentialEncryptedContext(context);
+    public boolean isDeviceEncryptedStorage() {
+        return mBase.isDeviceEncryptedStorage();
     }
 
+    /** {@hide} */
+    @SystemApi
     @Override
-    public boolean isDeviceEncrypted() {
-        return mBase.isDeviceEncrypted();
-    }
-
-    @Override
-    public boolean isCredentialEncrypted() {
-        return mBase.isCredentialEncrypted();
+    public boolean isCredentialEncryptedStorage() {
+        return mBase.isCredentialEncryptedStorage();
     }
 }
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index bc7620c..a27d1cb 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4471,6 +4471,22 @@
      * @hide
      */
     public static final int FLAG_RECEIVER_BOOT_UPGRADE = 0x02000000;
+    /**
+     * If set, the broadcast will always go to manifest receivers in background (cached
+     * or not running) apps, regardless of whether that would be done by default.  By
+     * default they will only receive broadcasts if the broadcast has specified an
+     * explicit component or package name.
+     * @hide
+     */
+    public static final int FLAG_RECEIVER_INCLUDE_BACKGROUND = 0x01000000;
+    /**
+     * If set, the broadcast will never go to manifest receivers in background (cached
+     * or not running) apps, regardless of whether that would be done by default.  By
+     * default they will receive broadcasts if the broadcast has specified an
+     * explicit component or package name.
+     * @hide
+     */
+    public static final int FLAG_RECEIVER_EXCLUDE_BACKGROUND = 0x00800000;
 
     /**
      * @hide Flags that can't be changed with PendingIntent.
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 4a3c59b..0cb0e9f 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -756,13 +756,20 @@
     }
 
     public void dump(Printer pw, String prefix) {
+        dump(pw, prefix, DUMP_FLAG_ALL);
+    }
+
+    /** @hide */
+    public void dump(Printer pw, String prefix, int flags) {
         super.dumpFront(pw, prefix);
         if (permission != null) {
             pw.println(prefix + "permission=" + permission);
         }
-        pw.println(prefix + "taskAffinity=" + taskAffinity
-                + " targetActivity=" + targetActivity
-                + " persistableMode=" + persistableModeToString());
+        if ((flags&DUMP_FLAG_DETAILS) != 0) {
+            pw.println(prefix + "taskAffinity=" + taskAffinity
+                    + " targetActivity=" + targetActivity
+                    + " persistableMode=" + persistableModeToString());
+        }
         if (launchMode != 0 || flags != 0 || theme != 0) {
             pw.println(prefix + "launchMode=" + launchMode
                     + " flags=0x" + Integer.toHexString(flags)
@@ -777,14 +784,17 @@
         if (uiOptions != 0) {
             pw.println(prefix + " uiOptions=0x" + Integer.toHexString(uiOptions));
         }
-        pw.println(prefix + "resizeable=" + resizeable + " supportsPip=" + supportsPip);
-        pw.println(prefix + "lockTaskLaunchMode=" + lockTaskLaunchModeToString(lockTaskLaunchMode));
+        if ((flags&DUMP_FLAG_DETAILS) != 0) {
+            pw.println(prefix + "resizeable=" + resizeable + " supportsPip=" + supportsPip);
+            pw.println(prefix + "lockTaskLaunchMode="
+                    + lockTaskLaunchModeToString(lockTaskLaunchMode));
+        }
         if (layout != null) {
             pw.println(prefix + "initialLayout=" + layout.width + "|"
                     + layout.widthFraction + ", " + layout.height + "|"
                     + layout.heightFraction + ", " + layout.gravity);
         }
-        super.dumpBack(pw, prefix);
+        super.dumpBack(pw, prefix, flags);
     }
 
     public String toString() {
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 4c5e766..0633bff 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -710,21 +710,30 @@
     public int installLocation = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
 
     public void dump(Printer pw, String prefix) {
+        dump(pw, prefix, DUMP_FLAG_ALL);
+    }
+
+    /** @hide */
+    public void dump(Printer pw, String prefix, int flags) {
         super.dumpFront(pw, prefix);
-        if (className != null) {
+        if ((flags&DUMP_FLAG_DETAILS) != 0 && className != null) {
             pw.println(prefix + "className=" + className);
         }
         if (permission != null) {
             pw.println(prefix + "permission=" + permission);
         }
         pw.println(prefix + "processName=" + processName);
-        pw.println(prefix + "taskAffinity=" + taskAffinity);
+        if ((flags&DUMP_FLAG_DETAILS) != 0) {
+            pw.println(prefix + "taskAffinity=" + taskAffinity);
+        }
         pw.println(prefix + "uid=" + uid + " flags=0x" + Integer.toHexString(flags)
                 + " privateFlags=0x" + Integer.toHexString(privateFlags)
                 + " theme=0x" + Integer.toHexString(theme));
-        pw.println(prefix + "requiresSmallestWidthDp=" + requiresSmallestWidthDp
-                + " compatibleWidthLimitDp=" + compatibleWidthLimitDp
-                + " largestWidthLimitDp=" + largestWidthLimitDp);
+        if ((flags&DUMP_FLAG_DETAILS) != 0) {
+            pw.println(prefix + "requiresSmallestWidthDp=" + requiresSmallestWidthDp
+                    + " compatibleWidthLimitDp=" + compatibleWidthLimitDp
+                    + " largestWidthLimitDp=" + largestWidthLimitDp);
+        }
         pw.println(prefix + "sourceDir=" + sourceDir);
         if (!Objects.equals(sourceDir, publicSourceDir)) {
             pw.println(prefix + "publicSourceDir=" + publicSourceDir);
@@ -739,31 +748,36 @@
         if (resourceDirs != null) {
             pw.println(prefix + "resourceDirs=" + resourceDirs);
         }
-        if (seinfo != null) {
+        if ((flags&DUMP_FLAG_DETAILS) != 0 && seinfo != null) {
             pw.println(prefix + "seinfo=" + seinfo);
         }
         pw.println(prefix + "dataDir=" + dataDir);
-        pw.println(prefix + "deviceEncryptedDataDir=" + deviceEncryptedDataDir);
-        pw.println(prefix + "credentialEncryptedDataDir=" + credentialEncryptedDataDir);
-        if (sharedLibraryFiles != null) {
-            pw.println(prefix + "sharedLibraryFiles=" + Arrays.toString(sharedLibraryFiles));
+        if ((flags&DUMP_FLAG_DETAILS) != 0) {
+            pw.println(prefix + "deviceEncryptedDataDir=" + deviceEncryptedDataDir);
+            pw.println(prefix + "credentialEncryptedDataDir=" + credentialEncryptedDataDir);
+            if (sharedLibraryFiles != null) {
+                pw.println(prefix + "sharedLibraryFiles=" + Arrays.toString(sharedLibraryFiles));
+            }
         }
         pw.println(prefix + "enabled=" + enabled + " targetSdkVersion=" + targetSdkVersion
                 + " versionCode=" + versionCode);
-        if (manageSpaceActivityName != null) {
-            pw.println(prefix + "manageSpaceActivityName="+manageSpaceActivityName);
-        }
-        if (descriptionRes != 0) {
-            pw.println(prefix + "description=0x"+Integer.toHexString(descriptionRes));
-        }
-        if (uiOptions != 0) {
-            pw.println(prefix + "uiOptions=0x" + Integer.toHexString(uiOptions));
-        }
-        pw.println(prefix + "supportsRtl=" + (hasRtlSupport() ? "true" : "false"));
-        if (fullBackupContent > 0) {
-            pw.println(prefix + "fullBackupContent=@xml/" + fullBackupContent);
-        } else {
-            pw.println(prefix + "fullBackupContent=" + (fullBackupContent < 0 ? "false" : "true"));
+        if ((flags&DUMP_FLAG_DETAILS) != 0) {
+            if (manageSpaceActivityName != null) {
+                pw.println(prefix + "manageSpaceActivityName=" + manageSpaceActivityName);
+            }
+            if (descriptionRes != 0) {
+                pw.println(prefix + "description=0x" + Integer.toHexString(descriptionRes));
+            }
+            if (uiOptions != 0) {
+                pw.println(prefix + "uiOptions=0x" + Integer.toHexString(uiOptions));
+            }
+            pw.println(prefix + "supportsRtl=" + (hasRtlSupport() ? "true" : "false"));
+            if (fullBackupContent > 0) {
+                pw.println(prefix + "fullBackupContent=@xml/" + fullBackupContent);
+            } else {
+                pw.println(prefix + "fullBackupContent="
+                        + (fullBackupContent < 0 ? "false" : "true"));
+            }
         }
         super.dumpBack(pw, prefix);
     }
diff --git a/core/java/android/content/pm/ComponentInfo.java b/core/java/android/content/pm/ComponentInfo.java
index ad7ebe5..a295cc5 100644
--- a/core/java/android/content/pm/ComponentInfo.java
+++ b/core/java/android/content/pm/ComponentInfo.java
@@ -150,23 +150,32 @@
 
     protected void dumpFront(Printer pw, String prefix) {
         super.dumpFront(pw, prefix);
+        if (processName != null && !packageName.equals(processName)) {
+            pw.println(prefix + "processName=" + processName);
+        }
         pw.println(prefix + "enabled=" + enabled + " exported=" + exported
-                + " encryptionAware=" + encryptionAware + " processName=" + processName);
+                + " encryptionAware=" + encryptionAware);
         if (descriptionRes != 0) {
             pw.println(prefix + "description=" + descriptionRes);
         }
     }
-    
+
     protected void dumpBack(Printer pw, String prefix) {
-        if (applicationInfo != null) {
-            pw.println(prefix + "ApplicationInfo:");
-            applicationInfo.dump(pw, prefix + "  ");
-        } else {
-            pw.println(prefix + "ApplicationInfo: null");
+        dumpBack(pw, prefix, DUMP_FLAG_ALL);
+    }
+    
+    void dumpBack(Printer pw, String prefix, int flags) {
+        if ((flags&DUMP_FLAG_APPLICATION) != 0) {
+            if (applicationInfo != null) {
+                pw.println(prefix + "ApplicationInfo:");
+                applicationInfo.dump(pw, prefix + "  ", flags);
+            } else {
+                pw.println(prefix + "ApplicationInfo: null");
+            }
         }
         super.dumpBack(pw, prefix);
     }
-    
+
     public void writeToParcel(Parcel dest, int parcelableFlags) {
         super.writeToParcel(dest, parcelableFlags);
         if ((parcelableFlags & Parcelable.PARCELABLE_ELIDE_DUPLICATES) != 0) {
diff --git a/core/java/android/content/pm/PackageItemInfo.java b/core/java/android/content/pm/PackageItemInfo.java
index 22a899c..4df83036 100644
--- a/core/java/android/content/pm/PackageItemInfo.java
+++ b/core/java/android/content/pm/PackageItemInfo.java
@@ -282,6 +282,21 @@
         return null;
     }
 
+    /**
+     * @hide Flag for dumping: include all details.
+     */
+    public static final int DUMP_FLAG_DETAILS = 1<<0;
+
+    /**
+     * @hide Flag for dumping: include nested ApplicationInfo.
+     */
+    public static final int DUMP_FLAG_APPLICATION = 1<<1;
+
+    /**
+     * @hide Flag for dumping: all flags to dump everything.
+     */
+    public static final int DUMP_FLAG_ALL = DUMP_FLAG_DETAILS | DUMP_FLAG_APPLICATION;
+
     protected void dumpFront(Printer pw, String prefix) {
         if (name != null) {
             pw.println(prefix + "name=" + name);
diff --git a/core/java/android/content/pm/ProviderInfo.java b/core/java/android/content/pm/ProviderInfo.java
index f6ea058..7e7b32f 100644
--- a/core/java/android/content/pm/ProviderInfo.java
+++ b/core/java/android/content/pm/ProviderInfo.java
@@ -115,9 +115,15 @@
     }
 
     public void dump(Printer pw, String prefix) {
+        dump(pw, prefix, DUMP_FLAG_ALL);
+    }
+
+    /** @hide */
+    public void dump(Printer pw, String prefix, int flags) {
         super.dumpFront(pw, prefix);
         pw.println(prefix + "authority=" + authority);
         pw.println(prefix + "flags=0x" + Integer.toHexString(flags));
+        super.dumpBack(pw, prefix, flags);
     }
 
     public int describeContents() {
diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java
index 7bab35c..a5fb451 100644
--- a/core/java/android/content/pm/ResolveInfo.java
+++ b/core/java/android/content/pm/ResolveInfo.java
@@ -260,6 +260,11 @@
     }
 
     public void dump(Printer pw, String prefix) {
+        dump(pw, prefix, PackageItemInfo.DUMP_FLAG_ALL);
+    }
+
+    /** @hide */
+    public void dump(Printer pw, String prefix, int flags) {
         if (filter != null) {
             pw.println(prefix + "Filter:");
             filter.dump(pw, prefix + "  ");
@@ -279,16 +284,16 @@
         }
         if (activityInfo != null) {
             pw.println(prefix + "ActivityInfo:");
-            activityInfo.dump(pw, prefix + "  ");
+            activityInfo.dump(pw, prefix + "  ", flags);
         } else if (serviceInfo != null) {
             pw.println(prefix + "ServiceInfo:");
-            serviceInfo.dump(pw, prefix + "  ");
+            serviceInfo.dump(pw, prefix + "  ", flags);
         } else if (providerInfo != null) {
             pw.println(prefix + "ProviderInfo:");
-            providerInfo.dump(pw, prefix + "  ");
+            providerInfo.dump(pw, prefix + "  ", flags);
         }
     }
-    
+
     public ResolveInfo() {
         targetUserId = UserHandle.USER_CURRENT;
     }
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 796c2a4..74e5c2a 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -74,9 +74,15 @@
     }
 
     public void dump(Printer pw, String prefix) {
+        dump(pw, prefix, DUMP_FLAG_ALL);
+    }
+
+    /** @hide */
+    void dump(Printer pw, String prefix, int flags) {
         super.dumpFront(pw, prefix);
         pw.println(prefix + "permission=" + permission);
         pw.println(prefix + "flags=0x" + Integer.toHexString(flags));
+        super.dumpBack(pw, prefix, flags);
     }
     
     public String toString() {
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 8a87bff..02d4e59 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -47,6 +47,8 @@
 import java.util.LinkedHashMap;
 import java.util.List;
 
+import static android.system.OsConstants.*;
+
 /**
  * The Camera class is used to set image capture settings, start/stop preview,
  * snap pictures, and retrieve frames for encoding for video.  This class is a
@@ -173,13 +175,6 @@
     private final Object mAutoFocusCallbackLock = new Object();
 
     private static final int NO_ERROR = 0;
-    private static final int EACCESS = -13;
-    private static final int ENODEV = -19;
-    private static final int EBUSY = -16;
-    private static final int EINVAL = -22;
-    private static final int ENOSYS = -38;
-    private static final int EUSERS = -87;
-    private static final int EOPNOTSUPP = -95;
 
     /**
      * Broadcast Action:  A new picture is taken by the camera, and the entry of
@@ -415,30 +410,28 @@
     private Camera(int cameraId, int halVersion) {
         int err = cameraInitVersion(cameraId, halVersion);
         if (checkInitErrors(err)) {
-            switch(err) {
-                case EACCESS:
-                    throw new RuntimeException("Fail to connect to camera service");
-                case ENODEV:
-                    throw new RuntimeException("Camera initialization failed");
-                case ENOSYS:
-                    throw new RuntimeException("Camera initialization failed because some methods"
-                            + " are not implemented");
-                case EOPNOTSUPP:
-                    throw new RuntimeException("Camera initialization failed because the hal"
-                            + " version is not supported by this device");
-                case EINVAL:
-                    throw new RuntimeException("Camera initialization failed because the input"
-                            + " arugments are invalid");
-                case EBUSY:
-                    throw new RuntimeException("Camera initialization failed because the camera"
-                            + " device was already opened");
-                case EUSERS:
-                    throw new RuntimeException("Camera initialization failed because the max"
-                            + " number of camera devices were already opened");
-                default:
-                    // Should never hit this.
-                    throw new RuntimeException("Unknown camera error");
+            if (err == -EACCES) {
+                throw new RuntimeException("Fail to connect to camera service");
+            } else if (err == -ENODEV) {
+                throw new RuntimeException("Camera initialization failed");
+            } else if (err == -ENOSYS) {
+                throw new RuntimeException("Camera initialization failed because some methods"
+                        + " are not implemented");
+            } else if (err == -EOPNOTSUPP) {
+                throw new RuntimeException("Camera initialization failed because the hal"
+                        + " version is not supported by this device");
+            } else if (err == -EINVAL) {
+                throw new RuntimeException("Camera initialization failed because the input"
+                        + " arugments are invalid");
+            } else if (err == -EBUSY) {
+                throw new RuntimeException("Camera initialization failed because the camera"
+                        + " device was already opened");
+            } else if (err == -EUSERS) {
+                throw new RuntimeException("Camera initialization failed because the max"
+                        + " number of camera devices were already opened");
             }
+            // Should never hit this.
+            throw new RuntimeException("Unknown camera error");
         }
     }
 
@@ -490,15 +483,13 @@
     Camera(int cameraId) {
         int err = cameraInitNormal(cameraId);
         if (checkInitErrors(err)) {
-            switch(err) {
-                case EACCESS:
-                    throw new RuntimeException("Fail to connect to camera service");
-                case ENODEV:
-                    throw new RuntimeException("Camera initialization failed");
-                default:
-                    // Should never hit this.
-                    throw new RuntimeException("Unknown camera error");
+            if (err == -EACCES) {
+                throw new RuntimeException("Fail to connect to camera service");
+            } else if (err == -ENODEV) {
+                throw new RuntimeException("Camera initialization failed");
             }
+            // Should never hit this.
+            throw new RuntimeException("Unknown camera error");
         }
     }
 
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index 6b8e113..798c941 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -43,6 +43,9 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import static android.system.OsConstants.EACCES;
+import static android.system.OsConstants.ENODEV;
+
 /**
  * Compatibility implementation of the Camera2 API binder interface.
  *
@@ -88,6 +91,14 @@
         mSurfaceIdCounter = 0;
     }
 
+    private static int translateErrorsFromCamera1(int errorCode) {
+        if (errorCode == -EACCES) {
+            return CameraBinderDecorator.PERMISSION_DENIED;
+        }
+
+        return errorCode;
+    }
+
     /**
      * Create a separate looper/thread for the camera to run on; open the camera.
      *
@@ -382,7 +393,7 @@
         }
         if (mLegacyDevice.isClosed()) {
             Log.e(TAG, "Cannot submit request, device has been closed.");
-            return CameraBinderDecorator.ENODEV;
+            return -ENODEV;
         }
 
         synchronized(mConfigureLock) {
@@ -402,7 +413,7 @@
         }
         if (mLegacyDevice.isClosed()) {
             Log.e(TAG, "Cannot submit request list, device has been closed.");
-            return CameraBinderDecorator.ENODEV;
+            return -ENODEV;
         }
 
         synchronized(mConfigureLock) {
@@ -421,7 +432,7 @@
         }
         if (mLegacyDevice.isClosed()) {
             Log.e(TAG, "Cannot cancel request, device has been closed.");
-            return CameraBinderDecorator.ENODEV;
+            return -ENODEV;
         }
 
         synchronized(mConfigureLock) {
@@ -442,7 +453,7 @@
         }
         if (mLegacyDevice.isClosed()) {
             Log.e(TAG, "Cannot begin configure, device has been closed.");
-            return CameraBinderDecorator.ENODEV;
+            return -ENODEV;
         }
 
         synchronized(mConfigureLock) {
@@ -462,7 +473,7 @@
         }
         if (mLegacyDevice.isClosed()) {
             Log.e(TAG, "Cannot end configure, device has been closed.");
-            return CameraBinderDecorator.ENODEV;
+            return -ENODEV;
         }
 
         ArrayList<Surface> surfaces = null;
@@ -490,7 +501,7 @@
         }
         if (mLegacyDevice.isClosed()) {
             Log.e(TAG, "Cannot delete stream, device has been closed.");
-            return CameraBinderDecorator.ENODEV;
+            return -ENODEV;
         }
 
         synchronized(mConfigureLock) {
@@ -515,7 +526,7 @@
         }
         if (mLegacyDevice.isClosed()) {
             Log.e(TAG, "Cannot create stream, device has been closed.");
-            return CameraBinderDecorator.ENODEV;
+            return -ENODEV;
         }
 
         synchronized(mConfigureLock) {
@@ -552,7 +563,7 @@
         }
         if (mLegacyDevice.isClosed()) {
             Log.e(TAG, "Cannot create default request, device has been closed.");
-            return CameraBinderDecorator.ENODEV;
+            return -ENODEV;
         }
 
         CameraMetadataNative template;
@@ -585,7 +596,7 @@
         }
         if (mLegacyDevice.isClosed()) {
             Log.e(TAG, "Cannot wait until idle, device has been closed.");
-            return CameraBinderDecorator.ENODEV;
+            return -ENODEV;
         }
 
         synchronized(mConfigureLock) {
@@ -605,7 +616,7 @@
         }
         if (mLegacyDevice.isClosed()) {
             Log.e(TAG, "Cannot flush, device has been closed.");
-            return CameraBinderDecorator.ENODEV;
+            return -ENODEV;
         }
 
         synchronized(mConfigureLock) {
@@ -627,7 +638,7 @@
         }
         if (mLegacyDevice.isClosed()) {
             Log.e(TAG, "Cannot prepare stream, device has been closed.");
-            return CameraBinderDecorator.ENODEV;
+            return -ENODEV;
         }
 
         // LEGACY doesn't support actual prepare, just signal success right away
@@ -647,7 +658,7 @@
         }
         if (mLegacyDevice.isClosed()) {
             Log.e(TAG, "Cannot tear down stream, device has been closed.");
-            return CameraBinderDecorator.ENODEV;
+            return -ENODEV;
         }
 
         // LEGACY doesn't support actual teardown, so just a no-op
diff --git a/core/java/android/hardware/camera2/legacy/LegacyExceptionUtils.java b/core/java/android/hardware/camera2/legacy/LegacyExceptionUtils.java
index 4b7cfbf..4501e81 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyExceptionUtils.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyExceptionUtils.java
@@ -19,6 +19,8 @@
 import android.hardware.camera2.utils.CameraBinderDecorator;
 import android.util.AndroidException;
 
+import static android.system.OsConstants.ENODEV;
+
 /**
  * Utility class containing exception handling used solely by the compatibility mode shim.
  */
@@ -51,18 +53,15 @@
      * exceptions.</p>
      *
      * @param errorFlag error to throw as an exception.
-     * @throws {@link BufferQueueAbandonedException} for {@link CameraBinderDecorator#ENODEV}.
+     * @throws {@link BufferQueueAbandonedException} for -ENODEV.
      * @throws {@link UnsupportedOperationException} for an unknown negative error code.
      * @return {@code errorFlag} if the value was non-negative, throws otherwise.
      */
     public static int throwOnError(int errorFlag) throws BufferQueueAbandonedException {
-        switch (errorFlag) {
-            case CameraBinderDecorator.NO_ERROR: {
-                return CameraBinderDecorator.NO_ERROR;
-            }
-            case CameraBinderDecorator.BAD_VALUE: {
-                throw new BufferQueueAbandonedException();
-            }
+        if (errorFlag == CameraBinderDecorator.NO_ERROR) {
+            return CameraBinderDecorator.NO_ERROR;
+        } else if (errorFlag == -ENODEV) {
+            throw new BufferQueueAbandonedException();
         }
 
         if (errorFlag < 0) {
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index b8d6960..8be49e8 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -834,6 +834,7 @@
      * <ul>
      * <li>{@link ImageFormat#JPEG JPEG}
      * <li>{@link ImageFormat#RAW_SENSOR RAW16}
+     * <li>{@link ImageFormat#RAW_PRIVATE RAW_PRIVATE}
      * </ul>
      * </p>
      *
@@ -1328,9 +1329,7 @@
         SparseIntArray map = getFormatsMap(output);
         for (int j = 0; j < map.size(); j++) {
             int format = map.keyAt(j);
-            if (format != HAL_PIXEL_FORMAT_RAW_OPAQUE) {
-                formats[i++] = imageFormatToPublic(format);
-            }
+            formats[i++] = imageFormatToPublic(format);
         }
         if (output) {
             for (int j = 0; j < mDepthOutputFormats.size(); j++) {
@@ -1392,9 +1391,6 @@
     private int getPublicFormatCount(boolean output) {
         SparseIntArray formatsMap = getFormatsMap(output);
         int size = formatsMap.size();
-        if (formatsMap.indexOfKey(HAL_PIXEL_FORMAT_RAW_OPAQUE) >= 0) {
-            size -= 1;
-        }
         if (output) {
             size += mDepthOutputFormats.size();
         }
@@ -1603,6 +1599,8 @@
                 return "Y16";
             case ImageFormat.RAW_SENSOR:
                 return "RAW_SENSOR";
+            case ImageFormat.RAW_PRIVATE:
+                return "RAW_PRIVATE";
             case ImageFormat.RAW10:
                 return "RAW10";
             case ImageFormat.DEPTH16:
diff --git a/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java b/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java
index 1aee794..162edc9 100644
--- a/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java
+++ b/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java
@@ -22,6 +22,7 @@
 import static android.hardware.camera2.CameraAccessException.CAMERA_ERROR;
 import static android.hardware.camera2.CameraAccessException.MAX_CAMERAS_IN_USE;
 import static android.hardware.camera2.CameraAccessException.CAMERA_DEPRECATED_HAL;
+import static android.system.OsConstants.*;
 
 import android.os.DeadObjectException;
 import android.os.RemoteException;
@@ -37,12 +38,12 @@
 public class CameraBinderDecorator {
 
     public static final int NO_ERROR = 0;
-    public static final int PERMISSION_DENIED = -1;
-    public static final int ALREADY_EXISTS = -17;
-    public static final int BAD_VALUE = -22;
-    public static final int DEAD_OBJECT = -32;
-    public static final int INVALID_OPERATION = -38;
-    public static final int TIMED_OUT = -110;
+    public static final int PERMISSION_DENIED = -EPERM;
+    public static final int ALREADY_EXISTS = -EEXIST;
+    public static final int BAD_VALUE = -EINVAL;
+    public static final int DEAD_OBJECT = -ENOSYS;
+    public static final int INVALID_OPERATION = -EPIPE;
+    public static final int TIMED_OUT = -ETIMEDOUT;
 
     /**
      * TODO: add as error codes in Errors.h
@@ -52,12 +53,6 @@
      * - NOT_SUPPORTED
      * - TOO_MANY_USERS
      */
-    public static final int EACCES = -13;
-    public static final int EBUSY = -16;
-    public static final int ENODEV = -19;
-    public static final int EOPNOTSUPP = -95;
-    public static final int EUSERS = -87;
-
 
     static class CameraBinderDecoratorListener implements Decorator.DecoratorListener {
 
@@ -101,35 +96,34 @@
      * @param errorFlag error to throw as an exception.
      */
     public static void throwOnError(int errorFlag) {
-        switch (errorFlag) {
-            case NO_ERROR:
-                return;
-            case PERMISSION_DENIED:
-                throw new SecurityException("Lacking privileges to access camera service");
-            case ALREADY_EXISTS:
-                // This should be handled at the call site. Typically this isn't bad,
-                // just means we tried to do an operation that already completed.
-                return;
-            case BAD_VALUE:
-                throw new IllegalArgumentException("Bad argument passed to camera service");
-            case DEAD_OBJECT:
-                throw new CameraRuntimeException(CAMERA_DISCONNECTED);
-            case TIMED_OUT:
-                throw new CameraRuntimeException(CAMERA_ERROR,
-                        "Operation timed out in camera service");
-            case EACCES:
-                throw new CameraRuntimeException(CAMERA_DISABLED);
-            case EBUSY:
-                throw new CameraRuntimeException(CAMERA_IN_USE);
-            case EUSERS:
-                throw new CameraRuntimeException(MAX_CAMERAS_IN_USE);
-            case ENODEV:
-                throw new CameraRuntimeException(CAMERA_DISCONNECTED);
-            case EOPNOTSUPP:
-                throw new CameraRuntimeException(CAMERA_DEPRECATED_HAL);
-            case INVALID_OPERATION:
-                throw new CameraRuntimeException(CAMERA_ERROR,
-                        "Illegal state encountered in camera service.");
+        if (errorFlag == NO_ERROR) {
+            return;
+        } else if (errorFlag == PERMISSION_DENIED) {
+            throw new SecurityException("Lacking privileges to access camera service");
+        } else if (errorFlag == ALREADY_EXISTS) {
+            // This should be handled at the call site. Typically this isn't bad,
+            // just means we tried to do an operation that already completed.
+            return;
+        } else if (errorFlag == BAD_VALUE) {
+            throw new IllegalArgumentException("Bad argument passed to camera service");
+        } else if (errorFlag == DEAD_OBJECT) {
+            throw new CameraRuntimeException(CAMERA_DISCONNECTED);
+        } else if (errorFlag == TIMED_OUT) {
+            throw new CameraRuntimeException(CAMERA_ERROR,
+                    "Operation timed out in camera service");
+        } else if (errorFlag == -EACCES) {
+            throw new CameraRuntimeException(CAMERA_DISABLED);
+        } else if (errorFlag == -EBUSY) {
+            throw new CameraRuntimeException(CAMERA_IN_USE);
+        } else if (errorFlag == -EUSERS) {
+            throw new CameraRuntimeException(MAX_CAMERAS_IN_USE);
+        } else if (errorFlag == -ENODEV) {
+            throw new CameraRuntimeException(CAMERA_DISCONNECTED);
+        } else if (errorFlag == -EOPNOTSUPP) {
+            throw new CameraRuntimeException(CAMERA_DEPRECATED_HAL);
+        } else if (errorFlag == INVALID_OPERATION) {
+            throw new CameraRuntimeException(CAMERA_ERROR,
+                    "Illegal state encountered in camera service.");
         }
 
         /**
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index c85e97b..d490409 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -25,6 +25,8 @@
 import java.util.Arrays;
 import java.util.UUID;
 
+import static android.system.OsConstants.*;
+
 /**
  * The SoundTrigger class provides access via JNI to the native service managing
  * the sound trigger HAL.
@@ -35,11 +37,11 @@
 
     public static final int STATUS_OK = 0;
     public static final int STATUS_ERROR = Integer.MIN_VALUE;
-    public static final int STATUS_PERMISSION_DENIED = -1;
-    public static final int STATUS_NO_INIT = -19;
-    public static final int STATUS_BAD_VALUE = -22;
-    public static final int STATUS_DEAD_OBJECT = -32;
-    public static final int STATUS_INVALID_OPERATION = -38;
+    public static final int STATUS_PERMISSION_DENIED = -EPERM;
+    public static final int STATUS_NO_INIT = -ENODEV;
+    public static final int STATUS_BAD_VALUE = -EINVAL;
+    public static final int STATUS_DEAD_OBJECT = -EPIPE;
+    public static final int STATUS_INVALID_OPERATION = -ENOSYS;
 
     /*****************************************************************************
      * A ModuleProperties describes a given sound trigger hardware module
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 1346a39..00a874b 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -776,6 +776,16 @@
     }
 
     /**
+     * Return whether the calling user is running in a "locked" state. A user is
+     * unlocked only after they've entered their credentials (such as a lock
+     * pattern or PIN), and credential-encrypted private app data storage is
+     * available.
+     */
+    public boolean isUserRunningAndLocked() {
+        return isUserRunningAndLocked(Process.myUserHandle());
+    }
+
+    /**
      * Return whether the given user is running in a "locked" state. A user
      * is unlocked only after they've entered their credentials (such as a lock
      * pattern or PIN), and credential-encrypted private app data storage is
@@ -793,6 +803,16 @@
     }
 
     /**
+     * Return whether the calling user is running in an "unlocked" state. A user
+     * is unlocked only after they've entered their credentials (such as a lock
+     * pattern or PIN), and credential-encrypted private app data storage is
+     * available.
+     */
+    public boolean isUserRunningAndUnlocked() {
+        return isUserRunningAndUnlocked(Process.myUserHandle());
+    }
+
+    /**
      * Return whether the given user is running in an "unlocked" state. A user
      * is unlocked only after they've entered their credentials (such as a lock
      * pattern or PIN), and credential-encrypted private app data storage is
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 521aa3c..74fd8cd 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3323,6 +3323,7 @@
             PUBLIC_SETTINGS.add(SOUND_EFFECTS_ENABLED);
             PUBLIC_SETTINGS.add(HAPTIC_FEEDBACK_ENABLED);
             PUBLIC_SETTINGS.add(SHOW_WEB_SUGGESTIONS);
+            PUBLIC_SETTINGS.add(VIBRATE_WHEN_RINGING);
         }
 
         /**
@@ -3344,7 +3345,6 @@
             PRIVATE_SETTINGS.add(VIBRATE_IN_SILENT);
             PRIVATE_SETTINGS.add(MEDIA_BUTTON_RECEIVER);
             PRIVATE_SETTINGS.add(HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY);
-            PRIVATE_SETTINGS.add(VIBRATE_WHEN_RINGING);
             PRIVATE_SETTINGS.add(DTMF_TONE_TYPE_WHEN_DIALING);
             PRIVATE_SETTINGS.add(HEARING_AID);
             PRIVATE_SETTINGS.add(TTY_MODE);
@@ -4912,6 +4912,15 @@
                 "accessibility_display_daltonizer";
 
         /**
+         * Float list that specifies the color matrix to apply to
+         * the display. Valid values are defined in AccessibilityManager.
+         *
+         * @hide
+         */
+        public static final String ACCESSIBILITY_DISPLAY_COLOR_MATRIX =
+                "accessibility_display_color_matrix";
+
+        /**
          * Setting that specifies whether automatic click when the mouse pointer stops moving is
          * enabled.
          *
diff --git a/core/java/android/security/FrameworkNetworkSecurityPolicy.java b/core/java/android/security/FrameworkNetworkSecurityPolicy.java
new file mode 100644
index 0000000..e3dac5e
--- /dev/null
+++ b/core/java/android/security/FrameworkNetworkSecurityPolicy.java
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) 2015, 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.security;
+
+/**
+ * Android framework's implementation of {@link libcore.net.NetworkSecurityPolicy}.
+ *
+ * @hide
+ */
+public class FrameworkNetworkSecurityPolicy extends libcore.net.NetworkSecurityPolicy {
+    private final boolean mCleartextTrafficPermitted;
+
+    public FrameworkNetworkSecurityPolicy(boolean cleartextTrafficPermitted) {
+        mCleartextTrafficPermitted = cleartextTrafficPermitted;
+    }
+
+    @Override
+    public boolean isCleartextTrafficPermitted() {
+        return mCleartextTrafficPermitted;
+    }
+}
diff --git a/core/java/android/security/NetworkSecurityPolicy.java b/core/java/android/security/NetworkSecurityPolicy.java
index 7e87717..7991d37 100644
--- a/core/java/android/security/NetworkSecurityPolicy.java
+++ b/core/java/android/security/NetworkSecurityPolicy.java
@@ -60,7 +60,7 @@
      * <p>NOTE: {@link android.webkit.WebView} does not honor this flag.
      */
     public boolean isCleartextTrafficPermitted() {
-        return libcore.net.NetworkSecurityPolicy.isCleartextTrafficPermitted();
+        return libcore.net.NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted();
     }
 
     /**
@@ -72,6 +72,7 @@
      * @hide
      */
     public void setCleartextTrafficPermitted(boolean permitted) {
-        libcore.net.NetworkSecurityPolicy.setCleartextTrafficPermitted(permitted);
+        FrameworkNetworkSecurityPolicy policy = new FrameworkNetworkSecurityPolicy(permitted);
+        libcore.net.NetworkSecurityPolicy.setInstance(policy);
     }
 }
diff --git a/core/java/android/security/net/config/CertificateSource.java b/core/java/android/security/net/config/CertificateSource.java
index 2b7829e..7e3601e 100644
--- a/core/java/android/security/net/config/CertificateSource.java
+++ b/core/java/android/security/net/config/CertificateSource.java
@@ -23,4 +23,5 @@
 public interface CertificateSource {
     Set<X509Certificate> getCertificates();
     X509Certificate findBySubjectAndPublicKey(X509Certificate cert);
+    X509Certificate findByIssuerAndSignature(X509Certificate cert);
 }
diff --git a/core/java/android/security/net/config/CertificatesEntryRef.java b/core/java/android/security/net/config/CertificatesEntryRef.java
index 1d15e19..ff728ef 100644
--- a/core/java/android/security/net/config/CertificatesEntryRef.java
+++ b/core/java/android/security/net/config/CertificatesEntryRef.java
@@ -51,4 +51,13 @@
 
         return new TrustAnchor(foundCert, mOverridesPins);
     }
+
+    public TrustAnchor findByIssuerAndSignature(X509Certificate cert) {
+        X509Certificate foundCert = mSource.findByIssuerAndSignature(cert);
+        if (foundCert == null) {
+            return null;
+        }
+
+        return new TrustAnchor(foundCert, mOverridesPins);
+    }
 }
diff --git a/core/java/android/security/net/config/DirectoryCertificateSource.java b/core/java/android/security/net/config/DirectoryCertificateSource.java
index a261e06..bf29efa 100644
--- a/core/java/android/security/net/config/DirectoryCertificateSource.java
+++ b/core/java/android/security/net/config/DirectoryCertificateSource.java
@@ -95,6 +95,21 @@
         });
     }
 
+    @Override
+    public X509Certificate findByIssuerAndSignature(final X509Certificate cert) {
+        return findCert(cert.getIssuerX500Principal(), new CertSelector() {
+            @Override
+            public boolean match(X509Certificate ca) {
+                try {
+                    cert.verify(ca.getPublicKey());
+                    return true;
+                } catch (Exception e) {
+                    return false;
+                }
+            }
+        });
+    }
+
     private static interface CertSelector {
         boolean match(X509Certificate cert);
     }
diff --git a/core/java/android/security/net/config/KeyStoreCertificateSource.java b/core/java/android/security/net/config/KeyStoreCertificateSource.java
index 7a01a64..b6105cd 100644
--- a/core/java/android/security/net/config/KeyStoreCertificateSource.java
+++ b/core/java/android/security/net/config/KeyStoreCertificateSource.java
@@ -80,4 +80,14 @@
         }
         return anchor.getTrustedCert();
     }
+
+    @Override
+    public X509Certificate findByIssuerAndSignature(X509Certificate cert) {
+        ensureInitialized();
+        java.security.cert.TrustAnchor anchor = mIndex.findByIssuerAndSignature(cert);
+        if (anchor == null) {
+            return null;
+        }
+        return anchor.getTrustedCert();
+    }
 }
diff --git a/core/java/android/security/net/config/NetworkSecurityConfig.java b/core/java/android/security/net/config/NetworkSecurityConfig.java
index 2ab07b5..0a2edff 100644
--- a/core/java/android/security/net/config/NetworkSecurityConfig.java
+++ b/core/java/android/security/net/config/NetworkSecurityConfig.java
@@ -134,6 +134,17 @@
         return null;
     }
 
+    /** @hide */
+    public TrustAnchor findTrustAnchorByIssuerAndSignature(X509Certificate cert) {
+        for (CertificatesEntryRef ref : mCertificatesEntryRefs) {
+            TrustAnchor anchor = ref.findByIssuerAndSignature(cert);
+            if (anchor != null) {
+                return anchor;
+            }
+        }
+        return null;
+    }
+
     /**
      * Return a {@link Builder} for the default {@code NetworkSecurityConfig}.
      *
diff --git a/core/java/android/security/net/config/NetworkSecurityTrustManager.java b/core/java/android/security/net/config/NetworkSecurityTrustManager.java
index 6013c1e..982ed68 100644
--- a/core/java/android/security/net/config/NetworkSecurityTrustManager.java
+++ b/core/java/android/security/net/config/NetworkSecurityTrustManager.java
@@ -46,17 +46,13 @@
             throw new NullPointerException("config must not be null");
         }
         mNetworkSecurityConfig = config;
-        // TODO: Create our own better KeyStoreImpl
         try {
+            TrustedCertificateStoreAdapter certStore = new TrustedCertificateStoreAdapter(config);
+            // Provide an empty KeyStore since TrustManagerImpl doesn't support null KeyStores.
+            // TrustManagerImpl will use certStore to lookup certificates.
             KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType());
             store.load(null);
-            int certNum = 0;
-            for (TrustAnchor anchor : mNetworkSecurityConfig.getTrustAnchors()) {
-                store.setEntry(String.valueOf(certNum++),
-                        new KeyStore.TrustedCertificateEntry(anchor.certificate),
-                        null);
-            }
-            mDelegate = new TrustManagerImpl(store);
+            mDelegate = new TrustManagerImpl(store, null, certStore);
         } catch (GeneralSecurityException | IOException e) {
             throw new RuntimeException(e);
         }
diff --git a/core/java/android/security/net/config/ResourceCertificateSource.java b/core/java/android/security/net/config/ResourceCertificateSource.java
index b007f8f..e489c2c 100644
--- a/core/java/android/security/net/config/ResourceCertificateSource.java
+++ b/core/java/android/security/net/config/ResourceCertificateSource.java
@@ -90,4 +90,14 @@
         }
         return anchor.getTrustedCert();
     }
+
+    @Override
+    public X509Certificate findByIssuerAndSignature(X509Certificate cert) {
+        ensureInitialized();
+        java.security.cert.TrustAnchor anchor = mIndex.findByIssuerAndSignature(cert);
+        if (anchor == null) {
+            return null;
+        }
+        return anchor.getTrustedCert();
+    }
 }
diff --git a/core/java/android/security/net/config/TrustedCertificateStoreAdapter.java b/core/java/android/security/net/config/TrustedCertificateStoreAdapter.java
new file mode 100644
index 0000000..4a90f82
--- /dev/null
+++ b/core/java/android/security/net/config/TrustedCertificateStoreAdapter.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2015 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.security.net.config;
+
+import java.io.File;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+import java.util.Set;
+
+import com.android.org.conscrypt.TrustedCertificateStore;
+
+/** @hide */
+public class TrustedCertificateStoreAdapter extends TrustedCertificateStore {
+    private final NetworkSecurityConfig mConfig;
+
+    public TrustedCertificateStoreAdapter(NetworkSecurityConfig config) {
+        mConfig = config;
+    }
+
+    @Override
+    public X509Certificate findIssuer(X509Certificate cert) {
+        TrustAnchor anchor = mConfig.findTrustAnchorByIssuerAndSignature(cert);
+        if (anchor == null) {
+            return null;
+        }
+        return anchor.certificate;
+    }
+
+    @Override
+    public X509Certificate getTrustAnchor(X509Certificate cert) {
+        TrustAnchor anchor = mConfig.findTrustAnchorBySubjectAndPublicKey(cert);
+        if (anchor == null) {
+            return null;
+        }
+        return anchor.certificate;
+    }
+
+    @Override
+    public boolean isUserAddedCertificate(X509Certificate cert) {
+        // isUserAddedCertificate is used only for pinning overrides, so use overridesPins here.
+        TrustAnchor anchor = mConfig.findTrustAnchorBySubjectAndPublicKey(cert);
+        if (anchor == null) {
+            return false;
+        }
+        return anchor.overridesPins;
+    }
+
+    @Override
+    public File getCertificateFile(File dir, X509Certificate x) {
+        // getCertificateFile is only used for tests, do not support it here.
+        throw new UnsupportedOperationException();
+    }
+
+    // The methods below are exposed in TrustedCertificateStore but not used by conscrypt, do not
+    // support them.
+
+    @Override
+    public Certificate getCertificate(String alias) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Certificate getCertificate(String alias, boolean includeDeletedSystem) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Date getCreationDate(String alias) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Set<String> aliases() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Set<String> userAliases() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Set<String> allSystemAliases() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean containsAlias(String alias) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getCertificateAlias(Certificate c) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getCertificateAlias(Certificate c, boolean includeDeletedSystem) {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index 2459cfa..57fe131 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -20,7 +20,6 @@
 import android.text.Layout;
 import android.text.Selection;
 import android.text.Spannable;
-import android.view.InputDevice;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
@@ -222,34 +221,26 @@
         return lineEnd(widget, buffer);
     }
 
-    private static boolean isTouchSelecting(boolean isMouse, Spannable buffer) {
-        return isMouse ? Touch.isActivelySelecting(buffer) : isSelecting(buffer);
-    }
-
     @Override
     public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
         int initialScrollX = -1;
         int initialScrollY = -1;
         final int action = event.getAction();
-        final boolean isMouse = event.isFromSource(InputDevice.SOURCE_MOUSE);
 
         if (action == MotionEvent.ACTION_UP) {
             initialScrollX = Touch.getInitialScrollX(widget, buffer);
             initialScrollY = Touch.getInitialScrollY(widget, buffer);
         }
 
-        boolean wasTouchSelecting = isTouchSelecting(isMouse, buffer);
+        boolean wasTouchSelecting = isSelecting(buffer);
         boolean handled = Touch.onTouchEvent(widget, buffer, event);
 
-        if (widget.didTouchFocusSelect() && !isMouse) {
+        if (widget.didTouchFocusSelect()) {
             return handled;
         }
         if (action == MotionEvent.ACTION_DOWN) {
-            // Capture the mouse pointer down location to ensure selection starts
-            // right under the mouse (and is not influenced by cursor location).
-            // The code below needs to run for mouse events.
             // For touch events, the code should run only when selection is active.
-            if (isMouse || isTouchSelecting(isMouse, buffer)) {
+            if (isSelecting(buffer)) {
                 if (!widget.isFocused()) {
                     if (!widget.requestFocus()) {
                         return handled;
@@ -265,15 +256,8 @@
             }
         } else if (widget.isFocused()) {
             if (action == MotionEvent.ACTION_MOVE) {
-                // Cursor can be active at any location in the text while mouse pointer can start
-                // selection from a totally different location. Use LAST_TAP_DOWN span to ensure
-                // text selection will start from mouse pointer location.
-                final int startOffset = buffer.getSpanStart(LAST_TAP_DOWN);
-                if (isMouse && Touch.isSelectionStarted(buffer)) {
-                    Selection.setSelection(buffer, startOffset);
-                }
-
-                if (isTouchSelecting(isMouse, buffer) && handled) {
+                if (isSelecting(buffer) && handled) {
+                    final int startOffset = buffer.getSpanStart(LAST_TAP_DOWN);
                     // Before selecting, make sure we've moved out of the "slop".
                     // handled will be true, if we're in select mode AND we're
                     // OUT of the slop
diff --git a/core/java/android/text/method/Touch.java b/core/java/android/text/method/Touch.java
index fee7377..d9068dc 100644
--- a/core/java/android/text/method/Touch.java
+++ b/core/java/android/text/method/Touch.java
@@ -119,18 +119,12 @@
             ds = buffer.getSpans(0, buffer.length(), DragState.class);
 
             if (ds.length > 0) {
-                ds[0].mIsSelectionStarted = false;
-
                 if (ds[0].mFarEnough == false) {
                     int slop = ViewConfiguration.get(widget.getContext()).getScaledTouchSlop();
 
                     if (Math.abs(event.getX() - ds[0].mX) >= slop ||
                         Math.abs(event.getY() - ds[0].mY) >= slop) {
                         ds[0].mFarEnough = true;
-                        if (event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)) {
-                            ds[0].mIsActivelySelecting = true;
-                            ds[0].mIsSelectionStarted = true;
-                        }
                     }
                 }
 
@@ -142,13 +136,9 @@
                             || MetaKeyKeyListener.getMetaState(buffer,
                                     MetaKeyKeyListener.META_SELECTING) != 0;
 
-                    if (!event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)) {
-                        ds[0].mIsActivelySelecting = false;
-                    }
-
                     float dx;
                     float dy;
-                    if (cap && event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)) {
+                    if (cap) {
                         // if we're selecting, we want the scroll to go in
                         // the direction of the drag
                         dx = event.getX() - ds[0].mX;
@@ -160,7 +150,6 @@
                     ds[0].mX = event.getX();
                     ds[0].mY = event.getY();
 
-                    int nx = widget.getScrollX() + (int) dx;
                     int ny = widget.getScrollY() + (int) dy;
 
                     int padding = widget.getTotalPaddingTop() + widget.getTotalPaddingBottom();
@@ -172,10 +161,6 @@
                     int oldX = widget.getScrollX();
                     int oldY = widget.getScrollY();
 
-                    if (!event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)) {
-                        scrollTo(widget, layout, nx, ny);
-                    }
-
                     // If we actually scrolled, then cancel the up action.
                     if (oldX != widget.getScrollX() || oldY != widget.getScrollY()) {
                         widget.cancelLongPress();
@@ -207,37 +192,6 @@
         return ds.length > 0 ? ds[0].mScrollY : -1;
     }
 
-    /**
-     * Checks if selection is still active.
-     * This is useful for extending Selection span on buffer.
-     * @param buffer The text buffer.
-     * @return true if buffer has been marked for selection.
-     *
-     * @hide
-     */
-    static boolean isActivelySelecting(Spannable buffer) {
-        DragState[] ds;
-        ds = buffer.getSpans(0, buffer.length(), DragState.class);
-
-        return ds.length > 0 && ds[0].mIsActivelySelecting;
-    }
-
-    /**
-     * Checks if selection has begun (are we out of slop?).
-     * Note: DragState.mIsSelectionStarted goes back to false with the very next event.
-     * This is useful for starting Selection span on buffer.
-     * @param buffer The text buffer.
-     * @return true if selection has started on the buffer.
-     *
-     * @hide
-     */
-    static boolean isSelectionStarted(Spannable buffer) {
-        DragState[] ds;
-        ds = buffer.getSpans(0, buffer.length(), DragState.class);
-
-        return ds.length > 0 && ds[0].mIsSelectionStarted;
-    }
-
     private static class DragState implements NoCopySpan {
         public float mX;
         public float mY;
@@ -245,8 +199,6 @@
         public int mScrollY;
         public boolean mFarEnough;
         public boolean mUsed;
-        public boolean mIsActivelySelecting;
-        public boolean mIsSelectionStarted;
 
         public DragState(float x, float y, int scrollX, int scrollY) {
             mX = x;
diff --git a/core/java/android/text/method/WordIterator.java b/core/java/android/text/method/WordIterator.java
index 3688cfa..664d1ea 100644
--- a/core/java/android/text/method/WordIterator.java
+++ b/core/java/android/text/method/WordIterator.java
@@ -20,7 +20,7 @@
 import android.text.Selection;
 import android.text.SpannableStringBuilder;
 
-import java.text.BreakIterator;
+import android.icu.text.BreakIterator;
 import java.util.Locale;
 
 /**
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 527d7e5..0195dec 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -973,7 +973,6 @@
      * </p>
      *
      * @see #getAxisValue(int, int)
-     * {@hide}
      */
     public static final int AXIS_SCROLL = 26;
 
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 6dd87a0..183ccf3 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -83,7 +83,6 @@
 import android.view.AccessibilityIterators.CharacterTextSegmentIterator;
 import android.view.AccessibilityIterators.WordTextSegmentIterator;
 import android.view.AccessibilityIterators.ParagraphTextSegmentIterator;
-import android.view.ViewGroup.LayoutParams;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityEventSource;
 import android.view.accessibility.AccessibilityManager;
@@ -2423,8 +2422,6 @@
      *                    1              PFLAG3_SCROLL_INDICATOR_END
      *                   1               PFLAG3_ASSIST_BLOCKED
      *            1111111                PFLAG3_POINTER_ICON_MASK
-     *           1                       PFLAG3_PARTIAL_LAYOUT_REQUESTED
-     *          1                        PFLAG3_LAYOUT_PARAMS_CHANGED
      * |-------|-------|-------|-------|
      */
 
@@ -2513,7 +2510,6 @@
      */
     static final int PFLAG3_SCROLL_INDICATOR_END = 0x2000;
 
-
     /* End of masks for mPrivateFlags3 */
 
     static final int DRAG_MASK = PFLAG2_DRAG_CAN_ACCEPT | PFLAG2_DRAG_HOVERED;
@@ -2652,19 +2648,6 @@
     private static final int PFLAG3_POINTER_ICON_VALUE_START = 3 << PFLAG3_POINTER_ICON_LSHIFT;
 
     /**
-     * Flag indicating that this view has requested a partial layout and
-     * is added to the AttachInfo's list of views that need a partial layout
-     * request handled on the next traversal.
-     */
-    static final int PFLAG3_PARTIAL_LAYOUT_REQUESTED = 0x800000;
-
-    /**
-     * Flag indicating that this view's LayoutParams have been explicitly changed
-     * since the last layout pass.
-     */
-    static final int PFLAG3_LAYOUT_PARAMS_CHANGED = 0x1000000;
-
-    /**
      * Always allow a user to over-scroll this view, provided it is a
      * view that can scroll.
      *
@@ -12671,14 +12654,10 @@
      * ViewGroup.LayoutParams, and these correspond to the different subclasses
      * of ViewGroup that are responsible for arranging their children.
      *
-     * <p>This method may return null if this View is not attached to a parent
+     * This method may return null if this View is not attached to a parent
      * ViewGroup or {@link #setLayoutParams(android.view.ViewGroup.LayoutParams)}
      * was not invoked successfully. When a View is attached to a parent
-     * ViewGroup, this method must not return null.</p>
-     *
-     * <p>Callers that modify the returned LayoutParams object should call
-     * {@link #setLayoutParams(LayoutParams)} to explicitly inform the view that
-     * LayoutParams have changed.</p>
+     * ViewGroup, this method must not return null.
      *
      * @return The LayoutParams associated with this view, or null if no
      *         parameters have been set yet
@@ -12695,9 +12674,6 @@
      * correspond to the different subclasses of ViewGroup that are responsible
      * for arranging their children.
      *
-     * <p>If the View's existing LayoutParams object as obtained by {@link #getLayoutParams()} is
-     * modified, you should call this method to inform the view that it has changed.</p>
-     *
      * @param params The layout parameters for this view, cannot be null
      */
     public void setLayoutParams(ViewGroup.LayoutParams params) {
@@ -12705,7 +12681,6 @@
             throw new NullPointerException("Layout parameters cannot be null");
         }
         mLayoutParams = params;
-        mPrivateFlags3 |= PFLAG3_LAYOUT_PARAMS_CHANGED;
         resolveLayoutParams();
         if (mParent instanceof ViewGroup) {
             ((ViewGroup) mParent).onSetLayoutParams(this, params);
@@ -14381,12 +14356,7 @@
             mParent.requestTransparentRegion(this);
         }
 
-        if ((mPrivateFlags & PFLAG_AWAKEN_SCROLL_BARS_ON_ATTACH) != 0) {
-            initialAwakenScrollBars();
-            mPrivateFlags &= ~PFLAG_AWAKEN_SCROLL_BARS_ON_ATTACH;
-        }
-
-        mPrivateFlags3 &= ~(PFLAG3_IS_LAID_OUT | PFLAG3_PARTIAL_LAYOUT_REQUESTED);
+        mPrivateFlags3 &= ~PFLAG3_IS_LAID_OUT;
 
         jumpDrawablesToCurrentState();
 
@@ -14724,13 +14694,8 @@
      */
     @CallSuper
     protected void onDetachedFromWindowInternal() {
-        if (mAttachInfo != null && isPartialLayoutRequested()) {
-            mAttachInfo.mPartialLayoutViews.remove(this);
-        }
-
         mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT;
-        mPrivateFlags3 &= ~(PFLAG3_IS_LAID_OUT | PFLAG3_PARTIAL_LAYOUT_REQUESTED
-                | PFLAG3_LAYOUT_PARAMS_CHANGED);
+        mPrivateFlags3 &= ~PFLAG3_IS_LAID_OUT;
 
         removeUnsetPressCallback();
         removeLongPressCallback();
@@ -16917,32 +16882,6 @@
     }
 
     /**
-     * Indicates whether or not this view has requested a partial layout that
-     * may not affect its size or position within its parent. This state will be reset
-     * the next time this view is laid out.
-     *
-     * @return true if partial layout has been requested
-     */
-    public final boolean isPartialLayoutRequested() {
-        return (mPrivateFlags3 & PFLAG3_PARTIAL_LAYOUT_REQUESTED)
-                == PFLAG3_PARTIAL_LAYOUT_REQUESTED;
-    }
-
-    /**
-     * Returns true if this view's {@link ViewGroup.LayoutParams LayoutParams} changed
-     * since the last time this view was successfully laid out. Typically this happens as a
-     * result of a call to {@link #setLayoutParams(LayoutParams)}.
-     *
-     * @return true if this view's LayoutParams changed since last layout.
-     */
-    public final boolean didLayoutParamsChange() {
-        if (sLayoutParamsAlwaysChanged) {
-            return true;
-        }
-        return (mPrivateFlags3 & PFLAG3_LAYOUT_PARAMS_CHANGED) == PFLAG3_LAYOUT_PARAMS_CHANGED;
-    }
-
-    /**
      * Return true if o is a ViewGroup that is laying out using optical bounds.
      * @hide
      */
@@ -16999,7 +16938,6 @@
         if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
             onLayout(changed, l, t, r, b);
             mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
-            mPrivateFlags3 &= ~PFLAG3_LAYOUT_PARAMS_CHANGED;
 
             ListenerInfo li = mListenerInfo;
             if (li != null && li.mOnLayoutChangeListeners != null) {
@@ -17013,7 +16951,6 @@
         }
 
         mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
-        mPrivateFlags3 &= ~PFLAG3_PARTIAL_LAYOUT_REQUESTED;
         mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
     }
 
@@ -19111,7 +19048,7 @@
         mPrivateFlags |= PFLAG_INVALIDATED;
 
         if (mParent != null && !mParent.isLayoutRequested()) {
-            mParent.requestLayoutForChild(this);
+            mParent.requestLayout();
         }
         if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
             mAttachInfo.mViewRequestingLayout = null;
@@ -19130,11 +19067,6 @@
         mPrivateFlags |= PFLAG_INVALIDATED;
     }
 
-    void forcePartialLayout() {
-        forceLayout();
-        mPrivateFlags3 |= PFLAG3_PARTIAL_LAYOUT_REQUESTED;
-    }
-
     /**
      * <p>
      * This is called to find out how big a view should be. The parent
@@ -22021,7 +21953,6 @@
         interface Callbacks {
             void playSoundEffect(int effectId);
             boolean performHapticFeedback(int effectId, boolean always);
-            void schedulePartialLayout();
         }
 
         /**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 488063b..0f7d296 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -60,8 +60,6 @@
 import java.util.List;
 import java.util.Map;
 import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 
 /**
  * <p>
@@ -5531,172 +5529,6 @@
             int l, int t, int r, int b);
 
     /**
-     * {@inheritDoc}
-     *
-     * <p>Most subclasses should not need to override this method. The default implementation
-     * will call {@link #findDependentLayoutAxes(View, int)} to determine how
-     * to optimally proceed. If neither horizontal nor vertical layout depends on the given
-     * child, this method will call {@link #requestPartialLayoutForChild(View)}. If one or both
-     * do, it will call {@link #requestLayout()}.</p>
-     *
-     * @param child Child requesting a layout
-     */
-    @Override
-    public void requestLayoutForChild(View child) {
-        if (child == null || child.getParent() != this) {
-            throw new IllegalArgumentException(
-                    "child parameter must be a direct child view of this ViewGroup");
-        }
-
-        // If we don't have a parent ourselves, record that we need a full layout.
-        // Our whole subtree is detached.
-        final ViewParent parent = getParent();
-        if (parent == null) {
-            requestLayout();
-            return;
-        }
-
-        // We can optimize the layout request for this child into a partial layout
-        // if the child has already been laid out at least once and neither horizontal nor
-        // vertical layout within ourselves is dependent on pending layout changes within
-        // this child. Otherwise we need to request a full layout for ourselves and continue
-        // to recurse up the view hierarchy.
-        if (child.isLaidOut() && findDependentLayoutAxes(child, FLAG_LAYOUT_AXIS_ANY) == 0) {
-            requestPartialLayoutForChild(child);
-        } else {
-            requestLayout();
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>The default implementation returns {@link #FLAG_LAYOUT_AXIS_ANY}.
-     * Optimized implementations for specific ViewGroup subclasses may check if the child's
-     * {@link View#didLayoutParamsChange() LayoutParams changed} and in what ways.</p>
-     *
-     * @param child Direct child of this ViewParent to check
-     * @param axisFilter Which axes to check for dependencies. Can be
-     *                   {@link #FLAG_LAYOUT_AXIS_HORIZONTAL}, {@link #FLAG_LAYOUT_AXIS_VERTICAL}
-     *                   or {@link #FLAG_LAYOUT_AXIS_ANY}.
-     * @return Axes of this ViewParent that depend on the given child's layout changes
-     */
-    @Override
-    public int findDependentLayoutAxes(View child, int axisFilter) {
-        return FLAG_LAYOUT_AXIS_ANY;
-    }
-
-    /**
-     * This is a helper implementation for {@link #findDependentLayoutAxes(View, int)} that
-     * is not the default implementation in ViewGroup. This is to preserve compatibility with
-     * existing app-side ViewGroup subclasses that existed before the partial layout system was
-     * added to Android. It explicitly checks that the LayoutParams of the child are of the
-     * expected type so that subclasses of standard framework layouts do not erroneously
-     * start believing that it's safe to do a partial layout when that assertion can't
-     * reasonably be confirmed.
-     *
-     * <p>If you're reading this as an author of a custom ViewGroup's findDependentLayoutAxes
-     * method you might be frustrated to discover that it is not a part of the Android public API.
-     * Many ViewGroup implementations will need to make small but important modifications
-     * to an implementation like this one in order to be correct. Instead of encouraging
-     * view authors to call this method, then make their own redundant recursive calls to
-     * <code>getParent().findDependentLayoutAxes(...)</code> in addition to the one
-     * that can happen here, this method is hidden and only used internally.</p>
-     *
-     * <p>Do feel free to copy this implementation and adapt it to suit your own purposes.</p>
-     *
-     * @hide
-     */
-    protected final int findDependentLayoutAxesHelper(View child, int axisFilter,
-            Class<?> layoutParamsClass) {
-        if (!checkPartialLayoutParams(child, layoutParamsClass)) return axisFilter;
-        if (child.didLayoutParamsChange()) {
-            // Anything could have changed about our previous assumptions.
-            return axisFilter;
-        }
-
-        final LayoutParams lp = child.getLayoutParams();
-
-        // Our layout can always end up depending on a WRAP_CONTENT child.
-        final int wrapAxisFilter = ((lp.width == WRAP_CONTENT ? FLAG_LAYOUT_AXIS_HORIZONTAL : 0)
-                | (lp.height == WRAP_CONTENT ? FLAG_LAYOUT_AXIS_VERTICAL : 0)) & axisFilter;
-
-        if (wrapAxisFilter == axisFilter) {
-            // We know all queried axes are affected, just return early.
-            return wrapAxisFilter;
-        }
-
-        // Our layout *may* depend on a MATCH_PARENT child, depending on whether we can determine
-        // that our layout will remain stable within our parent. We need to ask.
-        final int matchAxisFilter = ((lp.width == MATCH_PARENT ? FLAG_LAYOUT_AXIS_HORIZONTAL : 0)
-                | (lp.height == MATCH_PARENT ? FLAG_LAYOUT_AXIS_VERTICAL : 0)) & axisFilter;
-
-        if (matchAxisFilter != 0 || wrapAxisFilter != 0) {
-            final ViewParent parent = getParent();
-            if (parent != null) {
-                // If our parent depends on us for an axis, then our layout can also be affected
-                // by a MATCH_PARENT child along that axis.
-                return getParent().findDependentLayoutAxes(this, matchAxisFilter)
-                        | wrapAxisFilter;
-            }
-
-            // If we don't have a parent, assume we're affected
-            // in any determined affected direction.
-            return matchAxisFilter | wrapAxisFilter;
-        }
-
-        // Two exact sizes and LayoutParams didn't change. We're safe.
-        return 0;
-    }
-
-    /**
-     * Throw an IllegalArgumentException if the supplied view is not a direct child of
-     * this ViewGroup and return false if this view's LayoutParams is not of class lpClass.
-     * Implementations of {@link ViewGroup#findDependentLayoutAxes(View, int)} use this
-     * to check input parameters and defensively return the full axis filter mask themselves
-     * if the LayoutParams class is not of the exact expected type; e.g. it is a subclass
-     * of one of the standard framework layouts and we can't make assumptions.
-     * @hide
-     */
-    protected final boolean checkPartialLayoutParams(View child, Class<?> lpClass) {
-        if (child.getParent() != this) {
-            throw new IllegalArgumentException("View " + child
-                    + " is not a direct child of " + this);
-        }
-        final ViewGroup.LayoutParams lp = child.getLayoutParams();
-        return lp != null || lp.getClass() == lpClass;
-    }
-
-    /**
-     * Called when a child of this ViewParent requires a relayout before the next frame
-     * is drawn, but the caller can guarantee that the size of the child will not change
-     * during a measure and layout pass.
-     *
-     * <p>A call to this method will schedule a partial layout for the supplied view as long as
-     * it is a direct child of this ViewGroup and this ViewGroup is attached to a window.
-     * On the next scheduled view hierarchy traversal the given child view will be re-measured
-     * at its current measured size and re-laid out at its current position within its parent.</p>
-     *
-     * @param child Child that requires a partial layout
-     */
-    public void requestPartialLayoutForChild(View child) {
-        if (!child.isPartialLayoutRequested()) {
-            child.forcePartialLayout();
-            if (mAttachInfo != null) {
-                final List<View> partialLayoutViews = mAttachInfo.mPartialLayoutViews;
-                final boolean schedule = partialLayoutViews.isEmpty();
-                partialLayoutViews.add(child);
-                if (schedule) {
-                    mAttachInfo.mRootCallbacks.schedulePartialLayout();
-                }
-                child.invalidate();
-            } else {
-                requestLayout();
-            }
-        }
-    }
-
-    /**
      * Indicates whether the view group has the ability to animate its children
      * after the first layout.
      *
@@ -6042,7 +5874,7 @@
      *         of its descendants
      */
     protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
-        return new LayoutParams(p);
+        return p;
     }
 
     /**
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index f2ab35e..e9b123b5 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -26,11 +26,6 @@
  * 
  */
 public interface ViewParent {
-    public static final int FLAG_LAYOUT_AXIS_HORIZONTAL = 1;
-    public static final int FLAG_LAYOUT_AXIS_VERTICAL = 2;
-    public static final int FLAG_LAYOUT_AXIS_ANY
-            = FLAG_LAYOUT_AXIS_HORIZONTAL | FLAG_LAYOUT_AXIS_VERTICAL;
-
     /**
      * Called when something has changed which has invalidated the layout of a
      * child of this view parent. This will schedule a layout pass of the view
@@ -618,48 +613,4 @@
      * @return true if the action was consumed by this ViewParent
      */
     public boolean onNestedPrePerformAccessibilityAction(View target, int action, Bundle arguments);
-
-    /**
-     * Called when a child of this ViewParent requires a relayout before
-     * the next frame is drawn. A call to {@link View#requestLayout() child.requestLayout()}
-     * will implicitly result in a call to
-     * <code>child.getParent().requestLayoutForChild(child)</code>. App code should not call this
-     * method directly. Call <code>child.requestLayout()</code> instead.
-     *
-     * <p>On versions of Android from API 23 and older, a call to {@link View#requestLayout()}
-     * would cause a matching call to <code>requestLayout</code> on each parent view up to
-     * the root. With the addition of <code>requestLayoutForChild</code> a view's parent may
-     * explicitly decide how to handle a layout request. This allows for optimizations when
-     * a view parent knows that a layout-altering change in a child will not affect its own
-     * measurement.</p>
-     *
-     * @param child Child requesting a layout
-     */
-    public void requestLayoutForChild(View child);
-
-    /**
-     * Determine which axes of this ViewParent's layout are dependent on the given
-     * direct child view. The returned value is a flag set that may contain
-     * {@link #FLAG_LAYOUT_AXIS_HORIZONTAL} and/or {@link #FLAG_LAYOUT_AXIS_VERTICAL}.
-     * {@link #FLAG_LAYOUT_AXIS_ANY} is provided as a shortcut for
-     * <code>FLAG_LAYOUT_AXIS_HORIZONTAL | FLAG_LAYOUT_AXIS_VERTICAL</code>.
-     *
-     * <p>The given child must be a direct child view. Implementations should throw
-     * {@link IllegalArgumentException} otherwise.</p>
-     *
-     * <p>The caller may specify which axes it cares about. This should be treated as a filter.
-     * Implementations should never return a result that would be different from
-     * <code>result & axisFilter</code>.</p>
-     *
-     * @param child Direct child of this ViewParent to check
-     * @param axisFilter Which axes to check for dependencies. Can be
-     *                   {@link #FLAG_LAYOUT_AXIS_HORIZONTAL}, {@link #FLAG_LAYOUT_AXIS_VERTICAL}
-     *                   or {@link #FLAG_LAYOUT_AXIS_ANY}.
-     * @return Axes of this ViewParent that depend on the given child's layout changes
-     *
-     * @see #FLAG_LAYOUT_AXIS_HORIZONTAL
-     * @see #FLAG_LAYOUT_AXIS_VERTICAL
-     * @see #FLAG_LAYOUT_AXIS_ANY
-     */
-    public int findDependentLayoutAxes(View child, int axisFilter);
 }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index b7bb9a3..12cf66e 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -91,7 +91,6 @@
 import java.util.ArrayList;
 import java.util.concurrent.CountDownLatch;
 import java.util.HashSet;
-import java.util.List;
 
 /**
  * The top of a view hierarchy, implementing the needed protocol between View
@@ -953,25 +952,6 @@
     }
 
     @Override
-    public void requestLayoutForChild(View child) {
-        requestLayout();
-    }
-
-    @Override
-    public int findDependentLayoutAxes(View child, int axisFilter) {
-        if (child != mView) {
-            return 0;
-        }
-
-        final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) child.getLayoutParams();
-        final int horizontal = (lp.width == WindowManager.LayoutParams.WRAP_CONTENT
-                || lp.horizontalWeight != 0) ? FLAG_LAYOUT_AXIS_HORIZONTAL : 0;
-        final int vertical = (lp.height == WindowManager.LayoutParams.WRAP_CONTENT
-                || lp.verticalWeight != 0) ? FLAG_LAYOUT_AXIS_VERTICAL : 0;
-        return (horizontal | vertical) & axisFilter;
-    }
-
-    @Override
     public boolean isLayoutRequested() {
         return mLayoutRequested;
     }
@@ -1115,10 +1095,6 @@
         }
     }
 
-    public void schedulePartialLayout() {
-        scheduleTraversals();
-    }
-
     /**
      * Notifies the HardwareRenderer that a new frame will be coming soon.
      * Currently only {@link ThreadedRenderer} cares about this, and uses
@@ -1958,60 +1934,7 @@
                 || mAttachInfo.mRecomputeGlobalAttributes;
         if (didLayout) {
             performLayout(lp, desiredWindowWidth, desiredWindowHeight);
-        }
 
-        /*
-         * Handle partial layouts.
-         *
-         * Views that have requested partial layouts will not change size or position
-         * within their parent view, therefore we will re-measure and re-layout each one
-         * after any regularly scheduled layout pass. Any view that already had its
-         * isLayoutRequested bit cleared will be skipped, since this means the view has already
-         * been measured and laid out on this traversal pass naturally. Views won't be added
-         * to this list if layout was already requested when a partial layout is requested
-         * for a view, so there should not be duplicates in the list.
-         */
-        final List<View> partialLayoutViews = mAttachInfo.mPartialLayoutViews;
-        final boolean didPartialLayout;
-        if (!partialLayoutViews.isEmpty()) {
-            // Measurement or layout of views may result in changes to the list
-            // of partial-layout views. Swap in an "empty" list to prevent
-            // concurrent modification of the list being traversed.
-            if (mAttachInfo.mEmptyPartialLayoutViews == null) {
-                mAttachInfo.mPartialLayoutViews = new ArrayList<>();
-            } else {
-                mAttachInfo.mPartialLayoutViews = mAttachInfo.mEmptyPartialLayoutViews;
-            }
-
-            final int count = partialLayoutViews.size();
-            mInLayout = true;
-            for (int i = 0; i < count; i++) {
-                final View view = partialLayoutViews.get(i);
-
-                // Make sure the view is still attached and that it still has layout requested.
-                // We might have already serviced the layout request through the standard full-tree
-                // layout pass above or even through a previous partial layout view in this list.
-                if (view.isAttachedToWindow() && view.isLayoutRequested()) {
-                    final int widthSpec = MeasureSpec.makeMeasureSpec(view.getMeasuredWidth(),
-                            MeasureSpec.EXACTLY);
-                    final int heightSpec = MeasureSpec.makeMeasureSpec(view.getMeasuredHeight(),
-                            MeasureSpec.EXACTLY);
-                    view.measure(widthSpec, heightSpec);
-                    view.layout(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
-                }
-            }
-            mInLayout = false;
-            didPartialLayout = true;
-            triggerGlobalLayoutListener = true;
-
-            // The traversal list becomes the new empty list.
-            partialLayoutViews.clear();
-            mAttachInfo.mEmptyPartialLayoutViews = partialLayoutViews;
-        } else {
-            didPartialLayout = false;
-        }
-
-        if (didLayout || didPartialLayout) {
             // By this point all views have been sized and positioned
             // We can compute the transparent area
 
@@ -2041,7 +1964,7 @@
 
             if (DBG) {
                 System.out.println("======================================");
-                System.out.println("performTraversals -- after performLayout/partial layout");
+                System.out.println("performTraversals -- after setFrame");
                 host.debug();
             }
         }
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 7a359e7..53490b4 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -29,6 +29,7 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.PixelFormat;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.media.session.MediaController;
 import android.net.Uri;
@@ -247,12 +248,30 @@
 
     private static final String PROPERTY_HARDWARE_UI = "persist.sys.ui.hw";
 
+    /**
+     * Flag for letting the theme drive the color of the window caption controls. Use with
+     * {@link #setDecorCaptionShade(int)}. This is the default value.
+     */
+    public static final int DECOR_CAPTION_SHADE_AUTO = 0;
+    /**
+     * Flag for setting light-color controls on the window caption. Use with
+     * {@link #setDecorCaptionShade(int)}.
+     */
+    public static final int DECOR_CAPTION_SHADE_LIGHT = 1;
+    /**
+     * Flag for setting dark-color controls on the window caption. Use with
+     * {@link #setDecorCaptionShade(int)}.
+     */
+    public static final int DECOR_CAPTION_SHADE_DARK = 2;
+
     private final Context mContext;
 
     private TypedArray mWindowStyle;
     private Callback mCallback;
     private OnWindowDismissedCallback mOnWindowDismissedCallback;
     private WindowControllerCallback mWindowControllerCallback;
+    private RestrictedCaptionAreaListener mRestrictedCaptionAreaListener;
+    private Rect mRestrictedCaptionAreaRect;
     private WindowManager mWindowManager;
     private IBinder mAppToken;
     private String mAppName;
@@ -565,6 +584,18 @@
         int getWindowStackId() throws RemoteException;
     }
 
+    /**
+     * Callback for clients that want to be aware of where caption draws content.
+     */
+    public interface RestrictedCaptionAreaListener {
+        /**
+         * Called when the area where caption draws content changes.
+         *
+         * @param rect The area where caption content is positioned, relative to the top view.
+         */
+        void onRestrictedCaptionAreaChanged(Rect rect);
+    }
+
     public Window(Context context) {
         mContext = context;
         mFeatures = mLocalFeatures = getDefaultFeatures(context);
@@ -778,6 +809,16 @@
     }
 
     /**
+     * Set a callback for changes of area where caption will draw its content.
+     *
+     * @param listener Callback that will be called when the area changes.
+     */
+    public final void setRestrictedCaptionAreaListener(RestrictedCaptionAreaListener listener) {
+        mRestrictedCaptionAreaListener = listener;
+        mRestrictedCaptionAreaRect = listener != null ? new Rect() : null;
+    }
+
+    /**
      * Take ownership of this window's surface.  The window's view hierarchy
      * will no longer draw into the surface, though it will otherwise continue
      * to operate (such as for receiving input events).  The given SurfaceHolder
@@ -2040,5 +2081,29 @@
         return mOverlayWithDecorCaption;
     }
 
+    /** @hide */
+    public void notifyRestrictedCaptionAreaCallback(int left, int top, int right, int bottom) {
+        if (mRestrictedCaptionAreaListener != null) {
+            mRestrictedCaptionAreaRect.set(left, top, right, bottom);
+            mRestrictedCaptionAreaListener.onRestrictedCaptionAreaChanged(
+                    mRestrictedCaptionAreaRect);
+        }
+    }
 
+    /**
+     * Set what color should the caption controls be. By default the system will try to determine
+     * the color from the theme. You can overwrite this by using {@link #DECOR_CAPTION_SHADE_DARK}
+     * or {@link #DECOR_CAPTION_SHADE_DARK}.
+     */
+    public abstract void setDecorCaptionShade(int decorCaptionShade);
+
+    /**
+     * Set the drawable that is drawn underneath the caption during the resizing.
+     *
+     * During the resizing the caption might not be drawn fast enough to match the new dimensions.
+     * There is a second caption drawn underneath it that will be fast enough. By default the
+     * caption is constructed from the theme. You can provide a drawable, that will be drawn instead
+     * to better match your application.
+     */
+    public abstract void setResizingCaptionDrawable(Drawable drawable);
 }
diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java
index fbaf51c..a42f4d9 100644
--- a/core/java/android/view/inputmethod/InputMethodSubtype.java
+++ b/core/java/android/view/inputmethod/InputMethodSubtype.java
@@ -16,6 +16,7 @@
 
 package android.view.inputmethod;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -50,6 +51,7 @@
  *
  * @attr ref android.R.styleable#InputMethod_Subtype_label
  * @attr ref android.R.styleable#InputMethod_Subtype_icon
+ * @attr ref android.R.styleable#InputMethod_Subtype_languageTag
  * @attr ref android.R.styleable#InputMethod_Subtype_imeSubtypeLocale
  * @attr ref android.R.styleable#InputMethod_Subtype_imeSubtypeMode
  * @attr ref android.R.styleable#InputMethod_Subtype_imeSubtypeExtraValue
@@ -60,6 +62,7 @@
  */
 public final class InputMethodSubtype implements Parcelable {
     private static final String TAG = InputMethodSubtype.class.getSimpleName();
+    private static final String LANGUAGE_TAG_NONE = "";
     private static final String EXTRA_VALUE_PAIR_SEPARATOR = ",";
     private static final String EXTRA_VALUE_KEY_VALUE_SEPARATOR = "=";
     // TODO: remove this
@@ -74,6 +77,7 @@
     private final int mSubtypeNameResId;
     private final int mSubtypeId;
     private final String mSubtypeLocale;
+    private final String mSubtypeLanguageTag;
     private final String mSubtypeMode;
     private final String mSubtypeExtraValue;
     private volatile HashMap<String, String> mExtraValueHashMapCache;
@@ -171,6 +175,15 @@
         private String mSubtypeLocale = "";
 
         /**
+         * @param languageTag is the BCP-47 Language Tag supported by this subtype.
+         */
+        public InputMethodSubtypeBuilder setLanguageTag(String languageTag) {
+            mSubtypeLanguageTag = languageTag == null ? LANGUAGE_TAG_NONE : languageTag;
+            return this;
+        }
+        private String mSubtypeLanguageTag = LANGUAGE_TAG_NONE;
+
+        /**
          * @param subtypeMode is the mode supported by this subtype.
          */
         public InputMethodSubtypeBuilder setSubtypeMode(String subtypeMode) {
@@ -271,6 +284,7 @@
         mSubtypeNameResId = builder.mSubtypeNameResId;
         mSubtypeIconResId = builder.mSubtypeIconResId;
         mSubtypeLocale = builder.mSubtypeLocale;
+        mSubtypeLanguageTag = builder.mSubtypeLanguageTag;
         mSubtypeMode = builder.mSubtypeMode;
         mSubtypeExtraValue = builder.mSubtypeExtraValue;
         mIsAuxiliary = builder.mIsAuxiliary;
@@ -291,6 +305,8 @@
         s = source.readString();
         mSubtypeLocale = s != null ? s : "";
         s = source.readString();
+        mSubtypeLanguageTag = s != null ? s : LANGUAGE_TAG_NONE;
+        s = source.readString();
         mSubtypeMode = s != null ? s : "";
         s = source.readString();
         mSubtypeExtraValue = s != null ? s : "";
@@ -318,21 +334,38 @@
     /**
      * @return The locale of the subtype. This method returns the "locale" string parameter passed
      * to the constructor.
+     *
+     * @deprecated Use {@link #getLanguageTag()} instead.
      */
+    @Deprecated
+    @NonNull
     public String getLocale() {
         return mSubtypeLocale;
     }
 
     /**
-     * @return The normalized {@link Locale} object of the subtype. The returned locale may or may
-     * not equal to "locale" string parameter passed to the constructor.
+     * @return the BCP-47 Language Tag of the subtype.  Returns an empty string when no Language Tag
+     * is specified.
      *
-     * <p>TODO: Consider to make this a public API.</p>
+     * @see Locale#forLanguageTag(String)
+     */
+    @NonNull
+    public String getLanguageTag() {
+        return mSubtypeLanguageTag;
+    }
+
+    /**
+     * @return {@link Locale} constructed from {@link #getLanguageTag()}. If the Language Tag is not
+     * specified, then try to construct from {@link #getLocale()}
+     *
+     * <p>TODO: Consider to make this a public API, or move this to support lib.</p>
      * @hide
      */
     @Nullable
     public Locale getLocaleObject() {
-        // TODO: Move the following method from InputMethodUtils to InputMethodSubtype.
+        if (!TextUtils.isEmpty(mSubtypeLanguageTag)) {
+            return Locale.forLanguageTag(mSubtypeLanguageTag);
+        }
         return InputMethodUtils.constructLocaleFromString(mSubtypeLocale);
     }
 
@@ -476,13 +509,14 @@
                 return (subtype.hashCode() == hashCode());
             }
             return (subtype.hashCode() == hashCode())
-                && (subtype.getLocale().equals(getLocale()))
-                && (subtype.getMode().equals(getMode()))
-                && (subtype.getExtraValue().equals(getExtraValue()))
-                && (subtype.isAuxiliary() == isAuxiliary())
-                && (subtype.overridesImplicitlyEnabledSubtype()
-                        == overridesImplicitlyEnabledSubtype())
-                && (subtype.isAsciiCapable() == isAsciiCapable());
+                    && (subtype.getLocale().equals(getLocale()))
+                    && (subtype.getLanguageTag().equals(getLanguageTag()))
+                    && (subtype.getMode().equals(getMode()))
+                    && (subtype.getExtraValue().equals(getExtraValue()))
+                    && (subtype.isAuxiliary() == isAuxiliary())
+                    && (subtype.overridesImplicitlyEnabledSubtype()
+                            == overridesImplicitlyEnabledSubtype())
+                    && (subtype.isAsciiCapable() == isAsciiCapable());
         }
         return false;
     }
@@ -497,6 +531,7 @@
         dest.writeInt(mSubtypeNameResId);
         dest.writeInt(mSubtypeIconResId);
         dest.writeString(mSubtypeLocale);
+        dest.writeString(mSubtypeLanguageTag);
         dest.writeString(mSubtypeMode);
         dest.writeString(mSubtypeExtraValue);
         dest.writeInt(mIsAuxiliary ? 1 : 0);
diff --git a/core/java/android/view/textservice/SpellCheckerInfo.java b/core/java/android/view/textservice/SpellCheckerInfo.java
index 491de78..471b6d4 100644
--- a/core/java/android/view/textservice/SpellCheckerInfo.java
+++ b/core/java/android/view/textservice/SpellCheckerInfo.java
@@ -117,6 +117,8 @@
                             a.getString(com.android.internal.R.styleable
                                     .SpellChecker_Subtype_subtypeLocale),
                             a.getString(com.android.internal.R.styleable
+                                    .SpellChecker_Subtype_languageTag),
+                            a.getString(com.android.internal.R.styleable
                                     .SpellChecker_Subtype_subtypeExtraValue),
                             a.getInt(com.android.internal.R.styleable
                                     .SpellChecker_Subtype_subtypeId, 0));
diff --git a/core/java/android/view/textservice/SpellCheckerSubtype.java b/core/java/android/view/textservice/SpellCheckerSubtype.java
index f2b03cc..df33698 100644
--- a/core/java/android/view/textservice/SpellCheckerSubtype.java
+++ b/core/java/android/view/textservice/SpellCheckerSubtype.java
@@ -18,6 +18,7 @@
 
 import com.android.internal.inputmethod.InputMethodUtils;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -40,6 +41,7 @@
  * @see SpellCheckerInfo
  *
  * @attr ref android.R.styleable#SpellChecker_Subtype_label
+ * @attr ref android.R.styleable#SpellChecker_Subtype_languageTag
  * @attr ref android.R.styleable#SpellChecker_Subtype_subtypeLocale
  * @attr ref android.R.styleable#SpellChecker_Subtype_subtypeExtraValue
  * @attr ref android.R.styleable#SpellChecker_Subtype_subtypeId
@@ -49,11 +51,13 @@
     private static final String EXTRA_VALUE_PAIR_SEPARATOR = ",";
     private static final String EXTRA_VALUE_KEY_VALUE_SEPARATOR = "=";
     private static final int SUBTYPE_ID_NONE = 0;
+    private static final String SUBTYPE_LANGUAGE_TAG_NONE = "";
 
     private final int mSubtypeId;
     private final int mSubtypeHashCode;
     private final int mSubtypeNameResId;
     private final String mSubtypeLocale;
+    private final String mSubtypeLanguageTag;
     private final String mSubtypeExtraValue;
     private HashMap<String, String> mExtraValueHashMapCache;
 
@@ -66,14 +70,17 @@
      *
      * @param nameId The name of the subtype
      * @param locale The locale supported by the subtype
+     * @param languageTag The BCP-47 Language Tag associated with this subtype.
      * @param extraValue The extra value of the subtype
      * @param subtypeId The subtype ID that is supposed to be stable during package update.
      *
      * @hide
      */
-    public SpellCheckerSubtype(int nameId, String locale, String extraValue, int subtypeId) {
+    public SpellCheckerSubtype(int nameId, String locale, String languageTag, String extraValue,
+            int subtypeId) {
         mSubtypeNameResId = nameId;
         mSubtypeLocale = locale != null ? locale : "";
+        mSubtypeLanguageTag = languageTag != null ? languageTag : SUBTYPE_LANGUAGE_TAG_NONE;
         mSubtypeExtraValue = extraValue != null ? extraValue : "";
         mSubtypeId = subtypeId;
         mSubtypeHashCode = mSubtypeId != SUBTYPE_ID_NONE ?
@@ -91,7 +98,7 @@
      * to instantiate {@link SpellCheckerSubtype} object.
      */
     public SpellCheckerSubtype(int nameId, String locale, String extraValue) {
-        this(nameId, locale, extraValue, SUBTYPE_ID_NONE);
+        this(nameId, locale, SUBTYPE_LANGUAGE_TAG_NONE, extraValue, SUBTYPE_ID_NONE);
     }
 
     SpellCheckerSubtype(Parcel source) {
@@ -100,6 +107,8 @@
         s = source.readString();
         mSubtypeLocale = s != null ? s : "";
         s = source.readString();
+        mSubtypeLanguageTag = s != null ? s : "";
+        s = source.readString();
         mSubtypeExtraValue = s != null ? s : "";
         mSubtypeId = source.readInt();
         mSubtypeHashCode = mSubtypeId != SUBTYPE_ID_NONE ?
@@ -115,12 +124,27 @@
 
     /**
      * @return the locale of the subtype
+     *
+     * @deprecated Use {@link #getLanguageTag()} instead.
      */
+    @Deprecated
+    @NonNull
     public String getLocale() {
         return mSubtypeLocale;
     }
 
     /**
+     * @return the BCP-47 Language Tag of the subtype.  Returns an empty string when no Language Tag
+     * is specified.
+     *
+     * @see Locale#forLanguageTag(String)
+     */
+    @NonNull
+    public String getLanguageTag() {
+        return mSubtypeLanguageTag;
+    }
+
+    /**
      * @return the extra value of the subtype
      */
     public String getExtraValue() {
@@ -182,20 +206,24 @@
             return (subtype.hashCode() == hashCode())
                     && (subtype.getNameResId() == getNameResId())
                     && (subtype.getLocale().equals(getLocale()))
+                    && (subtype.getLanguageTag().equals(getLanguageTag()))
                     && (subtype.getExtraValue().equals(getExtraValue()));
         }
         return false;
     }
 
     /**
-     * @return The normalized {@link Locale} object of the subtype. The returned locale may or may
-     * not equal to "locale" string parameter passed to the constructor.
+     * @return {@link Locale} constructed from {@link #getLanguageTag()}. If the Language Tag is not
+     * specified, then try to construct from {@link #getLocale()}
      *
-     * <p>TODO: Consider to make this a public API.</p>
+     * <p>TODO: Consider to make this a public API, or move this to support lib.</p>
      * @hide
      */
     @Nullable
     public Locale getLocaleObject() {
+        if (!TextUtils.isEmpty(mSubtypeLanguageTag)) {
+            return Locale.forLanguageTag(mSubtypeLanguageTag);
+        }
         return InputMethodUtils.constructLocaleFromString(mSubtypeLocale);
     }
 
@@ -234,6 +262,7 @@
     public void writeToParcel(Parcel dest, int parcelableFlags) {
         dest.writeInt(mSubtypeNameResId);
         dest.writeString(mSubtypeLocale);
+        dest.writeString(mSubtypeLanguageTag);
         dest.writeString(mSubtypeExtraValue);
         dest.writeInt(mSubtypeId);
     }
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index f050e49..e1ce9fe 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2109,11 +2109,6 @@
     }
 
     @Override
-    public int findDependentLayoutAxes(View child, int axisFilter) {
-        return findDependentLayoutAxesHelper(child, axisFilter, LayoutParams.class);
-    }
-
-    @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         if (mSelector == null) {
             useDefaultSelector();
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 5146bc6..2d1f855 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -75,6 +75,7 @@
 import android.view.DisplayListCanvas;
 import android.view.DragEvent;
 import android.view.Gravity;
+import android.view.InputDevice;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
@@ -229,7 +230,14 @@
     // Set when this TextView gained focus with some text selected. Will start selection mode.
     boolean mCreatedWithASelection;
 
-    boolean mDoubleTap = false;
+    // Indicates the current tap state (first tap, double tap, or triple click).
+    private int mTapState = TAP_STATE_INITIAL;
+    private long mLastTouchUpTime = 0;
+    private static final int TAP_STATE_INITIAL = 0;
+    private static final int TAP_STATE_FIRST_TAP = 1;
+    private static final int TAP_STATE_DOUBLE_TAP = 2;
+    // Only for mouse input.
+    private static final int TAP_STATE_TRIPLE_CLICK = 3;
 
     private Runnable mInsertionActionModeRunnable;
 
@@ -769,20 +777,12 @@
         return retOffset;
     }
 
-    /**
-     * Adjusts selection to the word under last touch offset. Return true if the operation was
-     * successfully performed.
-     */
-    private boolean selectCurrentWord() {
-        if (!mTextView.canSelectText()) {
-            return false;
-        }
-
+    private boolean needsToSelectAllToSelectWordOrParagraph() {
         if (mTextView.hasPasswordTransformationMethod()) {
             // Always select all on a password field.
             // Cut/copy menu entries are not available for passwords, but being able to select all
             // is however useful to delete or paste to replace the entire content.
-            return mTextView.selectAllText();
+            return true;
         }
 
         int inputType = mTextView.getInputType();
@@ -797,6 +797,21 @@
                 variation == InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS ||
                 variation == InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS ||
                 variation == InputType.TYPE_TEXT_VARIATION_FILTER) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Adjusts selection to the word under last touch offset. Return true if the operation was
+     * successfully performed.
+     */
+    private boolean selectCurrentWord() {
+        if (!mTextView.canSelectText()) {
+            return false;
+        }
+
+        if (needsToSelectAllToSelectWordOrParagraph()) {
             return mTextView.selectAllText();
         }
 
@@ -805,8 +820,8 @@
         final int maxOffset = TextUtils.unpackRangeEndFromLong(lastTouchOffsets);
 
         // Safety check in case standard touch event handling has been bypassed
-        if (minOffset < 0 || minOffset >= mTextView.getText().length()) return false;
-        if (maxOffset < 0 || maxOffset >= mTextView.getText().length()) return false;
+        if (minOffset < 0 || minOffset > mTextView.getText().length()) return false;
+        if (maxOffset < 0 || maxOffset > mTextView.getText().length()) return false;
 
         int selectionStart, selectionEnd;
 
@@ -839,6 +854,63 @@
         return selectionEnd > selectionStart;
     }
 
+    /**
+     * Adjusts selection to the paragraph under last touch offset. Return true if the operation was
+     * successfully performed.
+     */
+    private boolean selectCurrentParagraph() {
+        if (!mTextView.canSelectText()) {
+            return false;
+        }
+
+        if (needsToSelectAllToSelectWordOrParagraph()) {
+            return mTextView.selectAllText();
+        }
+
+        long lastTouchOffsets = getLastTouchOffsets();
+        final int minLastTouchOffset = TextUtils.unpackRangeStartFromLong(lastTouchOffsets);
+        final int maxLastTouchOffset = TextUtils.unpackRangeEndFromLong(lastTouchOffsets);
+
+        final long paragraphsRange = getParagraphsRange(minLastTouchOffset, maxLastTouchOffset);
+        final int start = TextUtils.unpackRangeStartFromLong(paragraphsRange);
+        final int end = TextUtils.unpackRangeEndFromLong(paragraphsRange);
+        if (start < end) {
+            Selection.setSelection((Spannable) mTextView.getText(), start, end);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Get the minimum range of paragraphs that contains startOffset and endOffset.
+     */
+    private long getParagraphsRange(int startOffset, int endOffset) {
+        final Layout layout = mTextView.getLayout();
+        if (layout == null) {
+            return TextUtils.packRangeInLong(-1, -1);
+        }
+        final CharSequence text = mTextView.getText();
+        int minLine = layout.getLineForOffset(startOffset);
+        // Search paragraph start.
+        while (minLine > 0) {
+            final int prevLineEndOffset = layout.getLineEnd(minLine - 1);
+            if (text.charAt(prevLineEndOffset - 1) == '\n') {
+                break;
+            }
+            minLine--;
+        }
+        int maxLine = layout.getLineForOffset(endOffset);
+        // Search paragraph end.
+        while (maxLine < layout.getLineCount() - 1) {
+            final int lineEndOffset = layout.getLineEnd(maxLine);
+            if (text.charAt(lineEndOffset - 1) == '\n') {
+                break;
+            }
+            maxLine++;
+        }
+        return TextUtils.packRangeInLong(layout.getLineStart(minLine), layout.getLineEnd(maxLine));
+    }
+
     void onLocaleChanged() {
         // Will be re-created on demand in getWordIterator with the proper new locale
         mWordIterator = null;
@@ -1219,7 +1291,31 @@
         }
     }
 
+    private void updateTapState(MotionEvent event) {
+        final int action = event.getActionMasked();
+        if (action == MotionEvent.ACTION_DOWN) {
+            final boolean isMouse = event.isFromSource(InputDevice.SOURCE_MOUSE);
+            // Detect double tap and triple click.
+            if (((mTapState == TAP_STATE_FIRST_TAP)
+                    || ((mTapState == TAP_STATE_DOUBLE_TAP) && isMouse))
+                        && (SystemClock.uptimeMillis() - mLastTouchUpTime) <=
+                                ViewConfiguration.getDoubleTapTimeout()) {
+                if (mTapState == TAP_STATE_FIRST_TAP) {
+                    mTapState = TAP_STATE_DOUBLE_TAP;
+                } else {
+                    mTapState = TAP_STATE_TRIPLE_CLICK;
+                }
+            } else {
+                mTapState = TAP_STATE_FIRST_TAP;
+            }
+        }
+        if (action == MotionEvent.ACTION_UP) {
+            mLastTouchUpTime = SystemClock.uptimeMillis();
+        }
+    }
+
     void onTouchEvent(MotionEvent event) {
+        updateTapState(event);
         updateFloatingToolbarVisibility(event);
 
         if (hasSelectionController()) {
@@ -1773,7 +1869,8 @@
         stopTextActionMode();
         mPreserveDetachedSelection = false;
 
-        getSelectionController().enterDrag();
+        getSelectionController().enterDrag(
+                SelectionModifierCursorController.DRAG_ACCELERATOR_MODE_WORD);
         return true;
     }
 
@@ -3777,30 +3874,6 @@
             }
         }
 
-        public void showAtLocation(int offset) {
-            // TODO - investigate if there's a better way to show the handles
-            // after the drag accelerator has occured.
-            int[] tmpCords = new int[2];
-            mTextView.getLocationInWindow(tmpCords);
-
-            Layout layout = mTextView.getLayout();
-            int posX = tmpCords[0];
-            int posY = tmpCords[1];
-
-            final int line = layout.getLineForOffset(offset);
-
-            int startX = (int) (layout.getPrimaryHorizontal(offset) - 0.5f
-                    - mHotspotX - getHorizontalOffset() + getCursorOffset());
-            int startY = layout.getLineBottom(line);
-
-            // Take TextView's padding and scroll into account.
-            startX += mTextView.viewportToContentHorizontalOffset();
-            startY += mTextView.viewportToContentVerticalOffset();
-
-            mContainer.showAtLocation(mTextView, Gravity.NO_GRAVITY,
-                    startX + posX, startY + posY);
-        }
-
         @Override
         protected void onDraw(Canvas c) {
             final int drawWidth = mDrawable.getIntrinsicWidth();
@@ -3933,13 +4006,16 @@
 
             // Cancel the single tap delayed runnable.
             if (mInsertionActionModeRunnable != null
-                    && (mDoubleTap || isCursorInsideEasyCorrectionSpan())) {
+                    && ((mTapState == TAP_STATE_DOUBLE_TAP)
+                            || (mTapState == TAP_STATE_TRIPLE_CLICK)
+                            || isCursorInsideEasyCorrectionSpan())) {
                 mTextView.removeCallbacks(mInsertionActionModeRunnable);
             }
 
             // Prepare and schedule the single tap runnable to run exactly after the double tap
             // timeout has passed.
-            if (!mDoubleTap && !isCursorInsideEasyCorrectionSpan()
+            if ((mTapState != TAP_STATE_DOUBLE_TAP) && (mTapState != TAP_STATE_TRIPLE_CLICK)
+                    && !isCursorInsideEasyCorrectionSpan()
                     && (durationSinceCutOrCopy < RECENT_CUT_COPY_DURATION)) {
                 if (mTextActionMode == null) {
                     if (mInsertionActionModeRunnable == null) {
@@ -4223,10 +4299,15 @@
 
             boolean isExpanding;
             final float xDiff = x - mPrevX;
-            if (atRtl == isStartHandle()) {
-                isExpanding = xDiff > 0 || currLine > mPreviousLineTouched;
+            if (isStartHandle()) {
+                isExpanding = currLine < mPreviousLineTouched;
             } else {
-                isExpanding = xDiff < 0 || currLine < mPreviousLineTouched;
+                isExpanding = currLine > mPreviousLineTouched;
+            }
+            if (atRtl == isStartHandle()) {
+                isExpanding |= xDiff > 0;
+            } else {
+                isExpanding |= xDiff < 0;
             }
 
             if (mTextView.getHorizontallyScrolling()) {
@@ -4492,14 +4573,24 @@
 
         // Where the user first starts the drag motion.
         private int mStartOffset = -1;
-        // Indicates whether the user is selecting text and using the drag accelerator.
-        private boolean mDragAcceleratorActive;
+
         private boolean mHaventMovedEnoughToStartDrag;
         // The line that a selection happened most recently with the drag accelerator.
         private int mLineSelectionIsOn = -1;
         // Whether the drag accelerator has selected past the initial line.
         private boolean mSwitchedLines = false;
 
+        // Indicates the drag accelerator mode that the user is currently using.
+        private int mDragAcceleratorMode = DRAG_ACCELERATOR_MODE_INACTIVE;
+        // Drag accelerator is inactive.
+        private static final int DRAG_ACCELERATOR_MODE_INACTIVE = 0;
+        // Character based selection by dragging. Only for mouse.
+        private static final int DRAG_ACCELERATOR_MODE_CHARACTER = 1;
+        // Word based selection by dragging. Enabled after long pressing or double tapping.
+        private static final int DRAG_ACCELERATOR_MODE_WORD = 2;
+        // Paragraph based selection by dragging. Enabled after mouse triple click.
+        private static final int DRAG_ACCELERATOR_MODE_PARAGRAPH = 3;
+
         SelectionModifierCursorController() {
             resetTouchOffsets();
         }
@@ -4510,7 +4601,6 @@
             }
             initDrawables();
             initHandles();
-            hideInsertionPointCursorController();
         }
 
         private void initDrawables() {
@@ -4548,10 +4638,10 @@
             if (mEndHandle != null) mEndHandle.hide();
         }
 
-        public void enterDrag() {
+        public void enterDrag(int dragAcceleratorMode) {
             // Just need to init the handles / hide insertion cursor.
             show();
-            mDragAcceleratorActive = true;
+            mDragAcceleratorMode = dragAcceleratorMode;
             // Start location of selection.
             mStartOffset = mTextView.getOffsetForPosition(mLastDownPositionX,
                     mLastDownPositionY);
@@ -4563,6 +4653,7 @@
             // the user to continue dragging across the screen to select text; TextView will
             // scroll as necessary.
             mTextView.getParent().requestDisallowInterceptTouchEvent(true);
+            mTextView.cancelLongPress();
         }
 
         public void onTouchEvent(MotionEvent event) {
@@ -4570,6 +4661,7 @@
             // selection and tap can move cursor from this tap position.
             final float eventX = event.getX();
             final float eventY = event.getY();
+            final boolean isMouse = event.isFromSource(InputDevice.SOURCE_MOUSE);
             switch (event.getActionMasked()) {
                 case MotionEvent.ACTION_DOWN:
                     if (extractedTextModeWillBeStarted()) {
@@ -4582,7 +4674,8 @@
 
                         // Double tap detection
                         if (mGestureStayedInTapRegion) {
-                            if (mDoubleTap) {
+                            if (mTapState == TAP_STATE_DOUBLE_TAP
+                                    || mTapState == TAP_STATE_TRIPLE_CLICK) {
                                 final float deltaX = eventX - mDownPositionX;
                                 final float deltaY = eventY - mDownPositionY;
                                 final float distanceSquared = deltaX * deltaX + deltaY * deltaY;
@@ -4593,8 +4686,12 @@
                                 boolean stayedInArea =
                                         distanceSquared < doubleTapSlop * doubleTapSlop;
 
-                                if (stayedInArea && isPositionOnText(eventX, eventY)) {
-                                    selectCurrentWordAndStartDrag();
+                                if (stayedInArea && (isMouse || isPositionOnText(eventX, eventY))) {
+                                    if (mTapState == TAP_STATE_DOUBLE_TAP) {
+                                        selectCurrentWordAndStartDrag();
+                                    } else if (mTapState == TAP_STATE_TRIPLE_CLICK) {
+                                        selectCurrentParagraphAndStartDrag();
+                                    }
                                     mDiscardNextActionUp = true;
                                 }
                             }
@@ -4639,94 +4736,168 @@
                         }
                     }
 
+                    if (isMouse && !isDragAcceleratorActive()) {
+                        final int offset = mTextView.getOffsetForPosition(eventX, eventY);
+                        if (mStartOffset != offset) {
+                            // Start character based drag accelerator.
+                            if (mTextActionMode != null) {
+                                mTextActionMode.finish();
+                            }
+                            enterDrag(DRAG_ACCELERATOR_MODE_CHARACTER);
+                            mDiscardNextActionUp = true;
+                            mHaventMovedEnoughToStartDrag = false;
+                        }
+                    }
+
                     if (mStartHandle != null && mStartHandle.isShowing()) {
                         // Don't do the drag if the handles are showing already.
                         break;
                     }
 
-                    if (mStartOffset != -1 && mTextView.getLayout() != null) {
-                        if (!mHaventMovedEnoughToStartDrag) {
-
-                            float y = eventY;
-                            if (mSwitchedLines) {
-                                // Offset the finger by the same vertical offset as the handles.
-                                // This improves visibility of the content being selected by
-                                // shifting the finger below the content, this is applied once
-                                // the user has switched lines.
-                                final float fingerOffset = (mStartHandle != null)
-                                        ? mStartHandle.getIdealVerticalOffset()
-                                        : touchSlop;
-                                y = eventY - fingerOffset;
-                            }
-
-                            final int currLine = getCurrentLineAdjustedForSlop(
-                                    mTextView.getLayout(),
-                                    mLineSelectionIsOn, y);
-                            if (!mSwitchedLines && currLine != mLineSelectionIsOn) {
-                                // Break early here, we want to offset the finger position from
-                                // the selection highlight, once the user moved their finger
-                                // to a different line we should apply the offset and *not* switch
-                                // lines until recomputing the position with the finger offset.
-                                mSwitchedLines = true;
-                                break;
-                            }
-
-                            int startOffset;
-                            int offset = mTextView.getOffsetAtCoordinate(currLine, eventX);
-                            // Snap to word boundaries.
-                            if (mStartOffset < offset) {
-                                // Expanding with end handle.
-                                offset = getWordEnd(offset);
-                                startOffset = getWordStart(mStartOffset);
-                            } else {
-                                // Expanding with start handle.
-                                offset = getWordStart(offset);
-                                startOffset = getWordEnd(mStartOffset);
-                            }
-                            mLineSelectionIsOn = currLine;
-                            Selection.setSelection((Spannable) mTextView.getText(),
-                                    startOffset, offset);
-                        }
-                    }
+                    updateSelection(event);
                     break;
 
                 case MotionEvent.ACTION_UP:
-                    if (mDragAcceleratorActive) {
-                        // No longer dragging to select text, let the parent intercept events.
-                        mTextView.getParent().requestDisallowInterceptTouchEvent(false);
-
-                        show();
-                        int startOffset = mTextView.getSelectionStart();
-                        int endOffset = mTextView.getSelectionEnd();
-
-                        // Since we don't let drag handles pass once they're visible, we need to
-                        // make sure the start / end locations are correct because the user *can*
-                        // switch directions during the initial drag.
-                        if (endOffset < startOffset) {
-                            int tmp = endOffset;
-                            endOffset = startOffset;
-                            startOffset = tmp;
-
-                            // Also update the selection with the right offsets in this case.
-                            Selection.setSelection((Spannable) mTextView.getText(),
-                                    startOffset, endOffset);
-                        }
-
-                        // Need to do this to display the handles.
-                        mStartHandle.showAtLocation(startOffset);
-                        mEndHandle.showAtLocation(endOffset);
-
-                        // No longer the first dragging motion, reset.
-                        startSelectionActionMode();
-
-                        mDragAcceleratorActive = false;
-                        mStartOffset = -1;
-                        mSwitchedLines = false;
+                    if (!isDragAcceleratorActive()) {
+                        break;
                     }
+                    updateSelection(event);
+
+                    // No longer dragging to select text, let the parent intercept events.
+                    mTextView.getParent().requestDisallowInterceptTouchEvent(false);
+
+                    int startOffset = mTextView.getSelectionStart();
+                    int endOffset = mTextView.getSelectionEnd();
+
+                    // Since we don't let drag handles pass once they're visible, we need to
+                    // make sure the start / end locations are correct because the user *can*
+                    // switch directions during the initial drag.
+                    if (endOffset < startOffset) {
+                        int tmp = endOffset;
+                        endOffset = startOffset;
+                        startOffset = tmp;
+
+                        // Also update the selection with the right offsets in this case.
+                        Selection.setSelection((Spannable) mTextView.getText(),
+                                startOffset, endOffset);
+                    }
+                    if (startOffset != endOffset) {
+                        startSelectionActionMode();
+                    }
+
+                    // No longer the first dragging motion, reset.
+                    resetDragAcceleratorState();
                     break;
             }
         }
 
+        private void updateSelection(MotionEvent event) {
+            if (mTextView.getLayout() != null) {
+                switch (mDragAcceleratorMode) {
+                    case DRAG_ACCELERATOR_MODE_CHARACTER:
+                        updateCharacterBasedSelection(event);
+                        break;
+                    case DRAG_ACCELERATOR_MODE_WORD:
+                        updateWordBasedSelection(event);
+                        break;
+                    case DRAG_ACCELERATOR_MODE_PARAGRAPH:
+                        updateParagraphBasedSelection(event);
+                        break;
+                }
+            }
+        }
+
+        /**
+         * If the TextView allows text selection, selects the current paragraph and starts a drag.
+         *
+         * @return true if the drag was started.
+         */
+        private boolean selectCurrentParagraphAndStartDrag() {
+            if (mInsertionActionModeRunnable != null) {
+                mTextView.removeCallbacks(mInsertionActionModeRunnable);
+            }
+            if (mTextActionMode != null) {
+                mTextActionMode.finish();
+            }
+            if (!selectCurrentParagraph()) {
+                return false;
+            }
+            enterDrag(SelectionModifierCursorController.DRAG_ACCELERATOR_MODE_PARAGRAPH);
+            return true;
+        }
+
+        private void updateCharacterBasedSelection(MotionEvent event) {
+            final int offset = mTextView.getOffsetForPosition(event.getX(), event.getY());
+            Selection.setSelection((Spannable) mTextView.getText(), mStartOffset, offset);
+        }
+
+        private void updateWordBasedSelection(MotionEvent event) {
+            if (mHaventMovedEnoughToStartDrag) {
+                return;
+            }
+            final boolean isMouse = event.isFromSource(InputDevice.SOURCE_MOUSE);
+            final ViewConfiguration viewConfig = ViewConfiguration.get(
+                    mTextView.getContext());
+            final float eventX = event.getX();
+            final float eventY = event.getY();
+            final int currLine;
+            if (isMouse) {
+                // No need to offset the y coordinate for mouse input.
+                currLine = mTextView.getLineAtCoordinate(eventY);
+            } else {
+                float y = eventY;
+                if (mSwitchedLines) {
+                    // Offset the finger by the same vertical offset as the handles.
+                    // This improves visibility of the content being selected by
+                    // shifting the finger below the content, this is applied once
+                    // the user has switched lines.
+                    final int touchSlop = viewConfig.getScaledTouchSlop();
+                    final float fingerOffset = (mStartHandle != null)
+                            ? mStartHandle.getIdealVerticalOffset()
+                            : touchSlop;
+                    y = eventY - fingerOffset;
+                }
+
+                currLine = getCurrentLineAdjustedForSlop(mTextView.getLayout(), mLineSelectionIsOn,
+                        y);
+                if (!mSwitchedLines && currLine != mLineSelectionIsOn) {
+                    // Break early here, we want to offset the finger position from
+                    // the selection highlight, once the user moved their finger
+                    // to a different line we should apply the offset and *not* switch
+                    // lines until recomputing the position with the finger offset.
+                    mSwitchedLines = true;
+                    return;
+                }
+            }
+
+            int startOffset;
+            int offset = mTextView.getOffsetAtCoordinate(currLine, eventX);
+            // Snap to word boundaries.
+            if (mStartOffset < offset) {
+                // Expanding with end handle.
+                offset = getWordEnd(offset);
+                startOffset = getWordStart(mStartOffset);
+            } else {
+                // Expanding with start handle.
+                offset = getWordStart(offset);
+                startOffset = getWordEnd(mStartOffset);
+            }
+            mLineSelectionIsOn = currLine;
+            Selection.setSelection((Spannable) mTextView.getText(),
+                    startOffset, offset);
+        }
+
+        private void updateParagraphBasedSelection(MotionEvent event) {
+            final int offset = mTextView.getOffsetForPosition(event.getX(), event.getY());
+
+            final int start = Math.min(offset, mStartOffset);
+            final int end = Math.max(offset, mStartOffset);
+            final long paragraphsRange = getParagraphsRange(start, end);
+            final int selectionStart = TextUtils.unpackRangeStartFromLong(paragraphsRange);
+            final int selectionEnd = TextUtils.unpackRangeEndFromLong(paragraphsRange);
+            Selection.setSelection((Spannable) mTextView.getText(), selectionStart, selectionEnd);
+        }
+
         /**
          * @param event
          */
@@ -4749,8 +4920,12 @@
 
         public void resetTouchOffsets() {
             mMinTouchOffset = mMaxTouchOffset = -1;
+            resetDragAcceleratorState();
+        }
+
+        private void resetDragAcceleratorState() {
             mStartOffset = -1;
-            mDragAcceleratorActive = false;
+            mDragAcceleratorMode = DRAG_ACCELERATOR_MODE_INACTIVE;
             mSwitchedLines = false;
         }
 
@@ -4765,7 +4940,7 @@
          * @return true if the user is selecting text using the drag accelerator.
          */
         public boolean isDragAcceleratorActive() {
-            return mDragAcceleratorActive;
+            return mDragAcceleratorMode != DRAG_ACCELERATOR_MODE_INACTIVE;
         }
 
         public void onTouchModeChanged(boolean isInTouchMode) {
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index 4d9f55c..280ff15 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -21,8 +21,12 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.PorterDuff;
 import android.graphics.Rect;
+import android.graphics.Region;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.Gravity;
@@ -32,6 +36,9 @@
 import android.view.ViewHierarchyEncoder;
 import android.widget.RemoteViews.RemoteView;
 
+import com.android.internal.R;
+
+
 /**
  * FrameLayout is designed to block out an area on the screen to display
  * a single item. Generally, FrameLayout should be used to hold a single child view, because it can
@@ -164,10 +171,6 @@
             mPaddingBottom + mForegroundPaddingBottom;
     }
 
-    @Override
-    public int findDependentLayoutAxes(View child, int axisFilter) {
-        return findDependentLayoutAxesHelper(child, axisFilter, LayoutParams.class);
-    }
 
     /**
      * {@inheritDoc}
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index bdb1e83..ad939be 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -16,7 +16,6 @@
 
 package android.widget;
 
-import android.view.ViewParent;
 import com.android.internal.R;
 
 import android.annotation.IntDef;
@@ -38,8 +37,6 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 
 /**
  * A Layout that arranges its children in a single column or a single row. The direction of 
@@ -647,60 +644,6 @@
         }
     }
 
-    @Override
-    public int findDependentLayoutAxes(View child, int axisFilter) {
-        // This implementation is almost exactly equivalent to the default implementation
-        // offered to the rest of the framework in ViewGroup, but we treat weight to be
-        // functionally equivalent to MATCH_PARENT along the orientation axis.
-
-        if (!checkPartialLayoutParams(child, LayoutParams.class)) return axisFilter;
-        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-        if (child.didLayoutParamsChange()) {
-            // Anything could have changed about our previous assumptions.
-            return axisFilter;
-        }
-
-        // Our layout can always end up depending on a WRAP_CONTENT child.
-        final int wrapAxisFilter = ((lp.width == WRAP_CONTENT ? FLAG_LAYOUT_AXIS_HORIZONTAL : 0)
-                | (lp.height == WRAP_CONTENT ? FLAG_LAYOUT_AXIS_VERTICAL : 0)) & axisFilter;
-
-        if (wrapAxisFilter == axisFilter) {
-            // We know all queried axes are affected, just return early.
-            return wrapAxisFilter;
-        }
-
-        // Our layout *may* depend on a MATCH_PARENT child, depending on whether we can determine
-        // that our layout will remain stable within our parent. We need to ask.
-        int matchAxisFilter = ((lp.width == MATCH_PARENT ? FLAG_LAYOUT_AXIS_HORIZONTAL : 0)
-                | (lp.height == MATCH_PARENT ? FLAG_LAYOUT_AXIS_VERTICAL : 0)) & axisFilter;
-
-        // For LinearLayout, a nonzero weight is equivalent to MATCH_PARENT for this purpose.
-        if (lp.weight > 0) {
-            if (mOrientation == HORIZONTAL) {
-                matchAxisFilter |= FLAG_LAYOUT_AXIS_HORIZONTAL & axisFilter;
-            } else {
-                matchAxisFilter |= FLAG_LAYOUT_AXIS_VERTICAL & axisFilter;
-            }
-        }
-
-        if (matchAxisFilter != 0 || wrapAxisFilter != 0) {
-            final ViewParent parent = getParent();
-            if (parent != null) {
-                // If our parent depends on us for an axis, then our layout can also be affected
-                // by a MATCH_PARENT child along that axis.
-                return getParent().findDependentLayoutAxes(this, matchAxisFilter)
-                        | wrapAxisFilter;
-            }
-
-            // If we don't have a parent, assume we're affected
-            // in any determined affected direction.
-            return matchAxisFilter | wrapAxisFilter;
-        }
-
-        // Two exact sizes and LayoutParams didn't change. We're safe.
-        return 0;
-    }
-
     /**
      * Determines where to position dividers between children.
      *
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index b43ea76..53ca6d1 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -1158,7 +1158,7 @@
             final View child = obtainView(0, mIsScrap);
 
             // Lay out child directly against the parent measure spec so that
-            // we can obtain expected minimum width and height.
+            // we can obtain exected minimum width and height.
             measureScrapChild(child, 0, widthMeasureSpec, heightSize);
 
             childWidth = child.getMeasuredWidth();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 56513a3..5574f86 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -17,7 +17,6 @@
 package android.widget;
 
 import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
-
 import android.R;
 import android.annotation.ColorInt;
 import android.annotation.DrawableRes;
@@ -115,6 +114,7 @@
 import android.view.DragEvent;
 import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
+import android.view.InputDevice;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
@@ -618,9 +618,6 @@
     private final Paint mHighlightPaint;
     private boolean mHighlightPathBogus = true;
 
-    private boolean mFirstTouch = false;
-    private long mLastTouchUpTime = 0;
-
     // Although these fields are specific to editable text, they are not added to Editor because
     // they are defined by the TextView's style and are theme-dependent.
     int mCursorDrawableRes;
@@ -6814,11 +6811,10 @@
 
         if (mEllipsize == TextUtils.TruncateAt.MARQUEE) {
             if (!compressText(ellipsisWidth)) {
+                final int height = mLayoutParams.height;
                 // If the size of the view does not depend on the size of the text, try to
                 // start the marquee immediately
-                final ViewParent parent = getParent();
-                if (parent != null && parent.findDependentLayoutAxes(this,
-                        ViewParent.FLAG_LAYOUT_AXIS_VERTICAL) == 0) {
+                if (height != LayoutParams.WRAP_CONTENT && height != LayoutParams.MATCH_PARENT) {
                     startMarquee();
                 } else {
                     // Defer the start of the marquee until we know our width (see setFrame())
@@ -7217,9 +7213,37 @@
      * new view layout.
      */
     private void checkForResize() {
-        // Always request a layout. The parent will perform the correct version
-        // of the intended optimizations as part of requestLayoutForChild.
-        requestLayout();
+        boolean sizeChanged = false;
+
+        if (mLayout != null) {
+            // Check if our width changed
+            if (mLayoutParams.width == LayoutParams.WRAP_CONTENT) {
+                sizeChanged = true;
+                invalidate();
+            }
+
+            // Check if our height changed
+            if (mLayoutParams.height == LayoutParams.WRAP_CONTENT) {
+                int desiredHeight = getDesiredHeight();
+
+                if (desiredHeight != this.getHeight()) {
+                    sizeChanged = true;
+                }
+            } else if (mLayoutParams.height == LayoutParams.MATCH_PARENT) {
+                if (mDesiredHeightAtMeasure >= 0) {
+                    int desiredHeight = getDesiredHeight();
+
+                    if (desiredHeight != mDesiredHeightAtMeasure) {
+                        sizeChanged = true;
+                    }
+                }
+            }
+        }
+
+        if (sizeChanged) {
+            requestLayout();
+            // caller will have already invalidated
+        }
     }
 
     /**
@@ -7227,11 +7251,56 @@
      * or merely a new text layout.
      */
     private void checkForRelayout() {
-        // Always request a layout. The parent will perform the correct version
-        // of the intended optimizations as part of requestLayoutForChild.
-        nullLayouts();
-        requestLayout();
-        invalidate();
+        // If we have a fixed width, we can just swap in a new text layout
+        // if the text height stays the same or if the view height is fixed.
+
+        if ((mLayoutParams.width != LayoutParams.WRAP_CONTENT ||
+                (mMaxWidthMode == mMinWidthMode && mMaxWidth == mMinWidth)) &&
+                (mHint == null || mHintLayout != null) &&
+                (mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight() > 0)) {
+            // Static width, so try making a new text layout.
+
+            int oldht = mLayout.getHeight();
+            int want = mLayout.getWidth();
+            int hintWant = mHintLayout == null ? 0 : mHintLayout.getWidth();
+
+            /*
+             * No need to bring the text into view, since the size is not
+             * changing (unless we do the requestLayout(), in which case it
+             * will happen at measure).
+             */
+            makeNewLayout(want, hintWant, UNKNOWN_BORING, UNKNOWN_BORING,
+                          mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight(),
+                          false);
+
+            if (mEllipsize != TextUtils.TruncateAt.MARQUEE) {
+                // In a fixed-height view, so use our new text layout.
+                if (mLayoutParams.height != LayoutParams.WRAP_CONTENT &&
+                    mLayoutParams.height != LayoutParams.MATCH_PARENT) {
+                    invalidate();
+                    return;
+                }
+
+                // Dynamic height, but height has stayed the same,
+                // so use our new text layout.
+                if (mLayout.getHeight() == oldht &&
+                    (mHintLayout == null || mHintLayout.getHeight() == oldht)) {
+                    invalidate();
+                    return;
+                }
+            }
+
+            // We lose: the height has changed and we have a dynamic height.
+            // Request a new view layout using our new text layout.
+            requestLayout();
+            invalidate();
+        } else {
+            // Dynamic width, so we have no choice but to request a new
+            // view layout with a new text layout.
+            nullLayouts();
+            requestLayout();
+            invalidate();
+        }
     }
 
     @Override
@@ -8334,23 +8403,6 @@
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         final int action = event.getActionMasked();
-
-        if (mEditor != null && action == MotionEvent.ACTION_DOWN) {
-            // Detect double tap and inform the Editor.
-            if (mFirstTouch && (SystemClock.uptimeMillis() - mLastTouchUpTime) <=
-                    ViewConfiguration.getDoubleTapTimeout()) {
-                mEditor.mDoubleTap = true;
-                mFirstTouch = false;
-            } else {
-                mEditor.mDoubleTap = false;
-                mFirstTouch = true;
-            }
-        }
-
-        if (action == MotionEvent.ACTION_UP) {
-            mLastTouchUpTime = SystemClock.uptimeMillis();
-        }
-
         if (mEditor != null) {
             mEditor.onTouchEvent(event);
 
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index 6e56513..acbf5eb 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -1368,11 +1368,6 @@
     }
 
     @Override
-    public int findDependentLayoutAxes(View child, int axisFilter) {
-        return findDependentLayoutAxesHelper(child, axisFilter, LayoutParams.class);
-    }
-
-    @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         int width = 0;
         int height = 0;
diff --git a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
index e607a3f..85cc841 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -175,7 +175,7 @@
         }
 
         private final TreeMap<InputMethodInfo, List<InputMethodSubtype>> mSortedImmis =
-                new TreeMap<InputMethodInfo, List<InputMethodSubtype>>(
+                new TreeMap<>(
                         new Comparator<InputMethodInfo>() {
                             @Override
                             public int compare(InputMethodInfo imi1, InputMethodInfo imi2) {
@@ -192,14 +192,9 @@
                             }
                         });
 
-        public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList() {
-            return getSortedInputMethodAndSubtypeList(true, false, false);
-        }
-
         public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList(
-                boolean showSubtypes, boolean includeAuxiliarySubtypes, boolean isScreenLocked) {
-            final ArrayList<ImeSubtypeListItem> imList =
-                    new ArrayList<ImeSubtypeListItem>();
+                boolean includeAuxiliarySubtypes, boolean isScreenLocked) {
+            final ArrayList<ImeSubtypeListItem> imList = new ArrayList<>();
             final HashMap<InputMethodInfo, List<InputMethodSubtype>> immis =
                     mSettings.getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked(
                             mContext);
@@ -219,12 +214,12 @@
                     continue;
                 }
                 List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypeList = immis.get(imi);
-                HashSet<String> enabledSubtypeSet = new HashSet<String>();
+                HashSet<String> enabledSubtypeSet = new HashSet<>();
                 for (InputMethodSubtype subtype : explicitlyOrImplicitlyEnabledSubtypeList) {
                     enabledSubtypeSet.add(String.valueOf(subtype.hashCode()));
                 }
                 final CharSequence imeLabel = imi.loadLabel(mPm);
-                if (showSubtypes && enabledSubtypeSet.size() > 0) {
+                if (enabledSubtypeSet.size() > 0) {
                     final int subtypeCount = imi.getSubtypeCount();
                     if (DEBUG) {
                         Slog.v(TAG, "Add subtypes: " + subtypeCount + ", " + imi.getId());
@@ -532,7 +527,8 @@
     public void resetCircularListLocked(Context context) {
         mSubtypeList = new InputMethodAndSubtypeList(context, mSettings);
         mController = ControllerImpl.createFrom(mController,
-                mSubtypeList.getSortedInputMethodAndSubtypeList());
+                mSubtypeList.getSortedInputMethodAndSubtypeList(
+                        false /* includeAuxiliarySubtypes */, false /* isScreenLocked */));
     }
 
     public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme, InputMethodInfo imi,
@@ -546,10 +542,10 @@
         return mController.getNextInputMethod(onlyCurrentIme, imi, subtype);
     }
 
-    public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeListLocked(boolean showSubtypes,
+    public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeListLocked(
             boolean includingAuxiliarySubtypes, boolean isScreenLocked) {
         return mSubtypeList.getSortedInputMethodAndSubtypeList(
-                showSubtypes, includingAuxiliarySubtypes, isScreenLocked);
+                includingAuxiliarySubtypes, isScreenLocked);
     }
 
     public void dump(final Printer pw) {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 4a969b2..cc815c4 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -7732,26 +7732,35 @@
                 }
 
                 final Uid u = getUidStatsLocked(mapUid(entry.uid));
-                u.noteNetworkActivityLocked(NETWORK_WIFI_RX_DATA, entry.rxBytes,
-                        entry.rxPackets);
-                u.noteNetworkActivityLocked(NETWORK_WIFI_TX_DATA, entry.txBytes,
-                        entry.txPackets);
-                rxPackets.put(u.getUid(), entry.rxPackets);
-                txPackets.put(u.getUid(), entry.txPackets);
+                if (entry.rxBytes != 0) {
+                    u.noteNetworkActivityLocked(NETWORK_WIFI_RX_DATA, entry.rxBytes,
+                            entry.rxPackets);
+                    mNetworkByteActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
+                            entry.rxBytes);
+                    mNetworkPacketActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
+                            entry.rxPackets);
 
-                // Sum the total number of packets so that the Rx Power and Tx Power can
-                // be evenly distributed amongst the apps.
-                totalRxPackets += entry.rxPackets;
-                totalTxPackets += entry.txPackets;
+                    rxPackets.put(u.getUid(), entry.rxPackets);
 
-                mNetworkByteActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
-                        entry.rxBytes);
-                mNetworkByteActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
-                        entry.txBytes);
-                mNetworkPacketActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
-                        entry.rxPackets);
-                mNetworkPacketActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
-                        entry.txPackets);
+                    // Sum the total number of packets so that the Rx Power can
+                    // be evenly distributed amongst the apps.
+                    totalRxPackets += entry.rxPackets;
+                }
+
+                if (entry.txBytes != 0) {
+                    u.noteNetworkActivityLocked(NETWORK_WIFI_TX_DATA, entry.txBytes,
+                            entry.txPackets);
+                    mNetworkByteActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
+                            entry.txBytes);
+                    mNetworkPacketActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
+                            entry.txPackets);
+
+                    txPackets.put(u.getUid(), entry.txPackets);
+
+                    // Sum the total number of packets so that the Tx Power can
+                    // be evenly distributed amongst the apps.
+                    totalTxPackets += entry.txPackets;
+                }
             }
         }
 
diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
index 75ca639..1b44ff3 100644
--- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java
+++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
@@ -63,17 +63,18 @@
     private boolean mReportNextDraw;
 
     private Drawable mCaptionBackgroundDrawable;
+    private Drawable mUserCaptionBackgroundDrawable;
     private Drawable mResizingBackgroundDrawable;
     private ColorDrawable mStatusBarColor;
 
     public BackdropFrameRenderer(DecorView decorView, ThreadedRenderer renderer, Rect initialBounds,
             Drawable resizingBackgroundDrawable, Drawable captionBackgroundDrawable,
-            int statusBarColor) {
+            Drawable userCaptionBackgroundDrawable, int statusBarColor) {
         setName("ResizeFrame");
 
         mRenderer = renderer;
         onResourcesLoaded(decorView, resizingBackgroundDrawable, captionBackgroundDrawable,
-                statusBarColor);
+                userCaptionBackgroundDrawable, statusBarColor);
 
         // Create a render node for the content and frame backdrop
         // which can be resized independently from the content.
@@ -92,10 +93,12 @@
     }
 
     void onResourcesLoaded(DecorView decorView, Drawable resizingBackgroundDrawable,
-            Drawable captionBackgroundDrawableDrawable, int statusBarColor) {
+            Drawable captionBackgroundDrawableDrawable, Drawable userCaptionBackgroundDrawable,
+            int statusBarColor) {
         mDecorView = decorView;
         mResizingBackgroundDrawable = resizingBackgroundDrawable;
         mCaptionBackgroundDrawable = captionBackgroundDrawableDrawable;
+        mUserCaptionBackgroundDrawable = userCaptionBackgroundDrawable;
         if (statusBarColor != 0) {
             mStatusBarColor = new ColorDrawable(statusBarColor);
             addSystemBarNodeIfNeeded();
@@ -281,8 +284,10 @@
 
         // Draw the caption and content backdrops in to our render node.
         DisplayListCanvas canvas = mFrameAndBackdropNode.start(width, height);
-        mCaptionBackgroundDrawable.setBounds(0, 0, left + width, top + mLastCaptionHeight);
-        mCaptionBackgroundDrawable.draw(canvas);
+        final Drawable drawable = mUserCaptionBackgroundDrawable != null
+                ? mUserCaptionBackgroundDrawable : mCaptionBackgroundDrawable;
+        drawable.setBounds(0, 0, left + width, top + mLastCaptionHeight);
+        drawable.draw(canvas);
 
         // The backdrop: clear everything with the background. Clipping is done elsewhere.
         mResizingBackgroundDrawable.setBounds(0, mLastCaptionHeight, left + width, top + height);
@@ -324,4 +329,8 @@
             mChoreographer.postFrameCallback(this);
         }
     }
+
+    void setUserCaptionBackgroundDrawable(Drawable userCaptionBackgroundDrawable) {
+        mUserCaptionBackgroundDrawable = userCaptionBackgroundDrawable;
+    }
 }
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 531ba2f..e405564 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -74,6 +74,8 @@
 import static android.view.View.MeasureSpec.getMode;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static android.view.Window.DECOR_CAPTION_SHADE_DARK;
+import static android.view.Window.DECOR_CAPTION_SHADE_LIGHT;
 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
 import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
@@ -184,9 +186,10 @@
 
     private boolean mWindowResizeCallbacksAdded = false;
 
-    BackdropFrameRenderer mBackdropFrameRenderer = null;
+    private BackdropFrameRenderer mBackdropFrameRenderer = null;
     private Drawable mResizingBackgroundDrawable;
     private Drawable mCaptionBackgroundDrawable;
+    private Drawable mUserCaptionBackgroundDrawable;
 
     DecorView(Context context, int featureId, PhoneWindow window) {
         super(context);
@@ -1580,18 +1583,20 @@
         initializeElevation();
     }
 
-    View onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
+    void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
         mStackId = getStackId();
 
         mResizingBackgroundDrawable = getResizingBackgroundDrawable(
                 mWindow.mBackgroundResource, mWindow.mBackgroundFallbackResource);
-        mCaptionBackgroundDrawable =
-                getContext().getDrawable(R.drawable.decor_caption_title_focused);
+        if (mCaptionBackgroundDrawable == null) {
+            mCaptionBackgroundDrawable = getContext().getDrawable(
+                    R.drawable.decor_caption_title_focused);
+        }
 
         if (mBackdropFrameRenderer != null) {
             mBackdropFrameRenderer.onResourcesLoaded(
                     this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
-                    getCurrentColor(mStatusColorViewState));
+                    mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState));
         }
 
         mDecorCaptionView = createDecorCaptionView(inflater);
@@ -1608,17 +1613,16 @@
         }
         mContentRoot = (ViewGroup) root;
         initializeElevation();
-        return root;
     }
 
     // Free floating overlapping windows require a caption.
     private DecorCaptionView createDecorCaptionView(LayoutInflater inflater) {
-        DecorCaptionView DecorCaptionView = null;
-        for (int i = getChildCount() - 1; i >= 0 && DecorCaptionView == null; i--) {
+        DecorCaptionView decorCaptionView = null;
+        for (int i = getChildCount() - 1; i >= 0 && decorCaptionView == null; i--) {
             View view = getChildAt(i);
             if (view instanceof DecorCaptionView) {
                 // The decor was most likely saved from a relaunch - so reuse it.
-                DecorCaptionView = (DecorCaptionView) view;
+                decorCaptionView = (DecorCaptionView) view;
                 removeViewAt(i);
             }
         }
@@ -1630,27 +1634,72 @@
                 && ActivityManager.StackId.hasWindowDecor(mStackId)) {
             // Dependent on the brightness of the used title we either use the
             // dark or the light button frame.
-            if (DecorCaptionView == null) {
-                Context context = getContext();
-                TypedValue value = new TypedValue();
-                context.getTheme().resolveAttribute(R.attr.colorPrimary, value, true);
-                inflater = inflater.from(context);
-                if (Color.luminance(value.data) < 0.5) {
-                    DecorCaptionView = (DecorCaptionView) inflater.inflate(
-                            R.layout.decor_caption_dark, null);
-                } else {
-                    DecorCaptionView = (DecorCaptionView) inflater.inflate(
-                            R.layout.decor_caption_light, null);
-                }
+            if (decorCaptionView == null) {
+                decorCaptionView = inflateDecorCaptionView(inflater);
             }
-            DecorCaptionView.setPhoneWindow(mWindow, true /*showDecor*/);
+            decorCaptionView.setPhoneWindow(mWindow, true /*showDecor*/);
         } else {
-            DecorCaptionView = null;
+            decorCaptionView = null;
         }
 
         // Tell the decor if it has a visible caption.
-        enableCaption(DecorCaptionView != null);
-        return DecorCaptionView;
+        enableCaption(decorCaptionView != null);
+        return decorCaptionView;
+    }
+
+    private DecorCaptionView inflateDecorCaptionView(LayoutInflater inflater) {
+        final Context context = getContext();
+        // We make a copy of the inflater, so it has the right context associated with it.
+        inflater = inflater.from(context);
+        final DecorCaptionView view = (DecorCaptionView) inflater.inflate(R.layout.decor_caption,
+                null);
+        setDecorCaptionShade(context, view);
+        return view;
+    }
+
+    private void setDecorCaptionShade(Context context, DecorCaptionView view) {
+        final int shade = mWindow.getDecorCaptionShade();
+        switch (shade) {
+            case DECOR_CAPTION_SHADE_LIGHT:
+                setLightDecorCaptionShade(view);
+                break;
+            case DECOR_CAPTION_SHADE_DARK:
+                setDarkDecorCaptionShade(view);
+                break;
+            default: {
+                TypedValue value = new TypedValue();
+                context.getTheme().resolveAttribute(R.attr.colorPrimary, value, true);
+                // We invert the shade depending on brightness of the theme. Dark shade for light
+                // theme and vice versa. Thanks to this the buttons should be visible on the
+                // background.
+                if (Color.luminance(value.data) < 0.5) {
+                    setLightDecorCaptionShade(view);
+                } else {
+                    setDarkDecorCaptionShade(view);
+                }
+                break;
+            }
+        }
+    }
+
+    void updateDecorCaptionShade() {
+        if (mDecorCaptionView != null) {
+            setDecorCaptionShade(getContext(), mDecorCaptionView);
+        }
+    }
+
+    private void setLightDecorCaptionShade(DecorCaptionView view) {
+        view.findViewById(R.id.maximize_window).setBackgroundResource(
+                R.drawable.decor_maximize_button_light);
+        view.findViewById(R.id.close_window).setBackgroundResource(
+                R.drawable.decor_close_button_light);
+    }
+
+    private void setDarkDecorCaptionShade(DecorCaptionView view) {
+        view.findViewById(R.id.maximize_window).setBackgroundResource(
+                R.drawable.decor_maximize_button_dark);
+        view.findViewById(R.id.close_window).setBackgroundResource(
+                R.drawable.decor_close_button_dark);
     }
 
     /**
@@ -1735,11 +1784,11 @@
         if (mBackdropFrameRenderer != null) {
             return;
         }
-        final ThreadedRenderer renderer = (ThreadedRenderer) getHardwareRenderer();
+        final ThreadedRenderer renderer = getHardwareRenderer();
         if (renderer != null) {
             mBackdropFrameRenderer = new BackdropFrameRenderer(this, renderer,
                     initialBounds, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
-                    getCurrentColor(mStatusColorViewState));
+                    mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState));
 
             // Get rid of the shadow while we are resizing. Shadow drawing takes considerable time.
             // If we want to get the shadow shown while resizing, we would need to elevate a new
@@ -1849,6 +1898,16 @@
                 getResources().getDisplayMetrics());
     }
 
+    /**
+     * Provide an override of the caption background drawable.
+     */
+    void setUserCaptionBackgroundDrawable(Drawable drawable) {
+        mUserCaptionBackgroundDrawable = drawable;
+        if (mBackdropFrameRenderer != null) {
+            mBackdropFrameRenderer.setUserCaptionBackgroundDrawable(drawable);
+        }
+    }
+
     private static class ColorViewState {
         View view = null;
         int targetVisibility = View.INVISIBLE;
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 337bb69..86bd782 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -94,7 +94,6 @@
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
 import android.widget.FrameLayout;
-import android.widget.LinearLayout;
 import android.widget.ImageView;
 import android.widget.ProgressBar;
 import android.widget.TextView;
@@ -273,6 +272,8 @@
     private boolean mIsStartingWindow;
     private int mTheme = -1;
 
+    private int mDecorCaptionShade = DECOR_CAPTION_SHADE_AUTO;
+
     static class WindowManagerHolder {
         static final IWindowManager sWindowManager = IWindowManager.Stub.asInterface(
                 ServiceManager.getService("window"));
@@ -2527,7 +2528,7 @@
         }
 
         mDecor.startChanging();
-        final View in = mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
+        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
 
         ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
         if (contentParent == null) {
@@ -3720,4 +3721,21 @@
             }
         }
     }
+
+    @Override
+    public void setResizingCaptionDrawable(Drawable drawable) {
+        mDecor.setUserCaptionBackgroundDrawable(drawable);
+    }
+
+    @Override
+    public void setDecorCaptionShade(int decorCaptionShade) {
+        mDecorCaptionShade = decorCaptionShade;
+        if (mDecor != null) {
+            mDecor.updateDecorCaptionShade();
+        }
+    }
+
+    int getDecorCaptionShade() {
+        return mDecorCaptionShade;
+    }
 }
diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
index 3e65320..c3a7460 100644
--- a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
+++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
@@ -360,11 +360,6 @@
     }
 
     @Override
-    public int findDependentLayoutAxes(View child, int axisFilter) {
-        return findDependentLayoutAxesHelper(child, axisFilter, LayoutParams.class);
-    }
-
-    @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         pullChildren();
 
diff --git a/core/java/com/android/internal/widget/DecorCaptionView.java b/core/java/com/android/internal/widget/DecorCaptionView.java
index d747686..c3fe9e7 100644
--- a/core/java/com/android/internal/widget/DecorCaptionView.java
+++ b/core/java/com/android/internal/widget/DecorCaptionView.java
@@ -319,6 +319,10 @@
                         captionHeight + mContent.getMeasuredHeight());
             }
         }
+
+        // This assumes that the caption bar is at the top.
+        mOwner.notifyRestrictedCaptionAreaCallback(mMaximize.getLeft(), mMaximize.getTop(),
+                mClose.getRight(), mClose.getBottom());
     }
     /**
      * Determine if the workspace is entirely covered by the window.
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index dac6d96..ab0df55 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -56,10 +56,11 @@
     return result;
 }
 
-static jboolean FontFamily_addFont(JNIEnv* env, jobject clazz, jlong familyPtr, jstring path) {
+static jboolean FontFamily_addFont(JNIEnv* env, jobject clazz, jlong familyPtr, jstring path,
+        jint ttcIndex) {
     NPE_CHECK_RETURN_ZERO(env, path);
     ScopedUtfChars str(env, path);
-    SkTypeface* face = SkTypeface::CreateFromFile(str.c_str());
+    SkTypeface* face = SkTypeface::CreateFromFile(str.c_str(), ttcIndex);
     if (face == NULL) {
         ALOGE("addFont failed to create font %s", str.c_str());
         return false;
@@ -69,10 +70,10 @@
 }
 
 static jboolean FontFamily_addFontWeightStyle(JNIEnv* env, jobject clazz, jlong familyPtr,
-        jstring path, jint weight, jboolean isItalic) {
+        jstring path, jint ttcIndex, jint weight, jboolean isItalic) {
     NPE_CHECK_RETURN_ZERO(env, path);
     ScopedUtfChars str(env, path);
-    SkTypeface* face = SkTypeface::CreateFromFile(str.c_str());
+    SkTypeface* face = SkTypeface::CreateFromFile(str.c_str(), ttcIndex);
     if (face == NULL) {
         ALOGE("addFont failed to create font %s", str.c_str());
         return false;
@@ -127,8 +128,8 @@
 static const JNINativeMethod gFontFamilyMethods[] = {
     { "nCreateFamily",         "(Ljava/lang/String;I)J", (void*)FontFamily_create },
     { "nUnrefFamily",          "(J)V", (void*)FontFamily_unref },
-    { "nAddFont",              "(JLjava/lang/String;)Z", (void*)FontFamily_addFont },
-    { "nAddFontWeightStyle",   "(JLjava/lang/String;IZ)Z", (void*)FontFamily_addFontWeightStyle },
+    { "nAddFont",              "(JLjava/lang/String;I)Z", (void*)FontFamily_addFont },
+    { "nAddFontWeightStyle",   "(JLjava/lang/String;IIZ)Z", (void*)FontFamily_addFontWeightStyle },
     { "nAddFontFromAsset",     "(JLandroid/content/res/AssetManager;Ljava/lang/String;)Z",
                                            (void*)FontFamily_addFontFromAsset },
 };
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 1ee7ea8..2488111 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -595,6 +595,36 @@
     MEMINFO_COUNT
 };
 
+static long long get_zram_mem_used()
+{
+#define ZRAM_SYSFS "/sys/block/zram0/"
+    FILE *f = fopen(ZRAM_SYSFS "mm_stat", "r");
+    if (f) {
+        long long mem_used_total = 0;
+
+        int matched = fscanf(f, "%*d %*d %lld %*d %*d %*d %*d", &mem_used_total);
+        if (matched != 1)
+            ALOGW("failed to parse " ZRAM_SYSFS "mm_stat");
+
+        fclose(f);
+        return mem_used_total;
+    }
+
+    f = fopen(ZRAM_SYSFS "mem_used_total", "r");
+    if (f) {
+        long long mem_used_total = 0;
+
+        int matched = fscanf(f, "%lld", &mem_used_total);
+        if (matched != 1)
+            ALOGW("failed to parse " ZRAM_SYSFS "mem_used_total");
+
+        fclose(f);
+        return mem_used_total;
+    }
+
+    return 0;
+}
+
 static void android_os_Debug_getMemInfo(JNIEnv *env, jobject clazz, jlongArray out)
 {
     char buffer[1024];
@@ -680,15 +710,7 @@
         if (*p) p++;
     }
 
-    fd = open("/sys/block/zram0/mem_used_total", O_RDONLY);
-    if (fd >= 0) {
-        len = read(fd, buffer, sizeof(buffer)-1);
-        close(fd);
-        if (len > 0) {
-            buffer[len] = 0;
-            mem[MEMINFO_ZRAM_TOTAL] = atoll(buffer)/1024;
-        }
-    }
+    mem[MEMINFO_ZRAM_TOTAL] = get_zram_mem_used() / 1024;
     // Recompute Vmalloc Used since the value in meminfo
     // doesn't account for I/O remapping which doesn't use RAM.
     mem[MEMINFO_VMALLOC_USED] = get_allocated_vmalloc_memory() / 1024;
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index ff51e4e..f6e68c4 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -149,7 +149,9 @@
         case PublicFormat::DEPTH16:
             return HAL_DATASPACE_DEPTH;
         case PublicFormat::RAW_SENSOR:
+        case PublicFormat::RAW_PRIVATE:
         case PublicFormat::RAW10:
+        case PublicFormat::RAW12:
             return HAL_DATASPACE_ARBITRARY;
         case PublicFormat::YUV_420_888:
         case PublicFormat::NV21:
@@ -170,6 +172,7 @@
         case HAL_PIXEL_FORMAT_RGB_565:
         case HAL_PIXEL_FORMAT_Y8:
         case HAL_PIXEL_FORMAT_RAW10:
+        case HAL_PIXEL_FORMAT_RAW12:
         case HAL_PIXEL_FORMAT_YCbCr_420_888:
         case HAL_PIXEL_FORMAT_YV12:
             // Enums overlap in both name and value
@@ -177,6 +180,9 @@
         case HAL_PIXEL_FORMAT_RAW16:
             // Name differs, though value is the same
             return PublicFormat::RAW_SENSOR;
+        case HAL_PIXEL_FORMAT_RAW_OPAQUE:
+            // Name differs, though value is the same
+            return PublicFormat::RAW_PRIVATE;
         case HAL_PIXEL_FORMAT_YCbCr_422_SP:
             // Name differs, though the value is the same
             return PublicFormat::NV16;
@@ -212,7 +218,6 @@
             }
             break;
         case HAL_PIXEL_FORMAT_BGRA_8888:
-        case HAL_PIXEL_FORMAT_RAW_OPAQUE:
             // Not defined in public API
             return PublicFormat::UNKNOWN;
 
diff --git a/core/res/Android.mk b/core/res/Android.mk
index cfc791d..a1bef83 100644
--- a/core/res/Android.mk
+++ b/core/res/Android.mk
@@ -23,6 +23,7 @@
 # Tell aapt to create "extending (non-application)" resource IDs,
 # since these resources will be used by many apps.
 LOCAL_AAPT_FLAGS := -x
+LOCAL_AAPT_FLAGS += --private-symbols com.android.internal
 
 LOCAL_MODULE_TAGS := optional
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 5251b20..1127197 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -277,6 +277,7 @@
     <protected-broadcast android:name="android.intent.action.ADVANCED_SETTINGS" />
     <protected-broadcast android:name="android.intent.action.APPLICATION_RESTRICTIONS_CHANGED" />
     <protected-broadcast android:name="android.intent.action.BUGREPORT_FINISHED" />
+    <protected-broadcast android:name="android.intent.action.BUGREPORT_STARTED" />
 
     <protected-broadcast android:name="android.intent.action.ACTION_IDLE_MAINTENANCE_START" />
     <protected-broadcast android:name="android.intent.action.ACTION_IDLE_MAINTENANCE_END" />
diff --git a/core/res/res/drawable/ic_decor_close_button_dark_focused.xml b/core/res/res/drawable/ic_decor_close_button_dark_focused.xml
index d7b167dd..0794ed3 100644
--- a/core/res/res/drawable/ic_decor_close_button_dark_focused.xml
+++ b/core/res/res/drawable/ic_decor_close_button_dark_focused.xml
@@ -23,7 +23,7 @@
            android:translateX="8.0"
            android:translateY="8.0" >
         <path
-            android:fillColor="#FFFFFFFF"
+            android:fillColor="#ff000000"
             android:pathData="M6.9,4.0l-2.9,2.9 9.1,9.1 -9.1,9.200001 2.9,2.799999 9.1,-9.1 9.1,9.1 2.9,-2.799999 -9.1,-9.200001 9.1,-9.1 -2.9,-2.9 -9.1,9.2z"/>
     </group>
 </vector>
diff --git a/core/res/res/drawable/ic_decor_close_button_dark_unfocused.xml b/core/res/res/drawable/ic_decor_close_button_dark_unfocused.xml
index e2e81b9..bd1db51 100644
--- a/core/res/res/drawable/ic_decor_close_button_dark_unfocused.xml
+++ b/core/res/res/drawable/ic_decor_close_button_dark_unfocused.xml
@@ -23,7 +23,7 @@
            android:translateX="8.0"
            android:translateY="8.0" >
         <path
-            android:fillColor="#33FFFFFF"
+            android:fillColor="#33000000"
             android:pathData="M6.9,4.0l-2.9,2.9 9.1,9.1 -9.1,9.200001 2.9,2.799999 9.1,-9.1 9.1,9.1 2.9,-2.799999 -9.1,-9.200001 9.1,-9.1 -2.9,-2.9 -9.1,9.2z"/>
     </group>
 </vector>
diff --git a/core/res/res/drawable/ic_decor_close_button_light_focused.xml b/core/res/res/drawable/ic_decor_close_button_light_focused.xml
index 0794ed3..d7b167dd 100644
--- a/core/res/res/drawable/ic_decor_close_button_light_focused.xml
+++ b/core/res/res/drawable/ic_decor_close_button_light_focused.xml
@@ -23,7 +23,7 @@
            android:translateX="8.0"
            android:translateY="8.0" >
         <path
-            android:fillColor="#ff000000"
+            android:fillColor="#FFFFFFFF"
             android:pathData="M6.9,4.0l-2.9,2.9 9.1,9.1 -9.1,9.200001 2.9,2.799999 9.1,-9.1 9.1,9.1 2.9,-2.799999 -9.1,-9.200001 9.1,-9.1 -2.9,-2.9 -9.1,9.2z"/>
     </group>
 </vector>
diff --git a/core/res/res/drawable/ic_decor_close_button_light_unfocused.xml b/core/res/res/drawable/ic_decor_close_button_light_unfocused.xml
index bd1db51..e2e81b9 100644
--- a/core/res/res/drawable/ic_decor_close_button_light_unfocused.xml
+++ b/core/res/res/drawable/ic_decor_close_button_light_unfocused.xml
@@ -23,7 +23,7 @@
            android:translateX="8.0"
            android:translateY="8.0" >
         <path
-            android:fillColor="#33000000"
+            android:fillColor="#33FFFFFF"
             android:pathData="M6.9,4.0l-2.9,2.9 9.1,9.1 -9.1,9.200001 2.9,2.799999 9.1,-9.1 9.1,9.1 2.9,-2.799999 -9.1,-9.200001 9.1,-9.1 -2.9,-2.9 -9.1,9.2z"/>
     </group>
 </vector>
diff --git a/core/res/res/drawable/ic_decor_maximize_button_dark_focused.xml b/core/res/res/drawable/ic_decor_maximize_button_dark_focused.xml
index 73d808b..c23390e 100644
--- a/core/res/res/drawable/ic_decor_maximize_button_dark_focused.xml
+++ b/core/res/res/drawable/ic_decor_maximize_button_dark_focused.xml
@@ -23,10 +23,10 @@
            android:translateX="8.0"
            android:translateY="8.0" >
         <path
-            android:fillColor="#FFFFFFFF"
+            android:fillColor="#FF000000"
             android:pathData="M2.0,4.0l0.0,16.0l28.0,0.0L30.0,4.0L2.0,4.0zM26.0,16.0L6.0,16.0L6.0,8.0l20.0,0.0L26.0,16.0z"/>
         <path
-            android:fillColor="#B2FFFFFF"
+            android:fillColor="#B2000000"
             android:pathData="M2.0,24.0l28.0,0.0l0.0,4.0l-28.0,0.0z"/>
     </group>
 </vector>
diff --git a/core/res/res/drawable/ic_decor_maximize_button_dark_unfocused.xml b/core/res/res/drawable/ic_decor_maximize_button_dark_unfocused.xml
index dc79e10..a194a39 100644
--- a/core/res/res/drawable/ic_decor_maximize_button_dark_unfocused.xml
+++ b/core/res/res/drawable/ic_decor_maximize_button_dark_unfocused.xml
@@ -23,10 +23,10 @@
            android:translateX="8.0"
            android:translateY="8.0" >
         <path
-            android:fillColor="#33FFFFFF"
+            android:fillColor="#33000000"
             android:pathData="M2.0,4.0l0.0,16.0l28.0,0.0L30.0,4.0L2.0,4.0zM26.0,16.0L6.0,16.0L6.0,8.0l20.0,0.0L26.0,16.0z"/>
         <path
-            android:fillColor="#33FFFFFF"
+            android:fillColor="#33000000"
             android:pathData="M2.0,24.0l28.0,0.0l0.0,4.0l-28.0,0.0z"/>
     </group>
 </vector>
diff --git a/core/res/res/drawable/ic_decor_maximize_button_light_focused.xml b/core/res/res/drawable/ic_decor_maximize_button_light_focused.xml
index c23390e..73d808b 100644
--- a/core/res/res/drawable/ic_decor_maximize_button_light_focused.xml
+++ b/core/res/res/drawable/ic_decor_maximize_button_light_focused.xml
@@ -23,10 +23,10 @@
            android:translateX="8.0"
            android:translateY="8.0" >
         <path
-            android:fillColor="#FF000000"
+            android:fillColor="#FFFFFFFF"
             android:pathData="M2.0,4.0l0.0,16.0l28.0,0.0L30.0,4.0L2.0,4.0zM26.0,16.0L6.0,16.0L6.0,8.0l20.0,0.0L26.0,16.0z"/>
         <path
-            android:fillColor="#B2000000"
+            android:fillColor="#B2FFFFFF"
             android:pathData="M2.0,24.0l28.0,0.0l0.0,4.0l-28.0,0.0z"/>
     </group>
 </vector>
diff --git a/core/res/res/drawable/ic_decor_maximize_button_light_unfocused.xml b/core/res/res/drawable/ic_decor_maximize_button_light_unfocused.xml
index a194a39..dc79e10 100644
--- a/core/res/res/drawable/ic_decor_maximize_button_light_unfocused.xml
+++ b/core/res/res/drawable/ic_decor_maximize_button_light_unfocused.xml
@@ -23,10 +23,10 @@
            android:translateX="8.0"
            android:translateY="8.0" >
         <path
-            android:fillColor="#33000000"
+            android:fillColor="#33FFFFFF"
             android:pathData="M2.0,4.0l0.0,16.0l28.0,0.0L30.0,4.0L2.0,4.0zM26.0,16.0L6.0,16.0L6.0,8.0l20.0,0.0L26.0,16.0z"/>
         <path
-            android:fillColor="#33000000"
+            android:fillColor="#33FFFFFF"
             android:pathData="M2.0,24.0l28.0,0.0l0.0,4.0l-28.0,0.0z"/>
     </group>
 </vector>
diff --git a/core/res/res/layout/decor_caption.xml b/core/res/res/layout/decor_caption.xml
new file mode 100644
index 0000000..0246736
--- /dev/null
+++ b/core/res/res/layout/decor_caption.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2015, 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.
+*/
+-->
+
+<com.android.internal.widget.DecorCaptionView xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:descendantFocusability="beforeDescendants" >
+    <LinearLayout
+            android:id="@+id/caption"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="end"
+            android:background="@drawable/decor_caption_title"
+            android:focusable="false"
+            android:descendantFocusability="blocksDescendants" >
+        <Button
+                android:id="@+id/maximize_window"
+                android:layout_width="32dp"
+                android:layout_height="32dp"
+                android:layout_margin="5dp"
+                android:padding="4dp"
+                android:layout_gravity="center_vertical|end"
+                android:contentDescription="@string/maximize_button_text"
+                android:background="@drawable/decor_maximize_button_dark" />
+        <Button
+                android:id="@+id/close_window"
+                android:layout_width="32dp"
+                android:layout_height="32dp"
+                android:layout_margin="5dp"
+                android:padding="4dp"
+                android:layout_gravity="center_vertical|end"
+                android:contentDescription="@string/close_button_text"
+                android:background="@drawable/decor_close_button_dark" />
+    </LinearLayout>
+</com.android.internal.widget.DecorCaptionView>
diff --git a/core/res/res/layout/decor_caption_dark.xml b/core/res/res/layout/decor_caption_dark.xml
deleted file mode 100644
index 95d2289..0000000
--- a/core/res/res/layout/decor_caption_dark.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2015, 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.
-*/
--->
-
-<com.android.internal.widget.DecorCaptionView xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="vertical"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:descendantFocusability="beforeDescendants" >
-    <LinearLayout
-        android:id="@+id/caption"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:gravity="end"
-        android:background="@drawable/decor_caption_title"
-        android:focusable="false"
-        android:descendantFocusability="blocksDescendants" >
-        <Button
-            android:id="@+id/maximize_window"
-            android:layout_width="32dp"
-            android:layout_height="32dp"
-            android:layout_margin="5dp"
-            android:padding="4dp"
-            android:layout_gravity="center_vertical|end"
-            android:contentDescription="@string/maximize_button_text"
-            android:background="@drawable/decor_maximize_button_dark" />
-        <Button
-            android:id="@+id/close_window"
-            android:layout_width="32dp"
-            android:layout_height="32dp"
-            android:layout_margin="5dp"
-            android:padding="4dp"
-            android:layout_gravity="center_vertical|end"
-            android:contentDescription="@string/close_button_text"
-            android:background="@drawable/decor_close_button_dark" />
-    </LinearLayout>
-</com.android.internal.widget.DecorCaptionView>
diff --git a/core/res/res/layout/decor_caption_light.xml b/core/res/res/layout/decor_caption_light.xml
deleted file mode 100644
index f0f661e..0000000
--- a/core/res/res/layout/decor_caption_light.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2015, 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.
-*/
--->
-
-<com.android.internal.widget.DecorCaptionView xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="vertical"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:descendantFocusability="beforeDescendants" >
-    <LinearLayout
-        android:id="@+id/caption"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:gravity="end"
-        android:background="@drawable/decor_caption_title"
-        android:focusable="false"
-        android:descendantFocusability="blocksDescendants" >
-        <Button
-            android:id="@+id/maximize_window"
-            android:layout_width="32dp"
-            android:layout_height="32dp"
-            android:layout_margin="5dp"
-            android:padding="4dp"
-            android:layout_gravity="center_vertical|end"
-            android:contentDescription="@string/maximize_button_text"
-            android:background="@drawable/decor_maximize_button_light" />
-        <Button
-            android:id="@+id/close_window"
-            android:layout_width="32dp"
-            android:layout_height="32dp"
-            android:layout_margin="5dp"
-            android:padding="4dp"
-            android:layout_gravity="center_vertical|end"
-            android:contentDescription="@string/close_button_text"
-            android:background="@drawable/decor_close_button_light" />
-    </LinearLayout>
-</com.android.internal.widget.DecorCaptionView>
diff --git a/core/res/res/layout/notification_material_action.xml b/core/res/res/layout/notification_material_action.xml
index 62602d8..398f52d 100644
--- a/core/res/res/layout/notification_material_action.xml
+++ b/core/res/res/layout/notification_material_action.xml
@@ -22,7 +22,7 @@
     android:layout_height="48dp"
     android:layout_gravity="center"
     android:layout_marginStart="4dp"
-    android:textColor="@color/secondary_text_material_light"
+    android:textColor="@color/notification_default_color"
     android:singleLine="true"
     android:ellipsize="end"
     android:background="@drawable/notification_material_action_background"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 9bca3d6..786554c 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3042,7 +3042,8 @@
         <attr name="imeSubtypeLocale" format="string" />
         <!-- The mode of the subtype. This string can be a mode (e.g. voice, keyboard...) and this
              string will be passed to the IME when the framework calls the IME with the
-             subtype.  -->
+             subtype.  {@link android.view.inputmethod.InputMethodSubtype#getLocale()} returns the
+             value specified in this attribute.  -->
         <attr name="imeSubtypeMode" format="string" />
         <!-- Set true if the subtype is auxiliary.  An auxiliary subtype won't be shown in the
              input method selection list in the settings app.
@@ -3067,6 +3068,9 @@
              this subtype. This is important because many password fields only allow
              ASCII-characters. -->
         <attr name="isAsciiCapable" format="boolean" />
+        <!-- The BCP-47 Language Tag of the subtype.  This replaces
+        {@link android.R.styleable#InputMethod_Subtype_imeSubtypeLocale}.  -->
+        <attr name="languageTag" format="string" />
     </declare-styleable>
 
     <!-- Use <code>spell-checker</code> as the root tag of the XML resource that
@@ -3090,7 +3094,8 @@
         <attr name="label" />
         <!-- The locale of the subtype. This string should be a locale (e.g. en_US, fr_FR...)
              This is also used by the framework to know the supported locales
-             of the spell checker.  -->
+             of the spell checker. {@link android.view.textservice.SpellCheckerSubtype#getLocale()}
+             returns the value specified in this attribute.  -->
         <attr name="subtypeLocale" format="string" />
         <!-- The extra value of the subtype. This string can be any string and will be passed to
              the SpellChecker.  -->
@@ -3102,6 +3107,9 @@
              {@code Arrays.hashCode(new Object[] {subtypeLocale, extraValue}) will be used instead.
               -->
         <attr name="subtypeId" />
+        <!-- The BCP-47 Language Tag of the subtype.  This replaces
+        {@link android.R.styleable#SpellChecker_Subtype_subtypeLocale}.  -->
+        <attr name="languageTag" />
     </declare-styleable>
 
     <!-- Use <code>accessibility-service</code> as the root tag of the XML resource that
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index af8ff2e..7711825 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -130,8 +130,8 @@
     <drawable name="notification_template_divider">#29000000</drawable>
     <drawable name="notification_template_divider_media">#29ffffff</drawable>
 
-    <color name="notification_icon_default_color">#ff616161</color>
-    <color name="notification_action_color_filter">@color/secondary_text_material_light</color>
+    <color name="notification_default_color">#757575</color> <!-- Gray 600 -->
+    <color name="notification_icon_default_color">@color/notification_default_color</color>
 
     <color name="notification_progress_background_color">@color/secondary_text_material_light</color>
 
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index b6b2e20..addeb05 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2683,6 +2683,7 @@
     <public type="attr" name="encryptionAware" />
     <public type="attr" name="preferenceFragmentStyle" />
     <public type="attr" name="canControlMagnification" />
+    <public type="attr" name="languageTag" />
 
     <public type="style" name="Theme.Material.DayNight" />
     <public type="style" name="Theme.Material.DayNight.DarkActionBar" />
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index eadcae0..dd81f89 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -16,10 +16,6 @@
 */
 -->
 <resources>
-  <!-- We don't want to publish private symbols in android.R as part of the
-       SDK.  Instead, put them here. -->
-  <private-symbols package="com.android.internal" />
-
   <!-- Private symbols that we need to reference from framework code.  See
        frameworks/base/core/res/MakeJavaSymbols.sed for how to easily generate
        this.
@@ -1878,7 +1874,6 @@
   <java-symbol type="layout" name="notification_template_material_big_text" />
   <java-symbol type="layout" name="notification_template_header" />
   <java-symbol type="layout" name="notification_material_media_action" />
-  <java-symbol type="color" name="notification_action_color_filter" />
   <java-symbol type="color" name="notification_icon_default_color" />
   <java-symbol type="color" name="notification_progress_background_color" />
   <java-symbol type="id" name="media_actions" />
@@ -1958,9 +1953,12 @@
   <java-symbol type="bool" name="config_built_in_sip_phone" />
   <java-symbol type="id" name="maximize_window" />
   <java-symbol type="id" name="close_window" />
-  <java-symbol type="layout" name="decor_caption_light" />
-  <java-symbol type="layout" name="decor_caption_dark" />
+  <java-symbol type="layout" name="decor_caption" />
   <java-symbol type="drawable" name="decor_caption_title_focused" />
+  <java-symbol type="drawable" name="decor_close_button_dark" />
+  <java-symbol type="drawable" name="decor_close_button_light" />
+  <java-symbol type="drawable" name="decor_maximize_button_dark" />
+  <java-symbol type="drawable" name="decor_maximize_button_light" />
 
   <!-- From TelephonyProvider -->
   <java-symbol type="xml" name="apns" />
@@ -2267,6 +2265,7 @@
 
   <!-- Floating toolbar -->
   <java-symbol type="id" name="floating_toolbar_menu_item_image_button" />
+  <java-symbol type="id" name="overflow" />
   <java-symbol type="layout" name="floating_popup_container" />
   <java-symbol type="layout" name="floating_popup_menu_button" />
   <java-symbol type="layout" name="floating_popup_open_overflow_button" />
diff --git a/core/tests/coretests/res/layout/activity_text_view.xml b/core/tests/coretests/res/layout/activity_text_view.xml
index 7ab0b13..e795c10 100644
--- a/core/tests/coretests/res/layout/activity_text_view.xml
+++ b/core/tests/coretests/res/layout/activity_text_view.xml
@@ -23,6 +23,6 @@
     <EditText
             android:id="@+id/textview"
             android:layout_width="match_parent"
-            android:layout_height="match_parent" />
+            android:layout_height="wrap_content" />
 
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
diff --git a/core/tests/coretests/src/android/view/textservice/SpellCheckerSubtypeTest.java b/core/tests/coretests/src/android/view/textservice/SpellCheckerSubtypeTest.java
index 73fdb10..4a1c414 100644
--- a/core/tests/coretests/src/android/view/textservice/SpellCheckerSubtypeTest.java
+++ b/core/tests/coretests/src/android/view/textservice/SpellCheckerSubtypeTest.java
@@ -30,11 +30,16 @@
  */
 public class SpellCheckerSubtypeTest extends InstrumentationTestCase {
     private static final int SUBTYPE_SUBTYPE_ID_NONE = 0;
+    private static final String SUBTYPE_SUBTYPE_LOCALE_STRING_NONE = "";
+    private static final String SUBTYPE_SUBTYPE_LANGUAGE_TAG_NONE = "";
+
     private static final String SUBTYPE_SUBTYPE_LOCALE_STRING_A = "en_GB";
+    private static final String SUBTYPE_SUBTYPE_LANGUAGE_TAG_A = "en-GB";
     private static final int SUBTYPE_NAME_RES_ID_A = 0x12345;
     private static final String SUBTYPE_EXTRA_VALUE_A = "Key1=Value1,Key2=Value2";
     private static final int SUBTYPE_SUBTYPE_ID_A = 42;
     private static final String SUBTYPE_SUBTYPE_LOCALE_STRING_B = "en_IN";
+    private static final String SUBTYPE_SUBTYPE_LANGUAGE_TAG_B = "en-IN";
     private static final int SUBTYPE_NAME_RES_ID_B = 0x54321;
     private static final String SUBTYPE_EXTRA_VALUE_B = "Key3=Value3,Key4=Value4";
     private static final int SUBTYPE_SUBTYPE_ID_B = -42;
@@ -60,9 +65,11 @@
     @SmallTest
     public void testSubtypeWithNoSubtypeId() throws Exception {
         final SpellCheckerSubtype subtype = new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A,
-                SUBTYPE_SUBTYPE_LOCALE_STRING_A, SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE);
+                SUBTYPE_SUBTYPE_LOCALE_STRING_A, SUBTYPE_SUBTYPE_LANGUAGE_TAG_A,
+                SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE);
         assertEquals(SUBTYPE_NAME_RES_ID_A, subtype.getNameResId());
         assertEquals(SUBTYPE_SUBTYPE_LOCALE_STRING_A, subtype.getLocale());
+        assertEquals(SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, subtype.getLanguageTag());
         assertEquals("Value1", subtype.getExtraValueOf("Key1"));
         assertEquals("Value2", subtype.getExtraValueOf("Key2"));
         // Historically we have used SpellCheckerSubtype#hashCode() to track which subtype is
@@ -75,6 +82,7 @@
         final SpellCheckerSubtype clonedSubtype = cloneViaParcel(subtype);
         assertEquals(SUBTYPE_NAME_RES_ID_A, clonedSubtype.getNameResId());
         assertEquals(SUBTYPE_SUBTYPE_LOCALE_STRING_A, clonedSubtype.getLocale());
+        assertEquals(SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, clonedSubtype.getLanguageTag());
         assertEquals("Value1", clonedSubtype.getExtraValueOf("Key1"));
         assertEquals("Value2", clonedSubtype.getExtraValueOf("Key2"));
         assertEquals(
@@ -84,10 +92,12 @@
 
     public void testSubtypeWithSubtypeId() throws Exception {
         final SpellCheckerSubtype subtype = new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A,
-                SUBTYPE_SUBTYPE_LOCALE_STRING_A, SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A);
+                SUBTYPE_SUBTYPE_LOCALE_STRING_A, SUBTYPE_SUBTYPE_LANGUAGE_TAG_A,
+                SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A);
 
         assertEquals(SUBTYPE_NAME_RES_ID_A, subtype.getNameResId());
         assertEquals(SUBTYPE_SUBTYPE_LOCALE_STRING_A, subtype.getLocale());
+        assertEquals(SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, subtype.getLanguageTag());
         assertEquals("Value1", subtype.getExtraValueOf("Key1"));
         assertEquals("Value2", subtype.getExtraValueOf("Key2"));
         // Similar to "SubtypeId" in InputMethodSubtype, "SubtypeId" in SpellCheckerSubtype enables
@@ -97,6 +107,7 @@
         final SpellCheckerSubtype clonedSubtype = cloneViaParcel(subtype);
         assertEquals(SUBTYPE_NAME_RES_ID_A, clonedSubtype.getNameResId());
         assertEquals(SUBTYPE_SUBTYPE_LOCALE_STRING_A, clonedSubtype.getLocale());
+        assertEquals(SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, clonedSubtype.getLanguageTag());
         assertEquals("Value1", clonedSubtype.getExtraValueOf("Key1"));
         assertEquals("Value2", clonedSubtype.getExtraValueOf("Key2"));
         assertEquals(SUBTYPE_SUBTYPE_ID_A, clonedSubtype.hashCode());
@@ -104,30 +115,42 @@
 
     @SmallTest
     public void testGetLocaleObject() throws Exception {
-        assertEquals(new Locale("en"), new SpellCheckerSubtype(
-                SUBTYPE_NAME_RES_ID_A, "en", SUBTYPE_EXTRA_VALUE_A).getLocaleObject());
-        assertEquals(new Locale("en", "US"), new SpellCheckerSubtype(
-                SUBTYPE_NAME_RES_ID_A, "en_US", SUBTYPE_EXTRA_VALUE_A).getLocaleObject());
-        assertEquals(new Locale("en", "US", "POSIX"), new SpellCheckerSubtype(
-                SUBTYPE_NAME_RES_ID_A, "en_US_POSIX", SUBTYPE_EXTRA_VALUE_A).getLocaleObject());
+        assertEquals(new Locale("en", "GB"),
+                new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, "en_GB",
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_NONE, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_NONE).getLocaleObject());
+        assertEquals(new Locale("en", "GB"),
+                new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_NONE,
+                        "en-GB", SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_NONE).getLocaleObject());
 
-        // Special rewrite rule for "tl" for versions of Android earlier than Lollipop that did not
-        // support three letter language codes, and used "tl" (Tagalog) as the language string for
-        // "fil" (Filipino).
-        assertEquals(new Locale("fil"), new SpellCheckerSubtype(
-                SUBTYPE_NAME_RES_ID_A, "tl", SUBTYPE_EXTRA_VALUE_A).getLocaleObject());
-        assertEquals(new Locale("fil", "PH"), new SpellCheckerSubtype(
-                SUBTYPE_NAME_RES_ID_A, "tl_PH", SUBTYPE_EXTRA_VALUE_A).getLocaleObject());
-        assertEquals(new Locale("fil", "PH", "POSIX"), new SpellCheckerSubtype(
-                SUBTYPE_NAME_RES_ID_A, "tl_PH_POSIX", SUBTYPE_EXTRA_VALUE_A).getLocaleObject());
+        // If neither locale string nor language tag is specified,
+        // {@link SpellCheckerSubtype#getLocaleObject} returns null.
+        assertNull(
+                new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_NONE,
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_NONE, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_NONE).getLocaleObject());
 
-        // So far rejecting invalid/unexpected locale strings is out of the scope.
-        assertEquals(new Locale("a"), new SpellCheckerSubtype(
-                SUBTYPE_NAME_RES_ID_A, "a", SUBTYPE_EXTRA_VALUE_A).getLocaleObject());
-        assertEquals(new Locale("a b c"), new SpellCheckerSubtype(
-                SUBTYPE_NAME_RES_ID_A, "a b c", SUBTYPE_EXTRA_VALUE_A).getLocaleObject());
-        assertEquals(new Locale("en-US"), new SpellCheckerSubtype(
-                SUBTYPE_NAME_RES_ID_A, "en-US", SUBTYPE_EXTRA_VALUE_A).getLocaleObject());
+        // If both locale string and language tag are specified,
+        // {@link SpellCheckerSubtype#getLocaleObject} uses language tag.
+        assertEquals(new Locale("en", "GB"),
+                new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, "en_US", "en-GB",
+                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE).getLocaleObject());
+
+        // Make sure that "tl_PH" is rewritten to "fil_PH" for spell checkers that need to support
+        // Android KitKat and prior, which do not support 3-letter language codes.
+        assertEquals(new Locale("fil", "PH"),
+                new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, "tl_PH",
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_NONE, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_NONE).getLocaleObject());
+
+        // "languageTag" attribute is available in Android N and later, where 3-letter country codes
+        // are guaranteed to be available.  It's developers' responsibility for specifying a valid
+        // country subtags here and we do not rewrite "tl" to "fil" for simplicity.
+        assertEquals("tl",
+                new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_NONE,
+                        "tl-PH", SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE)
+                        .getLocaleObject().getLanguage());
     }
 
     @SmallTest
@@ -156,50 +179,83 @@
         // If subtype ID is 0 (== SUBTYPE_SUBTYPE_ID_NONE), we keep the same behavior.
         assertEquals(
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
-                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE),
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_NONE),
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
-                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE));
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_NONE));
         assertNotEqual(
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
-                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE),
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_NONE),
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_B, SUBTYPE_SUBTYPE_LOCALE_STRING_B,
-                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE));
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_NONE));
         assertNotEqual(
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
-                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE),
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_NONE),
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_B,
-                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE));
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_NONE));
         assertNotEqual(
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
-                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_NONE),
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_NONE),
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
-                        SUBTYPE_EXTRA_VALUE_B, SUBTYPE_SUBTYPE_ID_NONE));
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_B, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_NONE));
+        assertNotEqual(
+                new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_NONE),
+                new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_B,
+                        SUBTYPE_SUBTYPE_ID_NONE));
 
         // If subtype ID is not 0, we test the equality based only on the subtype ID.
         assertEquals(
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
-                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A),
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_A),
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
-                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A));
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_A));
         assertEquals(
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
-                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A),
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_A),
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_B, SUBTYPE_SUBTYPE_LOCALE_STRING_B,
-                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A));
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_A));
         assertEquals(
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
-                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A),
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_A),
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_B,
-                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A));
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_A));
         assertEquals(
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
-                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A),
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_A),
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
-                        SUBTYPE_EXTRA_VALUE_B, SUBTYPE_SUBTYPE_ID_A));
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_B, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_A));
+        assertEquals(
+                new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_A),
+                new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_B,
+                        SUBTYPE_SUBTYPE_ID_A));
         assertNotEqual(
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
-                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_A),
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_A),
                 new SpellCheckerSubtype(SUBTYPE_NAME_RES_ID_A, SUBTYPE_SUBTYPE_LOCALE_STRING_A,
-                        SUBTYPE_EXTRA_VALUE_A, SUBTYPE_SUBTYPE_ID_B));
+                        SUBTYPE_SUBTYPE_LANGUAGE_TAG_A, SUBTYPE_EXTRA_VALUE_A,
+                        SUBTYPE_SUBTYPE_ID_B));
     }
+
 }
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
index ddbdc87..83a9e01 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
@@ -16,15 +16,23 @@
 
 package android.widget;
 
+import static android.widget.espresso.DragHandleUtils.assertNoSelectionHandles;
+import static android.widget.espresso.DragHandleUtils.onHandleView;
+import static android.widget.espresso.TextViewActions.mouseClickOnTextAtIndex;
 import static android.widget.espresso.TextViewActions.mouseDoubleClickOnTextAtIndex;
 import static android.widget.espresso.TextViewActions.mouseLongClickOnTextAtIndex;
 import static android.widget.espresso.TextViewActions.mouseDoubleClickAndDragOnText;
 import static android.widget.espresso.TextViewActions.mouseDragOnText;
 import static android.widget.espresso.TextViewActions.mouseLongClickAndDragOnText;
+import static android.widget.espresso.TextViewActions.mouseTripleClickAndDragOnText;
+import static android.widget.espresso.TextViewActions.mouseTripleClickOnTextAtIndex;
 import static android.widget.espresso.TextViewAssertions.hasSelection;
 import static android.support.test.espresso.Espresso.onView;
 import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.action.ViewActions.replaceText;
 import static android.support.test.espresso.action.ViewActions.typeTextIntoFocusedView;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
 
 import com.android.frameworks.coretests.R;
@@ -48,10 +56,23 @@
         final String helloWorld = "Hello world!";
         onView(withId(R.id.textview)).perform(click());
         onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
+
+        assertNoSelectionHandles();
+
         onView(withId(R.id.textview)).perform(
                 mouseDragOnText(helloWorld.indexOf("llo"), helloWorld.indexOf("ld!")));
 
         onView(withId(R.id.textview)).check(hasSelection("llo wor"));
+
+        onHandleView(com.android.internal.R.id.selection_start_handle)
+                .check(matches(isDisplayed()));
+        onHandleView(com.android.internal.R.id.selection_end_handle)
+                .check(matches(isDisplayed()));
+
+        onView(withId(R.id.textview)).perform(mouseClickOnTextAtIndex(helloWorld.indexOf("w")));
+        onView(withId(R.id.textview)).check(hasSelection(""));
+
+        assertNoSelectionHandles();
     }
 
     @SmallTest
@@ -89,6 +110,9 @@
         onView(withId(R.id.textview)).perform(mouseLongClickOnTextAtIndex(
                 helloWorld.indexOf("rld")));
         onView(withId(R.id.textview)).check(hasSelection("world"));
+
+        onView(withId(R.id.textview)).perform(mouseLongClickOnTextAtIndex(helloWorld.length()));
+        onView(withId(R.id.textview)).check(hasSelection("!"));
     }
 
     @SmallTest
@@ -113,6 +137,9 @@
         onView(withId(R.id.textview)).perform(mouseDoubleClickOnTextAtIndex(
                 helloWorld.indexOf("rld")));
         onView(withId(R.id.textview)).check(hasSelection("world"));
+
+        onView(withId(R.id.textview)).perform(mouseDoubleClickOnTextAtIndex(helloWorld.length()));
+        onView(withId(R.id.textview)).check(hasSelection("!"));
     }
 
     @SmallTest
@@ -166,4 +193,102 @@
                 mouseLongClickAndDragOnText(text.indexOf("j"), text.indexOf("f")));
         onView(withId(R.id.textview)).check(hasSelection("efg hijk"));
     }
+
+    @SmallTest
+    public void testSelectTextByTripleClick() throws Exception {
+        getActivity();
+
+        final StringBuilder builder = new StringBuilder();
+        builder.append("First paragraph.\n");
+        builder.append("Second paragraph.");
+        for (int i = 0; i < 10; i++) {
+            builder.append(" This paragraph is very long.");
+        }
+        builder.append('\n');
+        builder.append("Third paragraph.");
+        final String text = builder.toString();
+
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(replaceText(text));
+
+        onView(withId(R.id.textview)).perform(
+                mouseTripleClickOnTextAtIndex(text.indexOf("rst")));
+        onView(withId(R.id.textview)).check(hasSelection("First paragraph.\n"));
+
+        onView(withId(R.id.textview)).perform(
+                mouseTripleClickOnTextAtIndex(text.indexOf("cond")));
+        onView(withId(R.id.textview)).check(hasSelection(
+                text.substring(text.indexOf("Second"), text.indexOf("Third"))));
+
+        onView(withId(R.id.textview)).perform(
+                mouseTripleClickOnTextAtIndex(text.indexOf("ird")));
+        onView(withId(R.id.textview)).check(hasSelection("Third paragraph."));
+
+        onView(withId(R.id.textview)).perform(
+                mouseTripleClickOnTextAtIndex(text.indexOf("very long")));
+        onView(withId(R.id.textview)).check(hasSelection(
+                text.substring(text.indexOf("Second"), text.indexOf("Third"))));
+    }
+
+    @SmallTest
+    public void testSelectTextByTripleClickAndDrag() throws Exception {
+        getActivity();
+
+        final StringBuilder builder = new StringBuilder();
+        builder.append("First paragraph.\n");
+        builder.append("Second paragraph.");
+        for (int i = 0; i < 10; i++) {
+            builder.append(" This paragraph is very long.");
+        }
+        builder.append('\n');
+        builder.append("Third paragraph.");
+        final String text = builder.toString();
+
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(replaceText(text));
+
+        onView(withId(R.id.textview)).perform(
+                mouseTripleClickAndDragOnText(text.indexOf("irst"), text.indexOf("st")));
+        onView(withId(R.id.textview)).check(hasSelection("First paragraph.\n"));
+
+        onView(withId(R.id.textview)).perform(
+                mouseTripleClickAndDragOnText(text.indexOf("cond"), text.indexOf("Third") - 2));
+        onView(withId(R.id.textview)).check(hasSelection(
+                text.substring(text.indexOf("Second"), text.indexOf("Third"))));
+
+        onView(withId(R.id.textview)).perform(
+                mouseTripleClickAndDragOnText(text.indexOf("First"), text.indexOf("ird")));
+        onView(withId(R.id.textview)).check(hasSelection(text));
+    }
+
+    @SmallTest
+    public void testSelectTextByTripleClickAndDrag_reverse() throws Exception {
+        getActivity();
+
+        final StringBuilder builder = new StringBuilder();
+        builder.append("First paragraph.\n");
+        builder.append("Second paragraph.");
+        for (int i = 0; i < 10; i++) {
+            builder.append(" This paragraph is very long.");
+        }
+        builder.append('\n');
+        builder.append("Third paragraph.");
+        final String text = builder.toString();
+
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(replaceText(text));
+
+        onView(withId(R.id.textview)).perform(
+                mouseTripleClickAndDragOnText(text.indexOf("st"), text.indexOf("irst")));
+        onView(withId(R.id.textview)).check(hasSelection("First paragraph.\n"));
+
+        onView(withId(R.id.textview)).perform(
+                mouseTripleClickAndDragOnText(text.indexOf("Third") - 2, text.indexOf("cond")));
+        onView(withId(R.id.textview)).check(hasSelection(
+                text.substring(text.indexOf("Second"), text.indexOf("Third"))));
+
+        onView(withId(R.id.textview)).perform(
+                mouseTripleClickAndDragOnText(text.indexOf("ird"), text.indexOf("First")));
+        onView(withId(R.id.textview)).check(hasSelection(text));
+    }
 }
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 4614505..e54723e 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -16,6 +16,8 @@
 
 package android.widget;
 
+import static android.widget.espresso.DragHandleUtils.assertNoSelectionHandles;
+import static android.widget.espresso.DragHandleUtils.onHandleView;
 import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
 import static android.widget.espresso.TextViewActions.doubleTapAndDragOnText;
 import static android.widget.espresso.TextViewActions.doubleClickOnTextAtIndex;
@@ -26,24 +28,22 @@
 import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex;
 import static android.widget.espresso.TextViewAssertions.hasSelection;
 import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsDisplayed;
+import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsNotDisplayed;
+import static android.widget.espresso.FloatingToolbarEspressoUtils.sleepForFloatingToolbarPopup;
+import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarContainsItem;
+import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarDoesNotContainItem;
 import static android.support.test.espresso.Espresso.onView;
 import static android.support.test.espresso.action.ViewActions.click;
 import static android.support.test.espresso.action.ViewActions.pressKey;
+import static android.support.test.espresso.action.ViewActions.replaceText;
 import static android.support.test.espresso.action.ViewActions.typeTextIntoFocusedView;
 import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
-import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
-import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
 import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
 import static android.support.test.espresso.matcher.ViewMatchers.withText;
-import static org.hamcrest.Matchers.allOf;
 
 import com.android.frameworks.coretests.R;
 
-import android.support.test.espresso.NoMatchingRootException;
-import android.support.test.espresso.NoMatchingViewException;
-import android.support.test.espresso.ViewInteraction;
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.view.KeyEvent;
@@ -153,19 +153,115 @@
 
     @SmallTest
     public void testToolbarAppearsAfterSelection() throws Exception {
-        // It'll be nice to check that the toolbar is not visible (or does not exist) here
-        // I can't currently find a way to do this. I'll get to it later.
-
         final String text = "Toolbar appears after selection.";
         onView(withId(R.id.textview)).perform(click());
+        assertFloatingToolbarIsNotDisplayed();
         onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
         onView(withId(R.id.textview)).perform(
                 longPressOnTextAtIndex(text.indexOf("appears")));
 
-        // It takes the toolbar less than 100ms to start to animate into screen.
-        // Ideally, we'll wait using the UiController, but I guess this works for now.
-        Thread.sleep(100);
-        assertFloatingToolbarIsDisplayed(getActivity());
+        sleepForFloatingToolbarPopup();
+        assertFloatingToolbarIsDisplayed();
+
+        final String text2 = "Toolbar disappears after typing text.";
+        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text2));
+        assertFloatingToolbarIsNotDisplayed();
+    }
+
+    @SmallTest
+    public void testToolbarAndInsertionHandle() throws Exception {
+        final String text = "text";
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
+        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
+        assertFloatingToolbarIsNotDisplayed();
+
+        onHandleView(com.android.internal.R.id.insertion_handle).perform(click());
+        sleepForFloatingToolbarPopup();
+        assertFloatingToolbarIsDisplayed();
+
+        assertFloatingToolbarContainsItem(
+                getActivity().getString(com.android.internal.R.string.selectAll));
+        assertFloatingToolbarDoesNotContainItem(
+                getActivity().getString(com.android.internal.R.string.copy));
+        assertFloatingToolbarDoesNotContainItem(
+                getActivity().getString(com.android.internal.R.string.cut));
+    }
+
+    @SmallTest
+    public void testToolbarAndSelectionHandle() throws Exception {
+        final String text = "abcd efg hijk";
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
+
+        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf("f")));
+        sleepForFloatingToolbarPopup();
+        assertFloatingToolbarIsDisplayed();
+
+        assertFloatingToolbarContainsItem(
+                getActivity().getString(com.android.internal.R.string.selectAll));
+        assertFloatingToolbarContainsItem(
+                getActivity().getString(com.android.internal.R.string.copy));
+        assertFloatingToolbarContainsItem(
+                getActivity().getString(com.android.internal.R.string.cut));
+
+        final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
+        onHandleView(com.android.internal.R.id.selection_start_handle)
+                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('a')));
+        sleepForFloatingToolbarPopup();
+        assertFloatingToolbarIsDisplayed();
+
+        onHandleView(com.android.internal.R.id.selection_end_handle)
+                .perform(dragHandle(textView, Handle.SELECTION_END, text.length()));
+        sleepForFloatingToolbarPopup();
+        assertFloatingToolbarIsDisplayed();
+
+        assertFloatingToolbarDoesNotContainItem(
+                getActivity().getString(com.android.internal.R.string.selectAll));
+        assertFloatingToolbarContainsItem(
+                getActivity().getString(com.android.internal.R.string.copy));
+        assertFloatingToolbarContainsItem(
+                getActivity().getString(com.android.internal.R.string.cut));
+    }
+
+    @SmallTest
+    public void testInsertionHandle() throws Exception {
+        final String text = "abcd efg hijk ";
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
+
+        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length()));
+
+        final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
+
+        onHandleView(com.android.internal.R.id.insertion_handle)
+                .perform(dragHandle(textView, Handle.INSERTION, text.indexOf('a')));
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("a")));
+
+        onHandleView(com.android.internal.R.id.insertion_handle)
+                .perform(dragHandle(textView, Handle.INSERTION, text.indexOf('f')));
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("f")));
+    }
+
+    @SmallTest
+    public void testInsertionHandle_multiLine() throws Exception {
+        final String text = "abcd\n" + "efg\n" + "hijk\n";
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
+
+        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length()));
+
+        final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
+
+        onHandleView(com.android.internal.R.id.insertion_handle)
+                .perform(dragHandle(textView, Handle.INSERTION, text.indexOf('a')));
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("a")));
+
+        onHandleView(com.android.internal.R.id.insertion_handle)
+                .perform(dragHandle(textView, Handle.INSERTION, text.indexOf('f')));
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("f")));
     }
 
     @SmallTest
@@ -183,7 +279,7 @@
         onHandleView(com.android.internal.R.id.selection_end_handle)
                 .check(matches(isDisplayed()));
 
-        final TextView textView = (TextView)getActivity().findViewById(R.id.textview);
+        final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
         onHandleView(com.android.internal.R.id.selection_start_handle)
                 .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('a')));
         onView(withId(R.id.textview)).check(hasSelection("abcd efg"));
@@ -200,7 +296,7 @@
         onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
         onView(withId(R.id.textview)).perform(doubleClickOnTextAtIndex(text.indexOf('i')));
 
-        final TextView textView = (TextView)getActivity().findViewById(R.id.textview);
+        final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
         onHandleView(com.android.internal.R.id.selection_start_handle)
                 .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('e')));
         onView(withId(R.id.textview)).check(hasSelection("efg\nhijk"));
@@ -219,13 +315,46 @@
     }
 
     @SmallTest
+    public void testSelectionHandles_multiLine_rtl() throws Exception {
+        // Arabic text.
+        final String text = "\u062A\u062B\u062C\n" + "\u062D\u062E\u062F\n"
+                + "\u0630\u0631\u0632\n" + "\u0633\u0634\u0635\n" + "\u0636\u0637\u0638\n"
+                + "\u0639\u063A\u063B";
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(replaceText(text));
+        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.length()));
+        onView(withId(R.id.textview)).perform(doubleClickOnTextAtIndex(text.indexOf('\u0634')));
+
+        final TextView textView = (TextView)getActivity().findViewById(R.id.textview);
+        onHandleView(com.android.internal.R.id.selection_start_handle)
+                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('\u062E')));
+        onView(withId(R.id.textview)).check(hasSelection(
+                text.substring(text.indexOf('\u062D'), text.indexOf('\u0635') + 1)));
+
+        onHandleView(com.android.internal.R.id.selection_start_handle)
+                .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('\u062A')));
+        onView(withId(R.id.textview)).check(hasSelection(
+                text.substring(text.indexOf('\u062A'), text.indexOf('\u0635') + 1)));
+
+        onHandleView(com.android.internal.R.id.selection_end_handle)
+                .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('\u0638')));
+        onView(withId(R.id.textview)).check(hasSelection(
+                text.substring(text.indexOf('\u062A'), text.indexOf('\u0638') + 1)));
+
+        onHandleView(com.android.internal.R.id.selection_end_handle)
+                .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('\u063B')));
+        onView(withId(R.id.textview)).check(hasSelection(text));
+    }
+
+
+    @SmallTest
     public void testSelectionHandles_doesNotPassAnotherHandle() throws Exception {
         final String text = "abcd efg hijk lmn";
         onView(withId(R.id.textview)).perform(click());
         onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
         onView(withId(R.id.textview)).perform(doubleClickOnTextAtIndex(text.indexOf('f')));
 
-        final TextView textView = (TextView)getActivity().findViewById(R.id.textview);
+        final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
         onHandleView(com.android.internal.R.id.selection_start_handle)
                 .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('l')));
         onView(withId(R.id.textview)).check(hasSelection("g"));
@@ -243,7 +372,7 @@
         onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
         onView(withId(R.id.textview)).perform(doubleClickOnTextAtIndex(text.indexOf('i')));
 
-        final TextView textView = (TextView)getActivity().findViewById(R.id.textview);
+        final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
         onHandleView(com.android.internal.R.id.selection_start_handle)
                 .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('r') + 1));
         onView(withId(R.id.textview)).check(hasSelection("k"));
@@ -261,7 +390,7 @@
         onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
         onView(withId(R.id.textview)).perform(doubleClickOnTextAtIndex(text.indexOf('i')));
 
-        final TextView textView = (TextView)getActivity().findViewById(R.id.textview);
+        final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
 
         onHandleView(com.android.internal.R.id.selection_start_handle)
                 .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('f')));
@@ -314,7 +443,7 @@
         onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
         onView(withId(R.id.textview)).perform(doubleClickOnTextAtIndex(text.indexOf('m')));
 
-        final TextView textView = (TextView)getActivity().findViewById(R.id.textview);
+        final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
 
         onHandleView(com.android.internal.R.id.selection_start_handle)
                 .perform(dragHandle(textView, Handle.SELECTION_START, text.indexOf('c')));
@@ -342,25 +471,4 @@
                 .perform(dragHandle(textView, Handle.SELECTION_END, text.indexOf('i')));
         onView(withId(R.id.textview)).check(hasSelection("hijk"));
     }
-
-    private static void assertNoSelectionHandles() {
-        try {
-            onHandleView(com.android.internal.R.id.selection_start_handle)
-                    .check(matches(isDisplayed()));
-        } catch (NoMatchingRootException | NoMatchingViewException | AssertionError e) {
-            try {
-                onHandleView(com.android.internal.R.id.selection_end_handle)
-                        .check(matches(isDisplayed()));
-            } catch (NoMatchingRootException | NoMatchingViewException | AssertionError e1) {
-                return;
-            }
-        }
-        throw new AssertionError("Selection handle found");
-    }
-
-    private static ViewInteraction onHandleView(int id)
-            throws NoMatchingRootException, NoMatchingViewException, AssertionError {
-        return onView(allOf(withId(id), isAssignableFrom(Editor.HandleView.class)))
-                .inRoot(withDecorView(hasDescendant(withId(id))));
-    }
 }
diff --git a/core/tests/coretests/src/android/widget/espresso/DragAction.java b/core/tests/coretests/src/android/widget/espresso/DragAction.java
index ce97568..b2c8e38 100644
--- a/core/tests/coretests/src/android/widget/espresso/DragAction.java
+++ b/core/tests/coretests/src/android/widget/espresso/DragAction.java
@@ -157,6 +157,59 @@
         },
 
         /**
+         * Starts a drag with a mouse triple click.
+         */
+        MOUSE_TRIPLE_CLICK {
+            private DownMotionPerformer downMotion = new DownMotionPerformer() {
+                @Override
+                @Nullable
+                public MotionEvent perform(
+                        UiController uiController, float[] coordinates, float[] precision) {
+                    MotionEvent downEvent = MotionEvents.sendDown(
+                            uiController, coordinates, precision)
+                            .down;
+                    for (int i = 0; i < 2; ++i) {
+                        try {
+                            if (!MotionEvents.sendUp(uiController, downEvent)) {
+                                String logMessage = "Injection of up event as part of the triple "
+                                        + "click failed. Sending cancel event.";
+                                Log.d(TAG, logMessage);
+                                MotionEvents.sendCancel(uiController, downEvent);
+                                return null;
+                            }
+
+                            long doubleTapMinimumTimeout = ViewConfiguration.getDoubleTapMinTime();
+                            uiController.loopMainThreadForAtLeast(doubleTapMinimumTimeout);
+                        } finally {
+                            downEvent.recycle();
+                        }
+                        downEvent = MotionEvents.sendDown(
+                                uiController, coordinates, precision).down;
+                    }
+                    return downEvent;
+                }
+            };
+
+            @Override
+            public Status sendSwipe(
+                    UiController uiController,
+                    float[] startCoordinates, float[] endCoordinates, float[] precision) {
+                return sendLinearDrag(
+                        uiController, downMotion, startCoordinates, endCoordinates, precision);
+            }
+
+            @Override
+            public String toString() {
+                return "mouse triple click and drag to select";
+            }
+
+            @Override
+            public UiController wrapUiController(UiController uiController) {
+                return new MouseUiController(uiController);
+            }
+        },
+
+        /**
          * Starts a drag with a tap.
          */
         TAP {
diff --git a/core/tests/coretests/src/android/widget/espresso/DragHandleUtils.java b/core/tests/coretests/src/android/widget/espresso/DragHandleUtils.java
new file mode 100644
index 0000000..f744cae
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/espresso/DragHandleUtils.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 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.widget.espresso;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
+import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
+import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static org.hamcrest.Matchers.allOf;
+
+import android.support.test.espresso.NoMatchingRootException;
+import android.support.test.espresso.NoMatchingViewException;
+import android.support.test.espresso.ViewInteraction;
+import android.widget.Editor;
+
+public class DragHandleUtils {
+    private DragHandleUtils() {
+
+    }
+
+    public static void assertNoSelectionHandles() {
+        try {
+            onHandleView(com.android.internal.R.id.selection_start_handle)
+                    .check(matches(isDisplayed()));
+        } catch (NoMatchingRootException | NoMatchingViewException | AssertionError e) {
+            try {
+                onHandleView(com.android.internal.R.id.selection_end_handle)
+                        .check(matches(isDisplayed()));
+            } catch (NoMatchingRootException | NoMatchingViewException | AssertionError e1) {
+                return;
+            }
+        }
+        throw new AssertionError("Selection handle found");
+    }
+
+    public static ViewInteraction onHandleView(int id)
+            throws NoMatchingRootException, NoMatchingViewException, AssertionError {
+        return onView(allOf(withId(id), isAssignableFrom(Editor.HandleView.class)))
+                .inRoot(withDecorView(hasDescendant(withId(id))));
+    }
+}
diff --git a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
index fc01d84..f02fe00 100644
--- a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
+++ b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
@@ -19,31 +19,133 @@
 import static android.support.test.espresso.Espresso.onView;
 import static android.support.test.espresso.assertion.ViewAssertions.matches;
 import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
+import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
 import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.isRoot;
 import static android.support.test.espresso.matcher.ViewMatchers.withTagValue;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static org.hamcrest.Matchers.allOf;
 import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.not;
 
-import android.app.Activity;
+import org.hamcrest.Matcher;
+
+import android.support.test.espresso.NoMatchingRootException;
+import android.support.test.espresso.NoMatchingViewException;
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.ViewAction;
+import android.support.test.espresso.ViewInteraction;
+import android.support.test.espresso.action.ViewActions;
+import android.support.test.espresso.matcher.ViewMatchers;
+import android.view.View;
+
 import com.android.internal.widget.FloatingToolbar;
 
 /**
  * Espresso utility methods for the floating toolbar.
  */
 public class FloatingToolbarEspressoUtils {
-
+    private final static Object TAG = FloatingToolbar.FLOATING_TOOLBAR_TAG;
 
     private FloatingToolbarEspressoUtils() {}
 
+    private static ViewInteraction onFloatingToolBar() {
+        return onView(withTagValue(is(TAG)))
+                .inRoot(withDecorView(hasDescendant(withTagValue(is(TAG)))));
+    }
+
     /**
      * Asserts that the floating toolbar is displayed on screen.
      *
      * @throws AssertionError if the assertion fails
      */
-    public static void assertFloatingToolbarIsDisplayed(Activity activity) {
-        onView(withTagValue(is((Object) FloatingToolbar.FLOATING_TOOLBAR_TAG)))
-                .inRoot(withDecorView(not(is(activity.getWindow().getDecorView()))))
-                .check(matches(isDisplayed()));
+    public static void assertFloatingToolbarIsDisplayed() {
+        onFloatingToolBar().check(matches(isDisplayed()));
     }
 
+    /**
+     * Asserts that the floating toolbar is not displayed on screen.
+     *
+     * @throws AssertionError if the assertion fails
+     */
+    public static void assertFloatingToolbarIsNotDisplayed() {
+        try {
+            onFloatingToolBar().check(matches(isDisplayed()));
+        } catch (NoMatchingRootException | NoMatchingViewException | AssertionError e) {
+            return;
+        }
+        throw new AssertionError("Floating toolbar is displayed");
+    }
+
+    private static void toggleOverflow() {
+        final int id = com.android.internal.R.id.overflow;
+        onView(allOf(withId(id), isDisplayed()))
+                .inRoot(withDecorView(hasDescendant(withId(id))))
+                .perform(ViewActions.click());
+        onView(isRoot()).perform(SLEEP);
+    }
+
+    public static void sleepForFloatingToolbarPopup() {
+        onView(isRoot()).perform(SLEEP);
+    }
+
+    /**
+     * Asserts that the floating toolbar contains the specified item.
+     *
+     * @param itemLabel label of the item.
+     * @throws AssertionError if the assertion fails
+     */
+    public static void assertFloatingToolbarContainsItem(String itemLabel) {
+        try{
+            onFloatingToolBar().check(matches(hasDescendant(ViewMatchers.withText(itemLabel))));
+        } catch (AssertionError e) {
+            try{
+                toggleOverflow();
+            } catch (NoMatchingViewException | NoMatchingRootException e2) {
+                // No overflow items.
+                throw e;
+            }
+            try{
+                onFloatingToolBar().check(matches(hasDescendant(ViewMatchers.withText(itemLabel))));
+            } finally {
+                toggleOverflow();
+            }
+        }
+    }
+
+    /**
+     * Asserts that the floating toolbar doesn't contain the specified item.
+     *
+     * @param itemLabel label of the item.
+     * @throws AssertionError if the assertion fails
+     */
+    public static void assertFloatingToolbarDoesNotContainItem(String itemLabel) {
+        try{
+            assertFloatingToolbarContainsItem(itemLabel);
+        } catch (AssertionError e) {
+            return;
+        }
+        throw new AssertionError("Floating toolbar contains " + itemLabel);
+    }
+
+    /**
+     * ViewAction to sleep to wait floating toolbar's animation.
+     */
+    private static final ViewAction SLEEP = new ViewAction() {
+        private static final long SLEEP_DURATION = 400;
+
+        @Override
+        public Matcher<View> getConstraints() {
+            return isDisplayed();
+        }
+
+        @Override
+        public String getDescription() {
+            return "Sleep " + SLEEP_DURATION + " ms.";
+        }
+
+        @Override
+        public void perform(UiController uiController, View view) {
+            uiController.loopMainThreadForAtLeast(SLEEP_DURATION);
+        }
+    };
 }
diff --git a/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java b/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java
index de640ca..e51f2785 100644
--- a/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java
+++ b/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java
@@ -22,9 +22,13 @@
 import android.support.test.espresso.ViewAction;
 import android.support.test.espresso.action.CoordinatesProvider;
 import android.support.test.espresso.action.GeneralClickAction;
+import android.support.test.espresso.action.MotionEvents;
+import android.support.test.espresso.action.MotionEvents.DownResultHolder;
 import android.support.test.espresso.action.PrecisionDescriber;
+import android.support.test.espresso.action.Press;
 import android.support.test.espresso.action.Tapper;
 import android.view.View;
+import android.view.ViewConfiguration;
 
 /**
  * ViewAction for performing an click on View by a mouse.
@@ -32,10 +36,58 @@
 public final class MouseClickAction implements ViewAction {
     private final GeneralClickAction mGeneralClickAction;
 
-    public MouseClickAction(Tapper tapper, CoordinatesProvider coordinatesProvider,
-            PrecisionDescriber precisionDescriber) {
+    public enum CLICK implements Tapper {
+        TRIPLE {
+            @Override
+            public Tapper.Status sendTap(UiController uiController, float[] coordinates,
+                    float[] precision) {
+                Tapper.Status stat = sendSingleTap(uiController, coordinates, precision);
+                boolean warning = false;
+                if (stat == Tapper.Status.FAILURE) {
+                    return Tapper.Status.FAILURE;
+                } else if (stat == Tapper.Status.WARNING) {
+                    warning = true;
+                }
+
+                long doubleTapMinimumTimeout = ViewConfiguration.getDoubleTapMinTime();
+                for (int i = 0; i < 2; i++) {
+                    if (0 < doubleTapMinimumTimeout) {
+                        uiController.loopMainThreadForAtLeast(doubleTapMinimumTimeout);
+                    }
+                    stat = sendSingleTap(uiController, coordinates, precision);
+                    if (stat == Tapper.Status.FAILURE) {
+                        return Tapper.Status.FAILURE;
+                    } else if (stat == Tapper.Status.WARNING) {
+                        warning = true;
+                    }
+                }
+
+                if (warning) {
+                    return Tapper.Status.WARNING;
+                } else {
+                    return Tapper.Status.SUCCESS;
+                }
+            }
+        };
+
+        private static Tapper.Status sendSingleTap(UiController uiController,
+                float[] coordinates, float[] precision) {
+            DownResultHolder res = MotionEvents.sendDown(uiController, coordinates, precision);
+            try {
+                if (!MotionEvents.sendUp(uiController, res.down)) {
+                    MotionEvents.sendCancel(uiController, res.down);
+                    return Tapper.Status.FAILURE;
+                }
+            } finally {
+                res.down.recycle();
+            }
+            return res.longPress ? Tapper.Status.WARNING : Tapper.Status.SUCCESS;
+        }
+    };
+
+    public MouseClickAction(Tapper tapper, CoordinatesProvider coordinatesProvider) {
         mGeneralClickAction = new GeneralClickAction(tapper, coordinatesProvider,
-                precisionDescriber);
+                Press.PINPOINT);
     }
 
     @Override
@@ -51,5 +103,13 @@
     @Override
     public void perform(UiController uiController, View view) {
         mGeneralClickAction.perform(new MouseUiController(uiController), view);
+        long doubleTapTimeout = ViewConfiguration.getDoubleTapTimeout();
+        if (0 < doubleTapTimeout) {
+            // Wait to avoid false gesture detection. Without this wait, consecutive clicks can be
+            // detected as a triple click. e.g. 2 double clicks are detected as a triple click and
+            // a single click because espresso isn't aware of triple click detection logic, which
+            // is TextView specific gesture.
+            uiController.loopMainThreadForAtLeast(doubleTapTimeout);
+        }
     }
 }
diff --git a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
index 32cc6d6..54d5823 100644
--- a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
+++ b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
@@ -64,7 +64,7 @@
      */
     public static ViewAction mouseClickOnTextAtIndex(int index) {
         return actionWithAssertions(
-                new MouseClickAction(Tap.SINGLE, new TextCoordinates(index), Press.PINPOINT));
+                new MouseClickAction(Tap.SINGLE, new TextCoordinates(index)));
     }
 
     /**
@@ -94,7 +94,7 @@
      */
     public static ViewAction mouseDoubleClickOnTextAtIndex(int index) {
         return actionWithAssertions(
-                new MouseClickAction(Tap.DOUBLE, new TextCoordinates(index), Press.PINPOINT));
+                new MouseClickAction(Tap.DOUBLE, new TextCoordinates(index)));
     }
 
     /**
@@ -124,7 +124,22 @@
      */
     public static ViewAction mouseLongClickOnTextAtIndex(int index) {
         return actionWithAssertions(
-                new MouseClickAction(Tap.LONG, new TextCoordinates(index), Press.PINPOINT));
+                new MouseClickAction(Tap.LONG, new TextCoordinates(index)));
+    }
+
+    /**
+     * Returns an action that triple-clicks by mouse on text at an index on the TextView.<br>
+     * <br>
+     * View constraints:
+     * <ul>
+     * <li>must be a TextView displayed on screen
+     * <ul>
+     *
+     * @param index The index of the TextView's text to triple-click on.
+     */
+    public static ViewAction mouseTripleClickOnTextAtIndex(int index) {
+        return actionWithAssertions(
+                new MouseClickAction(MouseClickAction.CLICK.TRIPLE, new TextCoordinates(index)));
     }
 
     /**
@@ -237,6 +252,28 @@
                         TextView.class));
     }
 
+    /**
+    * Returns an action that triple click then drags by mouse on text from startIndex to endIndex
+    * on the TextView.<br>
+    * <br>
+    * View constraints:
+    * <ul>
+    * <li>must be a TextView displayed on screen
+    * <ul>
+    *
+    * @param startIndex The index of the TextView's text to start a drag from
+    * @param endIndex The index of the TextView's text to end the drag at
+    */
+   public static ViewAction mouseTripleClickAndDragOnText(int startIndex, int endIndex) {
+       return actionWithAssertions(
+               new DragAction(
+                       DragAction.Drag.MOUSE_TRIPLE_CLICK,
+                       new TextCoordinates(startIndex),
+                       new TextCoordinates(endIndex),
+                       Press.PINPOINT,
+                       TextView.class));
+   }
+
     public enum Handle {
         SELECTION_START,
         SELECTION_END,
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index b8b7e76..6309ed3 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -58,12 +58,12 @@
         }
     }
 
-    public boolean addFont(String path) {
-        return nAddFont(mNativePtr, path);
+    public boolean addFont(String path, int ttcIndex) {
+        return nAddFont(mNativePtr, path, ttcIndex);
     }
 
-    public boolean addFontWeightStyle(String path, int weight, boolean style) {
-        return nAddFontWeightStyle(mNativePtr, path, weight, style);
+    public boolean addFontWeightStyle(String path, int ttcIndex, int weight, boolean style) {
+        return nAddFontWeightStyle(mNativePtr, path, ttcIndex, weight, style);
     }
 
     public boolean addFontFromAsset(AssetManager mgr, String path) {
@@ -72,9 +72,9 @@
 
     private static native long nCreateFamily(String lang, int variant);
     private static native void nUnrefFamily(long nativePtr);
-    private static native boolean nAddFont(long nativeFamily, String path);
+    private static native boolean nAddFont(long nativeFamily, String path, int ttcIndex);
     private static native boolean nAddFontWeightStyle(long nativeFamily, String path,
-            int weight, boolean isItalic);
+            int ttcIndex, int weight, boolean isItalic);
     private static native boolean nAddFontFromAsset(long nativeFamily, AssetManager mgr,
             String path);
 }
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index 97081f9..2596f53 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -43,12 +43,14 @@
     }
 
     public static class Font {
-        Font(String fontName, int weight, boolean isItalic) {
+        Font(String fontName, int ttcIndex, int weight, boolean isItalic) {
             this.fontName = fontName;
+            this.ttcIndex = ttcIndex;
             this.weight = weight;
             this.isItalic = isItalic;
         }
         public String fontName;
+        public int ttcIndex;
         public int weight;
         public boolean isItalic;
     }
@@ -112,12 +114,14 @@
             if (parser.getEventType() != XmlPullParser.START_TAG) continue;
             String tag = parser.getName();
             if (tag.equals("font")) {
+                String ttcIndexStr = parser.getAttributeValue(null, "ttcIndex");
+                int ttcIndex = ttcIndexStr == null ? 0 : Integer.parseInt(ttcIndexStr);
                 String weightStr = parser.getAttributeValue(null, "weight");
                 int weight = weightStr == null ? 400 : Integer.parseInt(weightStr);
                 boolean isItalic = "italic".equals(parser.getAttributeValue(null, "style"));
                 String filename = parser.nextText();
                 String fullFilename = "/system/fonts/" + filename;
-                fonts.add(new Font(fullFilename, weight, isItalic));
+                fonts.add(new Font(fullFilename, ttcIndex, weight, isItalic));
             } else {
                 skip(parser);
             }
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index 7aa0aef..a226e85 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -361,6 +361,17 @@
     public static final int RAW_SENSOR = 0x20;
 
     /**
+     * <p>Private raw camera sensor image format, a single channel image with
+     * implementation depedent pixel layout.</p>
+     *
+     * <p>RAW_PRIVATE is a format for unprocessed raw image buffers coming from an
+     * image sensor. The actual structure of buffers of this format is
+     * implementation-dependent.</p>
+     *
+     */
+    public static final int RAW_PRIVATE = 0x24;
+
+    /**
      * <p>
      * Android 10-bit raw format
      * </p>
@@ -748,6 +759,7 @@
             case FLEX_RGB_888:
             case FLEX_RGBA_8888:
             case RAW_SENSOR:
+            case RAW_PRIVATE:
             case RAW10:
             case RAW12:
             case DEPTH16:
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 7eb5584..1294323 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -209,7 +209,7 @@
     public static Typeface createFromFile(String path) {
         if (sFallbackFonts != null) {
             FontFamily fontFamily = new FontFamily();
-            if (fontFamily.addFont(path)) {
+            if (fontFamily.addFont(path, 0 /* ttcIndex */)) {
                 FontFamily[] families = { fontFamily };
                 return createFromFamiliesWithDefault(families);
             }
@@ -262,7 +262,7 @@
     private static FontFamily makeFamilyFromParsed(FontListParser.Family family) {
         FontFamily fontFamily = new FontFamily(family.lang, family.variant);
         for (FontListParser.Font font : family.fonts) {
-            fontFamily.addFontWeightStyle(font.fontName, font.weight, font.isItalic);
+            fontFamily.addFontWeightStyle(font.fontName, font.ttcIndex, font.weight, font.isItalic);
         }
         return fontFamily;
     }
diff --git a/include/android_runtime/android_view_Surface.h b/include/android_runtime/android_view_Surface.h
index ed83314..b1e552a 100644
--- a/include/android_runtime/android_view_Surface.h
+++ b/include/android_runtime/android_view_Surface.h
@@ -45,7 +45,9 @@
     RAW_SENSOR        = 0x20,
     PRIVATE           = 0x22,
     YUV_420_888       = 0x23,
+    RAW_PRIVATE       = 0x24,
     RAW10             = 0x25,
+    RAW12             = 0x26,
     JPEG              = 0x100,
     DEPTH_POINT_CLOUD = 0x101,
     YV12              = 0x32315659,
diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp
index fde12dd..d4dbb00 100644
--- a/libs/hwui/BakedOpDispatcher.cpp
+++ b/libs/hwui/BakedOpDispatcher.cpp
@@ -20,6 +20,8 @@
 #include "Caches.h"
 #include "Glop.h"
 #include "GlopBuilder.h"
+#include "Patch.h"
+#include "PathTessellator.h"
 #include "renderstate/OffscreenBufferPool.h"
 #include "renderstate/RenderState.h"
 #include "utils/GLUtils.h"
@@ -27,6 +29,7 @@
 
 #include <algorithm>
 #include <math.h>
+#include <SkPaintDefaults.h>
 
 namespace android {
 namespace uirenderer {
@@ -54,8 +57,6 @@
     if (entry) {
         entry->uvMapper.map(texCoords);
     }
-    // init to non-empty, so we can safely expandtoCoverRect
-    Rect totalBounds = firstState.computedState.clippedBounds;
     for (size_t i = 0; i < opList.count; i++) {
         const BakedOpState& state = *(opList.states[i]);
         TextureVertex* rectVerts = &vertices[i * 4];
@@ -66,8 +67,6 @@
         }
         storeTexturedRect(rectVerts, opBounds, texCoords);
         renderer.dirtyRenderTarget(opBounds);
-
-        totalBounds.expandToCover(opBounds);
     }
 
     const int textureFillFlags = (bitmap->colorType() == kAlpha_8_SkColorType)
@@ -78,7 +77,111 @@
             .setMeshTexturedIndexedQuads(vertices, opList.count * 6)
             .setFillTexturePaint(*texture, textureFillFlags, firstState.op->paint, firstState.alpha)
             .setTransform(Matrix4::identity(), TransformFlags::None)
-            .setModelViewOffsetRect(0, 0, totalBounds) // don't snap here, we snap per-quad above
+            .setModelViewIdentityEmptyBounds()
+            .build();
+    renderer.renderGlop(nullptr, opList.clipSideFlags ? &opList.clip : nullptr, glop);
+}
+
+void BakedOpDispatcher::onMergedPatchOps(BakedOpRenderer& renderer,
+        const MergedBakedOpList& opList) {
+    const PatchOp& firstOp = *(static_cast<const PatchOp*>(opList.states[0]->op));
+    const BakedOpState& firstState = *(opList.states[0]);
+    AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(
+            firstOp.bitmap->pixelRef());
+
+    // Batches will usually contain a small number of items so it's
+    // worth performing a first iteration to count the exact number
+    // of vertices we need in the new mesh
+    uint32_t totalVertices = 0;
+
+    for (size_t i = 0; i < opList.count; i++) {
+        const PatchOp& op = *(static_cast<const PatchOp*>(opList.states[i]->op));
+
+        // TODO: cache mesh lookups
+        const Patch* opMesh = renderer.caches().patchCache.get(
+                entry, op.bitmap->width(), op.bitmap->height(),
+                op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch);
+        totalVertices += opMesh->verticesCount;
+    }
+
+    const bool dirtyRenderTarget = renderer.offscreenRenderTarget();
+
+    uint32_t indexCount = 0;
+
+    TextureVertex vertices[totalVertices];
+    TextureVertex* vertex = &vertices[0];
+    // Create a mesh that contains the transformed vertices for all the
+    // 9-patch objects that are part of the batch. Note that onDefer()
+    // enforces ops drawn by this function to have a pure translate or
+    // identity matrix
+    for (size_t i = 0; i < opList.count; i++) {
+        const PatchOp& op = *(static_cast<const PatchOp*>(opList.states[i]->op));
+        const BakedOpState& state = *opList.states[i];
+
+        // TODO: cache mesh lookups
+        const Patch* opMesh = renderer.caches().patchCache.get(
+                entry, op.bitmap->width(), op.bitmap->height(),
+                op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch);
+
+
+        uint32_t vertexCount = opMesh->verticesCount;
+        if (vertexCount == 0) continue;
+
+        // We use the bounds to know where to translate our vertices
+        // Using patchOp->state.mBounds wouldn't work because these
+        // bounds are clipped
+        const float tx = floorf(state.computedState.transform.getTranslateX()
+                + op.unmappedBounds.left + 0.5f);
+        const float ty = floorf(state.computedState.transform.getTranslateY()
+                + op.unmappedBounds.top + 0.5f);
+
+        // Copy & transform all the vertices for the current operation
+        TextureVertex* opVertices = opMesh->vertices.get();
+        for (uint32_t j = 0; j < vertexCount; j++, opVertices++) {
+            TextureVertex::set(vertex++,
+                    opVertices->x + tx, opVertices->y + ty,
+                    opVertices->u, opVertices->v);
+        }
+
+        // Dirty the current layer if possible. When the 9-patch does not
+        // contain empty quads we can take a shortcut and simply set the
+        // dirty rect to the object's bounds.
+        if (dirtyRenderTarget) {
+            if (!opMesh->hasEmptyQuads) {
+                renderer.dirtyRenderTarget(Rect(tx, ty,
+                        tx + op.unmappedBounds.getWidth(), ty + op.unmappedBounds.getHeight()));
+            } else {
+                const size_t count = opMesh->quads.size();
+                for (size_t i = 0; i < count; i++) {
+                    const Rect& quadBounds = opMesh->quads[i];
+                    const float x = tx + quadBounds.left;
+                    const float y = ty + quadBounds.top;
+                    renderer.dirtyRenderTarget(Rect(x, y,
+                            x + quadBounds.getWidth(), y + quadBounds.getHeight()));
+                }
+            }
+        }
+
+        indexCount += opMesh->indexCount;
+    }
+
+
+    Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(firstOp.bitmap);
+    if (!texture) return;
+    const AutoTexture autoCleanup(texture);
+
+    // 9 patches are built for stretching - always filter
+    int textureFillFlags = TextureFillFlags::ForceFilter;
+    if (firstOp.bitmap->colorType() == kAlpha_8_SkColorType) {
+        textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture;
+    }
+    Glop glop;
+    GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
+            .setRoundRectClipState(firstState.roundRectClipState)
+            .setMeshTexturedIndexedQuads(vertices, indexCount)
+            .setFillTexturePaint(*texture, textureFillFlags, firstOp.paint, firstState.alpha)
+            .setTransform(Matrix4::identity(), TransformFlags::None)
+            .setModelViewIdentityEmptyBounds()
             .build();
     renderer.renderGlop(nullptr, opList.clipSideFlags ? &opList.clip : nullptr, glop);
 }
@@ -206,6 +309,98 @@
     LOG_ALWAYS_FATAL("unsupported operation");
 }
 
+void BakedOpDispatcher::onCirclePropsOp(BakedOpRenderer&, const CirclePropsOp&, const BakedOpState&) {
+    LOG_ALWAYS_FATAL("unsupported operation");
+}
+
+void BakedOpDispatcher::onRoundRectPropsOp(BakedOpRenderer&, const RoundRectPropsOp&, const BakedOpState&) {
+    LOG_ALWAYS_FATAL("unsupported operation");
+}
+
+namespace VertexBufferRenderFlags {
+    enum {
+        Offset = 0x1,
+        ShadowInterp = 0x2,
+    };
+}
+
+static void renderVertexBuffer(BakedOpRenderer& renderer, const BakedOpState& state,
+        const VertexBuffer& vertexBuffer, float translateX, float translateY,
+        const SkPaint& paint, int vertexBufferRenderFlags) {
+    if (CC_LIKELY(vertexBuffer.getVertexCount())) {
+        bool shadowInterp = vertexBufferRenderFlags & VertexBufferRenderFlags::ShadowInterp;
+        const int transformFlags = TransformFlags::OffsetByFudgeFactor;
+        Glop glop;
+        GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
+                .setRoundRectClipState(state.roundRectClipState)
+                .setMeshVertexBuffer(vertexBuffer, shadowInterp)
+                .setFillPaint(paint, state.alpha)
+                .setTransform(state.computedState.transform, transformFlags)
+                .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds())
+                .build();
+        renderer.renderGlop(state, glop);
+    }
+}
+
+static void renderConvexPath(BakedOpRenderer& renderer, const BakedOpState& state,
+        const SkPath& path, const SkPaint& paint) {
+    VertexBuffer vertexBuffer;
+    // TODO: try clipping large paths to viewport
+    PathTessellator::tessellatePath(path, &paint, state.computedState.transform, vertexBuffer);
+    renderVertexBuffer(renderer, state, vertexBuffer, 0.0f, 0.0f, paint, 0);
+}
+
+static void renderPathTexture(BakedOpRenderer& renderer, const BakedOpState& state,
+        PathTexture& texture, const RecordedOp& op) {
+    Rect dest(texture.width, texture.height);
+    dest.translate(texture.left + op.unmappedBounds.left - texture.offset,
+            texture.top + op.unmappedBounds.top - texture.offset);
+    Glop glop;
+    GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
+            .setRoundRectClipState(state.roundRectClipState)
+            .setMeshTexturedUnitQuad(nullptr)
+            .setFillPathTexturePaint(texture, *(op.paint), state.alpha)
+            .setTransform(state.computedState.transform,  TransformFlags::None)
+            .setModelViewMapUnitToRect(dest)
+            .build();
+    renderer.renderGlop(state, glop);
+}
+
+SkRect getBoundsOfFill(const RecordedOp& op) {
+    SkRect bounds = op.unmappedBounds.toSkRect();
+    if (op.paint->getStyle() == SkPaint::kStrokeAndFill_Style) {
+        float outsetDistance = op.paint->getStrokeWidth() / 2;
+        bounds.outset(outsetDistance, outsetDistance);
+    }
+    return bounds;
+}
+
+void BakedOpDispatcher::onArcOp(BakedOpRenderer& renderer, const ArcOp& op, const BakedOpState& state) {
+    // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180)
+    if (op.paint->getStyle() != SkPaint::kStroke_Style
+            || op.paint->getPathEffect() != nullptr
+            || op.useCenter) {
+        PathTexture* texture = renderer.caches().pathCache.getArc(
+                op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(),
+                op.startAngle, op.sweepAngle, op.useCenter, op.paint);
+        const AutoTexture holder(texture);
+        if (CC_LIKELY(holder.texture)) {
+            renderPathTexture(renderer, state, *texture, op);
+        }
+    } else {
+        SkRect rect = getBoundsOfFill(op);
+        SkPath path;
+        if (op.useCenter) {
+            path.moveTo(rect.centerX(), rect.centerY());
+        }
+        path.arcTo(rect, op.startAngle, op.sweepAngle, !op.useCenter);
+        if (op.useCenter) {
+            path.close();
+        }
+        renderConvexPath(renderer, state, path, *(op.paint));
+    }
+}
+
 void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op, const BakedOpState& state) {
     Texture* texture = renderer.getTexture(op.bitmap);
     if (!texture) return;
@@ -224,44 +419,230 @@
     renderer.renderGlop(state, glop);
 }
 
-void BakedOpDispatcher::onLinesOp(BakedOpRenderer& renderer, const LinesOp& op, const BakedOpState& state) {
-    LOG_ALWAYS_FATAL("todo");
-}
+void BakedOpDispatcher::onBitmapMeshOp(BakedOpRenderer& renderer, const BitmapMeshOp& op, const BakedOpState& state) {
+    const static UvMapper defaultUvMapper;
+    const uint32_t elementCount = op.meshWidth * op.meshHeight * 6;
 
-void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, const BakedOpState& state) {
+    std::unique_ptr<ColorTextureVertex[]> mesh(new ColorTextureVertex[elementCount]);
+    ColorTextureVertex* vertex = &mesh[0];
+
+    const int* colors = op.colors;
+    std::unique_ptr<int[]> tempColors;
+    if (!colors) {
+        uint32_t colorsCount = (op.meshWidth + 1) * (op.meshHeight + 1);
+        tempColors.reset(new int[colorsCount]);
+        memset(tempColors.get(), 0xff, colorsCount * sizeof(int));
+        colors = tempColors.get();
+    }
+
+    Texture* texture = renderer.renderState().assetAtlas().getEntryTexture(op.bitmap->pixelRef());
+    const UvMapper& mapper(texture && texture->uvMapper ? *texture->uvMapper : defaultUvMapper);
+
+    for (int32_t y = 0; y < op.meshHeight; y++) {
+        for (int32_t x = 0; x < op.meshWidth; x++) {
+            uint32_t i = (y * (op.meshWidth + 1) + x) * 2;
+
+            float u1 = float(x) / op.meshWidth;
+            float u2 = float(x + 1) / op.meshWidth;
+            float v1 = float(y) / op.meshHeight;
+            float v2 = float(y + 1) / op.meshHeight;
+
+            mapper.map(u1, v1, u2, v2);
+
+            int ax = i + (op.meshWidth + 1) * 2;
+            int ay = ax + 1;
+            int bx = i;
+            int by = bx + 1;
+            int cx = i + 2;
+            int cy = cx + 1;
+            int dx = i + (op.meshWidth + 1) * 2 + 2;
+            int dy = dx + 1;
+
+            const float* vertices = op.vertices;
+            ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]);
+            ColorTextureVertex::set(vertex++, vertices[ax], vertices[ay], u1, v2, colors[ax / 2]);
+            ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]);
+
+            ColorTextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2, colors[dx / 2]);
+            ColorTextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1, colors[bx / 2]);
+            ColorTextureVertex::set(vertex++, vertices[cx], vertices[cy], u2, v1, colors[cx / 2]);
+        }
+    }
+
+    if (!texture) {
+        texture = renderer.caches().textureCache.get(op.bitmap);
+        if (!texture) {
+            return;
+        }
+    }
+    const AutoTexture autoCleanup(texture);
+
+    /*
+     * TODO: handle alpha_8 textures correctly by applying paint color, but *not*
+     * shader in that case to mimic the behavior in SkiaCanvas::drawBitmapMesh.
+     */
+    const int textureFillFlags = TextureFillFlags::None;
     Glop glop;
     GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
             .setRoundRectClipState(state.roundRectClipState)
-            .setMeshUnitQuad()
-            .setFillPaint(*op.paint, state.alpha)
-            .setTransform(state.computedState.transform, TransformFlags::None)
-            .setModelViewMapUnitToRect(op.unmappedBounds)
+            .setMeshColoredTexturedMesh(mesh.get(), elementCount)
+            .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
+            .setTransform(state.computedState.transform,  TransformFlags::None)
+            .setModelViewOffsetRect(0, 0, op.unmappedBounds)
             .build();
     renderer.renderGlop(state, glop);
 }
 
-namespace VertexBufferRenderFlags {
-    enum {
-        Offset = 0x1,
-        ShadowInterp = 0x2,
-    };
+void BakedOpDispatcher::onBitmapRectOp(BakedOpRenderer& renderer, const BitmapRectOp& op, const BakedOpState& state) {
+    Texture* texture = renderer.getTexture(op.bitmap);
+    if (!texture) return;
+    const AutoTexture autoCleanup(texture);
+
+    Rect uv(std::max(0.0f, op.src.left / texture->width),
+            std::max(0.0f, op.src.top / texture->height),
+            std::min(1.0f, op.src.right / texture->width),
+            std::min(1.0f, op.src.bottom / texture->height));
+
+    const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType)
+            ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None;
+    const bool tryToSnap = MathUtils::areEqual(op.src.getWidth(), op.unmappedBounds.getWidth())
+            && MathUtils::areEqual(op.src.getHeight(), op.unmappedBounds.getHeight());
+    Glop glop;
+    GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
+            .setRoundRectClipState(state.roundRectClipState)
+            .setMeshTexturedUvQuad(texture->uvMapper, uv)
+            .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
+            .setTransform(state.computedState.transform, TransformFlags::None)
+            .setModelViewMapUnitToRectOptionalSnap(tryToSnap, op.unmappedBounds)
+            .build();
+    renderer.renderGlop(state, glop);
 }
 
-static void renderVertexBuffer(BakedOpRenderer& renderer, const BakedOpState& state,
-        const VertexBuffer& vertexBuffer, float translateX, float translateY,
-        SkPaint& paint, int vertexBufferRenderFlags) {
-    if (CC_LIKELY(vertexBuffer.getVertexCount())) {
-        bool shadowInterp = vertexBufferRenderFlags & VertexBufferRenderFlags::ShadowInterp;
-        const int transformFlags = TransformFlags::OffsetByFudgeFactor;
-        Glop glop;
-        GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
-                .setRoundRectClipState(state.roundRectClipState)
-                .setMeshVertexBuffer(vertexBuffer, shadowInterp)
-                .setFillPaint(paint, state.alpha)
-                .setTransform(state.computedState.transform, transformFlags)
-                .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds())
-                .build();
-        renderer.renderGlop(state, glop);
+void BakedOpDispatcher::onLinesOp(BakedOpRenderer& renderer, const LinesOp& op, const BakedOpState& state) {
+    VertexBuffer buffer;
+    PathTessellator::tessellateLines(op.points, op.floatCount, op.paint,
+            state.computedState.transform, buffer);
+    int displayFlags = op.paint->isAntiAlias() ? 0 : VertexBufferRenderFlags::Offset;
+    renderVertexBuffer(renderer, state, buffer, 0, 0, *(op.paint), displayFlags);
+}
+
+void BakedOpDispatcher::onOvalOp(BakedOpRenderer& renderer, const OvalOp& op, const BakedOpState& state) {
+    if (op.paint->getPathEffect() != nullptr) {
+        PathTexture* texture = renderer.caches().pathCache.getOval(
+                op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint);
+        const AutoTexture holder(texture);
+        if (CC_LIKELY(holder.texture)) {
+            renderPathTexture(renderer, state, *texture, op);
+        }
+    } else {
+        SkPath path;
+        SkRect rect = getBoundsOfFill(op);
+        path.addOval(rect);
+        renderConvexPath(renderer, state, path, *(op.paint));
+    }
+}
+
+void BakedOpDispatcher::onPatchOp(BakedOpRenderer& renderer, const PatchOp& op, const BakedOpState& state) {
+    // 9 patches are built for stretching - always filter
+    int textureFillFlags = TextureFillFlags::ForceFilter;
+    if (op.bitmap->colorType() == kAlpha_8_SkColorType) {
+        textureFillFlags |= TextureFillFlags::IsAlphaMaskTexture;
+    }
+
+    // TODO: avoid redoing the below work each frame:
+    AssetAtlas::Entry* entry = renderer.renderState().assetAtlas().getEntry(op.bitmap->pixelRef());
+    const Patch* mesh = renderer.caches().patchCache.get(
+            entry, op.bitmap->width(), op.bitmap->height(),
+            op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.patch);
+
+    Texture* texture = entry ? entry->texture : renderer.caches().textureCache.get(op.bitmap);
+    if (!texture) return;
+    const AutoTexture autoCleanup(texture);
+    Glop glop;
+    GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
+            .setRoundRectClipState(state.roundRectClipState)
+            .setMeshPatchQuads(*mesh)
+            .setMeshTexturedUnitQuad(texture->uvMapper)
+            .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha)
+            .setTransform(state.computedState.transform, TransformFlags::None)
+            .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
+                    Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
+            .build();
+    renderer.renderGlop(state, glop);
+}
+
+void BakedOpDispatcher::onPathOp(BakedOpRenderer& renderer, const PathOp& op, const BakedOpState& state) {
+    PathTexture* texture = renderer.caches().pathCache.get(op.path, op.paint);
+    const AutoTexture holder(texture);
+    if (CC_LIKELY(holder.texture)) {
+        renderPathTexture(renderer, state, *texture, op);
+    }
+}
+
+void BakedOpDispatcher::onPointsOp(BakedOpRenderer& renderer, const PointsOp& op, const BakedOpState& state) {
+    VertexBuffer buffer;
+    PathTessellator::tessellatePoints(op.points, op.floatCount, op.paint,
+            state.computedState.transform, buffer);
+    int displayFlags = op.paint->isAntiAlias() ? 0 : VertexBufferRenderFlags::Offset;
+    renderVertexBuffer(renderer, state, buffer, 0, 0, *(op.paint), displayFlags);
+}
+
+// See SkPaintDefaults.h
+#define SkPaintDefaults_MiterLimit SkIntToScalar(4)
+
+void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, const BakedOpState& state) {
+    if (op.paint->getStyle() != SkPaint::kFill_Style) {
+        // only fill + default miter is supported by drawConvexPath, since others must handle joins
+        static_assert(SkPaintDefaults_MiterLimit == 4.0f, "Miter limit has changed");
+        if (CC_UNLIKELY(op.paint->getPathEffect() != nullptr
+                || op.paint->getStrokeJoin() != SkPaint::kMiter_Join
+                || op.paint->getStrokeMiter() != SkPaintDefaults_MiterLimit)) {
+             PathTexture* texture = renderer.caches().pathCache.getRect(
+                     op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.paint);
+             const AutoTexture holder(texture);
+             if (CC_LIKELY(holder.texture)) {
+                 renderPathTexture(renderer, state, *texture, op);
+             }
+        } else {
+            SkPath path;
+            path.addRect(getBoundsOfFill(op));
+            renderConvexPath(renderer, state, path, *(op.paint));
+        }
+    } else {
+        if (op.paint->isAntiAlias() && !state.computedState.transform.isSimple()) {
+            SkPath path;
+            path.addRect(op.unmappedBounds.toSkRect());
+            renderConvexPath(renderer, state, path, *(op.paint));
+        } else {
+            // render simple unit quad, no tessellation required
+            Glop glop;
+            GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
+                    .setRoundRectClipState(state.roundRectClipState)
+                    .setMeshUnitQuad()
+                    .setFillPaint(*op.paint, state.alpha)
+                    .setTransform(state.computedState.transform, TransformFlags::None)
+                    .setModelViewMapUnitToRect(op.unmappedBounds)
+                    .build();
+            renderer.renderGlop(state, glop);
+        }
+    }
+}
+
+void BakedOpDispatcher::onRoundRectOp(BakedOpRenderer& renderer, const RoundRectOp& op, const BakedOpState& state) {
+    if (op.paint->getPathEffect() != nullptr) {
+        PathTexture* texture = renderer.caches().pathCache.getRoundRect(
+                op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(),
+                op.rx, op.ry, op.paint);
+        const AutoTexture holder(texture);
+        if (CC_LIKELY(holder.texture)) {
+            renderPathTexture(renderer, state, *texture, op);
+        }
+    } else {
+        const VertexBuffer* buffer = renderer.caches().tessellationCache.getRoundRect(
+                state.computedState.transform, *(op.paint),
+                op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.rx, op.ry);
+        renderVertexBuffer(renderer, state, *buffer,
+                op.unmappedBounds.left, op.unmappedBounds.top, *(op.paint), 0);
     }
 }
 
@@ -323,8 +704,6 @@
 void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) {
     OffscreenBuffer* buffer = *op.layerHandle;
 
-    // TODO: extend this to handle HW layers & paint properties which
-    // reside in node.properties().layerProperties()
     float layerAlpha = op.alpha * state.alpha;
     Glop glop;
     GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
diff --git a/libs/hwui/BakedOpDispatcher.h b/libs/hwui/BakedOpDispatcher.h
index 0e763d9..ed34ada 100644
--- a/libs/hwui/BakedOpDispatcher.h
+++ b/libs/hwui/BakedOpDispatcher.h
@@ -26,6 +26,10 @@
 /**
  * Provides all "onBitmapOp(...)" style static methods for every op type, which convert the
  * RecordedOps and their state to Glops, and renders them with the provided BakedOpRenderer.
+ *
+ * onXXXOp methods must either render directly with the renderer, or call a static renderYYY
+ * method to render content. There should never be draw content rejection in BakedOpDispatcher -
+ * it must happen at a higher level (except in error-ish cases, like texture-too-big).
  */
 class BakedOpDispatcher {
 public:
diff --git a/libs/hwui/BakedOpState.h b/libs/hwui/BakedOpState.h
index 983c27b..9c836a0 100644
--- a/libs/hwui/BakedOpState.h
+++ b/libs/hwui/BakedOpState.h
@@ -53,7 +53,7 @@
 class ResolvedRenderState {
 public:
     // TODO: remove the mapRects/matrix multiply when snapshot & recorded transforms are translates
-    ResolvedRenderState(const Snapshot& snapshot, const RecordedOp& recordedOp) {
+    ResolvedRenderState(const Snapshot& snapshot, const RecordedOp& recordedOp, bool expandForStroke) {
         /* TODO: benchmark a fast path for translate-only matrices, such as:
         if (CC_LIKELY(snapshot.transform->getType() == Matrix4::kTypeTranslate
                 && recordedOp.localMatrix.getType() == Matrix4::kTypeTranslate)) {
@@ -83,7 +83,17 @@
 
         // resolvedClippedBounds = intersect(resolvedMatrix * opBounds, resolvedClipRect)
         clippedBounds = recordedOp.unmappedBounds;
+        if (CC_UNLIKELY(expandForStroke)) {
+            // account for non-hairline stroke
+            clippedBounds.outset(recordedOp.paint->getStrokeWidth() * 0.5f);
+        }
         transform.mapRect(clippedBounds);
+        if (CC_UNLIKELY(expandForStroke
+                && (!transform.isPureTranslate() || recordedOp.paint->getStrokeWidth() < 1.0f))) {
+            // account for hairline stroke when stroke may be < 1 scaled pixel
+            // Non translate || strokeWidth < 1 is conservative, but will cover all cases
+            clippedBounds.outset(0.5f);
+        }
 
         if (clipRect.left > clippedBounds.left) clipSideFlags |= OpClipSideFlags::Left;
         if (clipRect.top > clippedBounds.top) clipSideFlags |= OpClipSideFlags::Top;
@@ -129,13 +139,36 @@
 public:
     static BakedOpState* tryConstruct(LinearAllocator& allocator,
             const Snapshot& snapshot, const RecordedOp& recordedOp) {
-        BakedOpState* bakedOp = new (allocator) BakedOpState(snapshot, recordedOp);
-        if (bakedOp->computedState.clippedBounds.isEmpty()) {
+        BakedOpState* bakedState = new (allocator) BakedOpState(snapshot, recordedOp, false);
+        if (bakedState->computedState.clippedBounds.isEmpty()) {
             // bounds are empty, so op is rejected
-            allocator.rewindIfLastAlloc(bakedOp);
+            allocator.rewindIfLastAlloc(bakedState);
             return nullptr;
         }
-        return bakedOp;
+        return bakedState;
+    }
+
+    enum class StrokeBehavior {
+        // stroking is forced, regardless of style on paint
+        Forced,
+        // stroking is defined by style on paint
+        StyleDefined,
+    };
+
+    static BakedOpState* tryStrokeableOpConstruct(LinearAllocator& allocator,
+            const Snapshot& snapshot, const RecordedOp& recordedOp, StrokeBehavior strokeBehavior) {
+        bool expandForStroke = (strokeBehavior == StrokeBehavior::StyleDefined)
+                ? (recordedOp.paint && recordedOp.paint->getStyle() != SkPaint::kFill_Style)
+                : true;
+
+        BakedOpState* bakedState = new (allocator) BakedOpState(
+                snapshot, recordedOp, expandForStroke);
+        if (bakedState->computedState.clippedBounds.isEmpty()) {
+            // bounds are empty, so op is rejected
+            allocator.rewindIfLastAlloc(bakedState);
+            return nullptr;
+        }
+        return bakedState;
     }
 
     static BakedOpState* tryShadowOpConstruct(LinearAllocator& allocator,
@@ -160,8 +193,8 @@
     const RecordedOp* op;
 
 private:
-    BakedOpState(const Snapshot& snapshot, const RecordedOp& recordedOp)
-            : computedState(snapshot, recordedOp)
+    BakedOpState(const Snapshot& snapshot, const RecordedOp& recordedOp, bool expandForStroke)
+            : computedState(snapshot, recordedOp, expandForStroke)
             , alpha(snapshot.alpha)
             , roundRectClipState(snapshot.roundRectClipState)
             , projectionPathMask(snapshot.projectionPathMask)
diff --git a/libs/hwui/Canvas.h b/libs/hwui/Canvas.h
index b585a27..0643a54 100644
--- a/libs/hwui/Canvas.h
+++ b/libs/hwui/Canvas.h
@@ -113,10 +113,10 @@
 
     // Geometry
     virtual void drawPoint(float x, float y, const SkPaint& paint) = 0;
-    virtual void drawPoints(const float* points, int count, const SkPaint& paint) = 0;
+    virtual void drawPoints(const float* points, int floatCount, const SkPaint& paint) = 0;
     virtual void drawLine(float startX, float startY, float stopX, float stopY,
                 const SkPaint& paint) = 0;
-    virtual void drawLines(const float* points, int count, const SkPaint& paint) = 0;
+    virtual void drawLines(const float* points, int floatCount, const SkPaint& paint) = 0;
     virtual void drawRect(float left, float top, float right, float bottom,
             const SkPaint& paint) = 0;
     virtual void drawRegion(const SkRegion& region, const SkPaint& paint) = 0;
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 9c8649f..8acdb62 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -73,7 +73,7 @@
             .setMeshTexturedIndexedQuads(texture.mesh(), texture.meshElementCount())
             .setFillTexturePaint(texture.getTexture(), textureFillFlags, paint, bakedState->alpha)
             .setTransform(bakedState->computedState.transform, transformFlags)
-            .setModelViewOffsetRect(0, 0, Rect(0, 0, 0, 0))
+            .setModelViewIdentityEmptyBounds()
             .build();
     // Note: don't pass dirty bounds here, so user must manage passing dirty bounds to renderer
     renderer->renderGlop(nullptr, clip, glop);
diff --git a/libs/hwui/Glop.h b/libs/hwui/Glop.h
index 4785ea4..bcf819e 100644
--- a/libs/hwui/Glop.h
+++ b/libs/hwui/Glop.h
@@ -64,7 +64,7 @@
 
         // Canvas transform isn't applied to the mesh at draw time,
         //since it's already built in.
-        MeshIgnoresCanvasTransform = 1 << 1,
+        MeshIgnoresCanvasTransform = 1 << 1, // TODO: remove
     };
 };
 
diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h
index b647b90..6e5797d 100644
--- a/libs/hwui/GlopBuilder.h
+++ b/libs/hwui/GlopBuilder.h
@@ -95,6 +95,10 @@
             return setModelViewOffsetRect(offsetX, offsetY, source);
         }
     }
+    GlopBuilder& setModelViewIdentityEmptyBounds() {
+        // pass empty rect since not needed for damage / snap
+        return setModelViewOffsetRect(0, 0, Rect());
+    }
 
     GlopBuilder& setRoundRectClipState(const RoundRectClipState* roundRectClipState);
 
diff --git a/libs/hwui/OpReorderer.cpp b/libs/hwui/OpReorderer.cpp
index 9460361..ec03e83 100644
--- a/libs/hwui/OpReorderer.cpp
+++ b/libs/hwui/OpReorderer.cpp
@@ -467,13 +467,13 @@
             // (temp layers are clipped to viewport, since they don't persist offscreen content)
             SkPaint saveLayerPaint;
             saveLayerPaint.setAlpha(properties.getAlpha());
-            onBeginLayerOp(*new (mAllocator) BeginLayerOp(
+            deferBeginLayerOp(*new (mAllocator) BeginLayerOp(
                     saveLayerBounds,
                     Matrix4::identity(),
                     saveLayerBounds,
                     &saveLayerPaint));
             deferNodeOps(node);
-            onEndLayerOp(*new (mAllocator) EndLayerOp());
+            deferEndLayerOp(*new (mAllocator) EndLayerOp());
         } else {
             deferNodeOps(node);
         }
@@ -559,7 +559,7 @@
         }
 
         const RenderNodeOp* childOp = zTranslatedNodes[drawIndex].value;
-        deferRenderNodeOp(*childOp);
+        deferRenderNodeOpImpl(*childOp);
         drawIndex++;
     }
 }
@@ -645,7 +645,7 @@
 
         int restoreTo = mCanvasState.save(SkCanvas::kMatrix_SaveFlag);
         mCanvasState.concatMatrix(childOp->transformFromCompositingAncestor);
-        deferRenderNodeOp(*childOp);
+        deferRenderNodeOpImpl(*childOp);
         mCanvasState.restoreToCount(restoreTo);
     }
 
@@ -653,13 +653,13 @@
 }
 
 /**
- * Used to define a list of lambdas referencing private OpReorderer::onXXXXOp() methods.
+ * Used to define a list of lambdas referencing private OpReorderer::onXX::defer() methods.
  *
  * This allows opIds embedded in the RecordedOps to be used for dispatching to these lambdas.
  * E.g. a BitmapOp op then would be dispatched to OpReorderer::onBitmapOp(const BitmapOp&)
  */
 #define OP_RECEIVER(Type) \
-        [](OpReorderer& reorderer, const RecordedOp& op) { reorderer.on##Type(static_cast<const Type&>(op)); },
+        [](OpReorderer& reorderer, const RecordedOp& op) { reorderer.defer##Type(static_cast<const Type&>(op)); },
 void OpReorderer::deferNodeOps(const RenderNode& renderNode) {
     typedef void (*OpDispatcher) (OpReorderer& reorderer, const RecordedOp& op);
     static OpDispatcher receivers[] = {
@@ -687,7 +687,7 @@
     }
 }
 
-void OpReorderer::deferRenderNodeOp(const RenderNodeOp& op) {
+void OpReorderer::deferRenderNodeOpImpl(const RenderNodeOp& op) {
     if (op.renderNode->nothingToDraw()) return;
     int count = mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
 
@@ -702,19 +702,43 @@
     mCanvasState.restoreToCount(count);
 }
 
-void OpReorderer::onRenderNodeOp(const RenderNodeOp& op) {
+void OpReorderer::deferRenderNodeOp(const RenderNodeOp& op) {
     if (!op.skipInOrderDraw) {
-        deferRenderNodeOp(op);
+        deferRenderNodeOpImpl(op);
     }
 }
 
-static batchid_t tessellatedBatchId(const SkPaint& paint) {
+/**
+ * Defers an unmergeable, strokeable op, accounting correctly
+ * for paint's style on the bounds being computed.
+ */
+void OpReorderer::deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
+        BakedOpState::StrokeBehavior strokeBehavior) {
+    // Note: here we account for stroke when baking the op
+    BakedOpState* bakedState = BakedOpState::tryStrokeableOpConstruct(
+            mAllocator, *mCanvasState.currentSnapshot(), op, strokeBehavior);
+    if (!bakedState) return; // quick rejected
+    currentLayer().deferUnmergeableOp(mAllocator, bakedState, batchId);
+}
+
+/**
+ * Returns batch id for tessellatable shapes, based on paint. Checks to see if path effect/AA will
+ * be used, since they trigger significantly different rendering paths.
+ *
+ * Note: not used for lines/points, since they don't currently support path effects.
+ */
+static batchid_t tessBatchId(const RecordedOp& op) {
+    const SkPaint& paint = *(op.paint);
     return paint.getPathEffect()
             ? OpBatchType::AlphaMaskTexture
             : (paint.isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices);
 }
 
-void OpReorderer::onBitmapOp(const BitmapOp& op) {
+void OpReorderer::deferArcOp(const ArcOp& op) {
+    deferStrokeableOp(op, tessBatchId(op));
+}
+
+void OpReorderer::deferBitmapOp(const BitmapOp& op) {
     BakedOpState* bakedState = tryBakeOpState(op);
     if (!bakedState) return; // quick rejected
 
@@ -733,25 +757,94 @@
     }
 }
 
-void OpReorderer::onLinesOp(const LinesOp& op) {
+void OpReorderer::deferBitmapMeshOp(const BitmapMeshOp& op) {
     BakedOpState* bakedState = tryBakeOpState(op);
     if (!bakedState) return; // quick rejected
-    currentLayer().deferUnmergeableOp(mAllocator, bakedState, tessellatedBatchId(*op.paint));
+    currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
 }
 
-void OpReorderer::onRectOp(const RectOp& op) {
+void OpReorderer::deferBitmapRectOp(const BitmapRectOp& op) {
     BakedOpState* bakedState = tryBakeOpState(op);
     if (!bakedState) return; // quick rejected
-    currentLayer().deferUnmergeableOp(mAllocator, bakedState, tessellatedBatchId(*op.paint));
+    currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
 }
 
-void OpReorderer::onSimpleRectsOp(const SimpleRectsOp& op) {
+void OpReorderer::deferCirclePropsOp(const CirclePropsOp& op) {
+    // allocate a temporary oval op (with mAllocator, so it persists until render), so the
+    // renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple.
+    float x = *(op.x);
+    float y = *(op.y);
+    float radius = *(op.radius);
+    Rect unmappedBounds(x - radius, y - radius, x + radius, y + radius);
+    const OvalOp* resolvedOp = new (mAllocator) OvalOp(
+            unmappedBounds,
+            op.localMatrix,
+            op.localClipRect,
+            op.paint);
+    deferOvalOp(*resolvedOp);
+}
+
+void OpReorderer::deferLinesOp(const LinesOp& op) {
+    batchid_t batch = op.paint->isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices;
+    deferStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced);
+}
+
+void OpReorderer::deferOvalOp(const OvalOp& op) {
+    deferStrokeableOp(op, tessBatchId(op));
+}
+
+void OpReorderer::deferPatchOp(const PatchOp& op) {
+    BakedOpState* bakedState = tryBakeOpState(op);
+    if (!bakedState) return; // quick rejected
+
+    if (bakedState->computedState.transform.isPureTranslate()
+            && PaintUtils::getXfermodeDirect(op.paint) == SkXfermode::kSrcOver_Mode) {
+        mergeid_t mergeId = (mergeid_t) op.bitmap->getGenerationID();
+        // TODO: AssetAtlas in mergeId
+
+        // Only use the MergedPatch batchId when merged, so Bitmap+Patch don't try to merge together
+        currentLayer().deferMergeableOp(mAllocator, bakedState, OpBatchType::MergedPatch, mergeId);
+    } else {
+        // Use Bitmap batchId since Bitmap+Patch use same shader
+        currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
+    }
+}
+
+void OpReorderer::deferPathOp(const PathOp& op) {
+    deferStrokeableOp(op, OpBatchType::Bitmap);
+}
+
+void OpReorderer::deferPointsOp(const PointsOp& op) {
+    batchid_t batch = op.paint->isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices;
+    deferStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced);
+}
+
+void OpReorderer::deferRectOp(const RectOp& op) {
+    deferStrokeableOp(op, tessBatchId(op));
+}
+
+void OpReorderer::deferRoundRectOp(const RoundRectOp& op) {
+    deferStrokeableOp(op, tessBatchId(op));
+}
+
+void OpReorderer::deferRoundRectPropsOp(const RoundRectPropsOp& op) {
+    // allocate a temporary round rect op (with mAllocator, so it persists until render), so the
+    // renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple.
+    const RoundRectOp* resolvedOp = new (mAllocator) RoundRectOp(
+            Rect(*(op.left), *(op.top), *(op.right), *(op.bottom)),
+            op.localMatrix,
+            op.localClipRect,
+            op.paint, *op.rx, *op.ry);
+    deferRoundRectOp(*resolvedOp);
+}
+
+void OpReorderer::deferSimpleRectsOp(const SimpleRectsOp& op) {
     BakedOpState* bakedState = tryBakeOpState(op);
     if (!bakedState) return; // quick rejected
     currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Vertices);
 }
 
-void OpReorderer::onTextOp(const TextOp& op) {
+void OpReorderer::deferTextOp(const TextOp& op) {
     BakedOpState* bakedState = tryBakeOpState(op);
     if (!bakedState) return; // quick rejected
 
@@ -794,7 +887,7 @@
 }
 
 // TODO: test rejection at defer time, where the bounds become empty
-void OpReorderer::onBeginLayerOp(const BeginLayerOp& op) {
+void OpReorderer::deferBeginLayerOp(const BeginLayerOp& op) {
     uint32_t layerWidth = (uint32_t) op.unmappedBounds.getWidth();
     uint32_t layerHeight = (uint32_t) op.unmappedBounds.getHeight();
 
@@ -839,7 +932,7 @@
             &op, nullptr);
 }
 
-void OpReorderer::onEndLayerOp(const EndLayerOp& /* ignored */) {
+void OpReorderer::deferEndLayerOp(const EndLayerOp& /* ignored */) {
     const BeginLayerOp& beginLayerOp = *currentLayer().beginLayerOp;
     int finishedLayerIndex = mLayerStack.back();
 
@@ -865,11 +958,11 @@
     }
 }
 
-void OpReorderer::onLayerOp(const LayerOp& op) {
+void OpReorderer::deferLayerOp(const LayerOp& op) {
     LOG_ALWAYS_FATAL("unsupported");
 }
 
-void OpReorderer::onShadowOp(const ShadowOp& op) {
+void OpReorderer::deferShadowOp(const ShadowOp& op) {
     LOG_ALWAYS_FATAL("unsupported");
 }
 
diff --git a/libs/hwui/OpReorderer.h b/libs/hwui/OpReorderer.h
index fc77c61..35343c8b 100644
--- a/libs/hwui/OpReorderer.h
+++ b/libs/hwui/OpReorderer.h
@@ -45,7 +45,7 @@
     enum {
         None = 0, // Don't batch
         Bitmap,
-        Patch,
+        MergedPatch,
         AlphaVertices,
         Vertices,
         AlphaMaskTexture,
@@ -237,7 +237,7 @@
 
     void deferNodeOps(const RenderNode& renderNode);
 
-    void deferRenderNodeOp(const RenderNodeOp& op);
+    void deferRenderNodeOpImpl(const RenderNodeOp& op);
 
     void replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers);
 
@@ -245,14 +245,18 @@
         mFrameAllocatedPaths.emplace_back(new SkPath);
         return mFrameAllocatedPaths.back().get();
     }
+
+    void deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
+            BakedOpState::StrokeBehavior strokeBehavior = BakedOpState::StrokeBehavior::StyleDefined);
+
     /**
-     * Declares all OpReorderer::onXXXXOp() methods for every RecordedOp type.
+     * Declares all OpReorderer::deferXXXXOp() methods for every RecordedOp type.
      *
      * These private methods are called from within deferImpl to defer each individual op
      * type differently.
      */
 #define INTERNAL_OP_HANDLER(Type) \
-    void on##Type(const Type& op);
+    void defer##Type(const Type& op);
     MAP_OPS(INTERNAL_OP_HANDLER)
 
     std::vector<std::unique_ptr<SkPath> > mFrameAllocatedPaths;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 2cb32c4..f49237c 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -40,6 +40,7 @@
 
 #include <SkCanvas.h>
 #include <SkColor.h>
+#include <SkPaintDefaults.h>
 #include <SkPathOps.h>
 #include <SkShader.h>
 #include <SkTypeface.h>
@@ -1908,9 +1909,6 @@
     drawConvexPath(path, p);
 }
 
-// See SkPaintDefaults.h
-#define SkPaintDefaults_MiterLimit SkIntToScalar(4)
-
 void OpenGLRenderer::drawRect(float left, float top, float right, float bottom,
         const SkPaint* p) {
     if (mState.currentlyIgnored()
@@ -1921,6 +1919,7 @@
 
     if (p->getStyle() != SkPaint::kFill_Style) {
         // only fill style is supported by drawConvexPath, since others have to handle joins
+        static_assert(SkPaintDefaults_MiterLimit == 4.0f, "Miter limit has changed");
         if (p->getPathEffect() != nullptr || p->getStrokeJoin() != SkPaint::kMiter_Join ||
                 p->getStrokeMiter() != SkPaintDefaults_MiterLimit) {
             mCaches.textureState().activateTexture(0);
diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h
index b966401..d1a4866 100644
--- a/libs/hwui/RecordedOp.h
+++ b/libs/hwui/RecordedOp.h
@@ -24,7 +24,8 @@
 #include "utils/LinearAllocator.h"
 #include "Vector.h"
 
-#include "SkXfermode.h"
+#include <androidfw/ResourceTypes.h>
+#include <SkXfermode.h>
 
 class SkBitmap;
 class SkPaint;
@@ -43,10 +44,20 @@
  * This serves as the authoritative list of ops, used for generating ID enum, and ID based LUTs.
  */
 #define MAP_OPS_BASED_ON_MERGEABILITY(U_OP_FN, M_OP_FN) \
+        U_OP_FN(ArcOp) \
         M_OP_FN(BitmapOp) \
+        U_OP_FN(BitmapMeshOp) \
+        U_OP_FN(BitmapRectOp) \
+        U_OP_FN(CirclePropsOp) \
         U_OP_FN(LinesOp) \
+        U_OP_FN(OvalOp) \
+        M_OP_FN(PatchOp) \
+        U_OP_FN(PathOp) \
+        U_OP_FN(PointsOp) \
         U_OP_FN(RectOp) \
         U_OP_FN(RenderNodeOp) \
+        U_OP_FN(RoundRectOp) \
+        U_OP_FN(RoundRectPropsOp) \
         U_OP_FN(ShadowOp) \
         U_OP_FN(SimpleRectsOp) \
         M_OP_FN(TextOp) \
@@ -74,7 +85,7 @@
         Count,
     };
 }
-static_assert(RecordedOpId::BitmapOp == 0,
+static_assert(RecordedOpId::ArcOp == 0,
         "First index must be zero for LUTs to work");
 
 #define BASE_PARAMS const Rect& unmappedBounds, const Matrix4& localMatrix, const Rect& localClipRect, const SkPaint* paint
@@ -86,7 +97,7 @@
     /* ID from RecordedOpId - generally used for jumping into function tables */
     const int opId;
 
-    /* bounds in *local* space, without accounting for DisplayList transformation */
+    /* bounds in *local* space, without accounting for DisplayList transformation, or stroke */
     const Rect unmappedBounds;
 
     /* transform in recording space (vs DisplayList origin) */
@@ -128,6 +139,17 @@
 // Standard Ops
 ////////////////////////////////////////////////////////////////////////////////////////////////////
 
+struct ArcOp : RecordedOp {
+    ArcOp(BASE_PARAMS, float startAngle, float sweepAngle, bool useCenter)
+            : SUPER(ArcOp)
+            , startAngle(startAngle)
+            , sweepAngle(sweepAngle)
+            , useCenter(useCenter) {}
+    const float startAngle;
+    const float sweepAngle;
+    const bool useCenter;
+};
+
 struct BitmapOp : RecordedOp {
     BitmapOp(BASE_PARAMS, const SkBitmap* bitmap)
             : SUPER(BitmapOp)
@@ -136,6 +158,43 @@
     // TODO: asset atlas/texture id lookup?
 };
 
+struct BitmapMeshOp : RecordedOp {
+    BitmapMeshOp(BASE_PARAMS, const SkBitmap* bitmap, int meshWidth, int meshHeight,
+            const float* vertices, const int* colors)
+            : SUPER(BitmapMeshOp)
+            , bitmap(bitmap)
+            , meshWidth(meshWidth)
+            , meshHeight(meshHeight)
+            , vertices(vertices)
+            , colors(colors) {}
+    const SkBitmap* bitmap;
+    const int meshWidth;
+    const int meshHeight;
+    const float* vertices;
+    const int* colors;
+};
+
+struct BitmapRectOp : RecordedOp {
+    BitmapRectOp(BASE_PARAMS, const SkBitmap* bitmap, const Rect& src)
+            : SUPER(BitmapRectOp)
+            , bitmap(bitmap)
+            , src(src) {}
+    const SkBitmap* bitmap;
+    const Rect src;
+};
+
+struct CirclePropsOp : RecordedOp {
+    CirclePropsOp(const Matrix4& localMatrix, const Rect& localClipRect, const SkPaint* paint,
+            float* x, float* y, float* radius)
+            : RecordedOp(RecordedOpId::CirclePropsOp, Rect(), localMatrix, localClipRect, paint)
+            , x(x)
+            , y(y)
+            , radius(radius) {}
+    const float* x;
+    const float* y;
+    const float* radius;
+};
+
 struct LinesOp : RecordedOp {
     LinesOp(BASE_PARAMS, const float* points, const int floatCount)
             : SUPER(LinesOp)
@@ -145,11 +204,68 @@
     const int floatCount;
 };
 
+struct OvalOp : RecordedOp {
+    OvalOp(BASE_PARAMS)
+            : SUPER(OvalOp) {}
+};
+
+struct PatchOp : RecordedOp {
+    PatchOp(BASE_PARAMS, const SkBitmap* bitmap, const Res_png_9patch* patch)
+            : SUPER(PatchOp)
+            , bitmap(bitmap)
+            , patch(patch) {}
+    const SkBitmap* bitmap;
+    const Res_png_9patch* patch;
+};
+
+struct PathOp : RecordedOp {
+    PathOp(BASE_PARAMS, const SkPath* path)
+            : SUPER(PathOp)
+            , path(path) {}
+    const SkPath* path;
+};
+
+struct PointsOp : RecordedOp {
+    PointsOp(BASE_PARAMS, const float* points, const int floatCount)
+            : SUPER(PointsOp)
+            , points(points)
+            , floatCount(floatCount) {}
+    const float* points;
+    const int floatCount;
+};
+
 struct RectOp : RecordedOp {
     RectOp(BASE_PARAMS)
             : SUPER(RectOp) {}
 };
 
+struct RoundRectOp : RecordedOp {
+    RoundRectOp(BASE_PARAMS, float rx, float ry)
+            : SUPER(RoundRectOp)
+            , rx(rx)
+            , ry(ry) {}
+    const float rx;
+    const float ry;
+};
+
+struct RoundRectPropsOp : RecordedOp {
+    RoundRectPropsOp(const Matrix4& localMatrix, const Rect& localClipRect, const SkPaint* paint,
+            float* left, float* top, float* right, float* bottom, float *rx, float *ry)
+            : RecordedOp(RecordedOpId::RoundRectPropsOp, Rect(), localMatrix, localClipRect, paint)
+            , left(left)
+            , top(top)
+            , right(right)
+            , bottom(bottom)
+            , rx(rx)
+            , ry(ry) {}
+    const float* left;
+    const float* top;
+    const float* right;
+    const float* bottom;
+    const float* rx;
+    const float* ry;
+};
+
 /**
  * Real-time, dynamic-lit shadow.
  *
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index e6020cd..1bf92be 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -237,26 +237,32 @@
             refPaint(&paint)));
 }
 
+static Rect calcBoundsOfPoints(const float* points, int floatCount) {
+    Rect unmappedBounds(points[0], points[1], points[0], points[1]);
+    for (int i = 2; i < floatCount; i += 2) {
+        unmappedBounds.expandToCover(points[i], points[i + 1]);
+    }
+    return unmappedBounds;
+}
+
 // Geometry
-void RecordingCanvas::drawPoints(const float* points, int count, const SkPaint& paint) {
-    LOG_ALWAYS_FATAL("TODO!");
+void RecordingCanvas::drawPoints(const float* points, int floatCount, const SkPaint& paint) {
+    if (floatCount < 2) return;
+    floatCount &= ~0x1; // round down to nearest two
+
+    addOp(new (alloc()) PointsOp(
+            calcBoundsOfPoints(points, floatCount),
+            *mState.currentSnapshot()->transform,
+            mState.getRenderTargetClipBounds(),
+            refPaint(&paint), refBuffer<float>(points, floatCount), floatCount));
 }
 
 void RecordingCanvas::drawLines(const float* points, int floatCount, const SkPaint& paint) {
     if (floatCount < 4) return;
     floatCount &= ~0x3; // round down to nearest four
 
-    Rect unmappedBounds(points[0], points[1], points[0], points[1]);
-    for (int i = 2; i < floatCount; i += 2) {
-        unmappedBounds.expandToCover(points[i], points[i + 1]);
-    }
-
-    // since anything AA stroke with less than 1.0 pixel width is drawn with an alpha-reduced
-    // 1.0 stroke, treat 1.0 as minimum.
-    unmappedBounds.outset(std::max(paint.getStrokeWidth(), 1.0f) * 0.5f);
-
     addOp(new (alloc()) LinesOp(
-            unmappedBounds,
+            calcBoundsOfPoints(points, floatCount),
             *mState.currentSnapshot()->transform,
             mState.getRenderTargetClipBounds(),
             refPaint(&paint), refBuffer<float>(points, floatCount), floatCount));
@@ -330,20 +336,80 @@
 }
 void RecordingCanvas::drawRoundRect(float left, float top, float right, float bottom,
             float rx, float ry, const SkPaint& paint) {
-    LOG_ALWAYS_FATAL("TODO!");
+    addOp(new (alloc()) RoundRectOp(
+            Rect(left, top, right, bottom),
+            *(mState.currentSnapshot()->transform),
+            mState.getRenderTargetClipBounds(),
+            refPaint(&paint), rx, ry));
 }
+
+void RecordingCanvas::drawRoundRect(
+        CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top,
+        CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom,
+        CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry,
+        CanvasPropertyPaint* paint) {
+    mDisplayList->ref(left);
+    mDisplayList->ref(top);
+    mDisplayList->ref(right);
+    mDisplayList->ref(bottom);
+    mDisplayList->ref(rx);
+    mDisplayList->ref(ry);
+    mDisplayList->ref(paint);
+    refBitmapsInShader(paint->value.getShader());
+    addOp(new (alloc()) RoundRectPropsOp(
+            *(mState.currentSnapshot()->transform),
+            mState.getRenderTargetClipBounds(),
+            &paint->value,
+            &left->value, &top->value, &right->value, &bottom->value,
+            &rx->value, &ry->value));
+}
+
 void RecordingCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) {
-    LOG_ALWAYS_FATAL("TODO!");
+    // TODO: move to Canvas.h
+    if (radius <= 0) return;
+    drawOval(x - radius, y - radius, x + radius, y + radius, paint);
 }
+
+void RecordingCanvas::drawCircle(
+        CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y,
+        CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) {
+    mDisplayList->ref(x);
+    mDisplayList->ref(y);
+    mDisplayList->ref(radius);
+    mDisplayList->ref(paint);
+    refBitmapsInShader(paint->value.getShader());
+    addOp(new (alloc()) CirclePropsOp(
+            *(mState.currentSnapshot()->transform),
+            mState.getRenderTargetClipBounds(),
+            &paint->value,
+            &x->value, &y->value, &radius->value));
+}
+
+
 void RecordingCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) {
-    LOG_ALWAYS_FATAL("TODO!");
+    addOp(new (alloc()) OvalOp(
+            Rect(left, top, right, bottom),
+            *(mState.currentSnapshot()->transform),
+            mState.getRenderTargetClipBounds(),
+            refPaint(&paint)));
 }
+
 void RecordingCanvas::drawArc(float left, float top, float right, float bottom,
-            float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) {
-    LOG_ALWAYS_FATAL("TODO!");
+        float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) {
+    addOp(new (alloc()) ArcOp(
+            Rect(left, top, right, bottom),
+            *(mState.currentSnapshot()->transform),
+            mState.getRenderTargetClipBounds(),
+            refPaint(&paint),
+            startAngle, sweepAngle, useCenter));
 }
+
 void RecordingCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
-    LOG_ALWAYS_FATAL("TODO!");
+    addOp(new (alloc()) PathOp(
+            Rect(path.getBounds()),
+            *(mState.currentSnapshot()->transform),
+            mState.getRenderTargetClipBounds(),
+            refPaint(&paint), refPath(&path)));
 }
 
 // Bitmap-based
@@ -375,6 +441,7 @@
         restore();
     }
 }
+
 void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
             float srcRight, float srcBottom, float dstLeft, float dstTop,
             float dstRight, float dstBottom, const SkPaint* paint) {
@@ -389,17 +456,35 @@
         drawBitmap(&bitmap, paint);
         restore();
     } else {
-        LOG_ALWAYS_FATAL("TODO!");
+        addOp(new (alloc()) BitmapRectOp(
+                Rect(dstLeft, dstTop, dstRight, dstBottom),
+                *(mState.currentSnapshot()->transform),
+                mState.getRenderTargetClipBounds(),
+                refPaint(paint), refBitmap(bitmap),
+                Rect(srcLeft, srcTop, srcRight, srcBottom)));
     }
 }
+
 void RecordingCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
             const float* vertices, const int* colors, const SkPaint* paint) {
-    LOG_ALWAYS_FATAL("TODO!");
+    int vertexCount = (meshWidth + 1) * (meshHeight + 1);
+    addOp(new (alloc()) BitmapMeshOp(
+            calcBoundsOfPoints(vertices, vertexCount * 2),
+            *(mState.currentSnapshot()->transform),
+            mState.getRenderTargetClipBounds(),
+            refPaint(paint), refBitmap(bitmap), meshWidth, meshHeight,
+            refBuffer<float>(vertices, vertexCount * 2), // 2 floats per vertex
+            refBuffer<int>(colors, vertexCount))); // 1 color per vertex
 }
-void RecordingCanvas::drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& chunk,
+
+void RecordingCanvas::drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& patch,
             float dstLeft, float dstTop, float dstRight, float dstBottom,
             const SkPaint* paint) {
-    LOG_ALWAYS_FATAL("TODO!");
+    addOp(new (alloc()) PatchOp(
+            Rect(dstLeft, dstTop, dstRight, dstBottom),
+            *(mState.currentSnapshot()->transform),
+            mState.getRenderTargetClipBounds(),
+            refPaint(paint), refBitmap(bitmap), refPatch(&patch)));
 }
 
 // Text
@@ -421,7 +506,6 @@
 
 void RecordingCanvas::drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path,
             float hOffset, float vOffset, const SkPaint& paint) {
-    // NOTE: can't use refPaint() directly, since it forces left alignment
     LOG_ALWAYS_FATAL("TODO!");
 }
 
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 6d0e9e0..6fbaa8a 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -69,6 +69,17 @@
     virtual GLuint getTargetFbo() const override { return -1; }
 
 // ----------------------------------------------------------------------------
+// HWUI Canvas draw operations
+// ----------------------------------------------------------------------------
+
+    void drawRoundRect(CanvasPropertyPrimitive* left, CanvasPropertyPrimitive* top,
+            CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom,
+            CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry,
+            CanvasPropertyPaint* paint);
+    void drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y,
+            CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint);
+
+// ----------------------------------------------------------------------------
 // android/graphics/Canvas interface
 // ----------------------------------------------------------------------------
     virtual SkCanvas* asSkCanvas() override;
@@ -140,13 +151,13 @@
         float points[2] = { x, y };
         drawPoints(points, 2, paint);
     }
-    virtual void drawPoints(const float* points, int count, const SkPaint& paint) override;
+    virtual void drawPoints(const float* points, int floatCount, const SkPaint& paint) override;
     virtual void drawLine(float startX, float startY, float stopX, float stopY,
             const SkPaint& paint) override {
         float points[4] = { startX, startY, stopX, stopY };
         drawLines(points, 4, paint);
     }
-    virtual void drawLines(const float* points, int count, const SkPaint& paint) override;
+    virtual void drawLines(const float* points, int floatCount, const SkPaint& paint) override;
     virtual void drawRect(float left, float top, float right, float bottom, const SkPaint& paint) override;
     virtual void drawRegion(const SkRegion& region, const SkPaint& paint) override;
     virtual void drawRoundRect(float left, float top, float right, float bottom,
diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h
index 4bcd96d..1c544b9 100644
--- a/libs/hwui/Texture.h
+++ b/libs/hwui/Texture.h
@@ -120,16 +120,16 @@
 
 class AutoTexture {
 public:
-    AutoTexture(const Texture* texture): mTexture(texture) { }
+    AutoTexture(const Texture* texture)
+            : texture(texture) {}
     ~AutoTexture() {
-        if (mTexture && mTexture->cleanup) {
-            mTexture->deleteTexture();
-            delete mTexture;
+        if (texture && texture->cleanup) {
+            texture->deleteTexture();
+            delete texture;
         }
     }
 
-private:
-    const Texture* mTexture;
+    const Texture *const texture;
 }; // class AutoTexture
 
 }; // namespace uirenderer
diff --git a/libs/hwui/tests/common/scenes/OpPropAnimation.cpp b/libs/hwui/tests/common/scenes/OpPropAnimation.cpp
new file mode 100644
index 0000000..5dfb2b4
--- /dev/null
+++ b/libs/hwui/tests/common/scenes/OpPropAnimation.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2015 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 "TestSceneBase.h"
+#include "utils/Color.h"
+
+class OpPropAnimation;
+
+static TestScene::Registrar _Shapes(TestScene::Info{
+    "opprops",
+    "A minimal demonstration of CanvasProperty drawing operations.",
+    TestScene::simpleCreateScene<OpPropAnimation>
+});
+
+class OpPropAnimation : public TestScene {
+public:
+    sp<CanvasPropertyPaint> mPaint = new CanvasPropertyPaint(SkPaint());
+
+    sp<CanvasPropertyPrimitive> mRoundRectLeft = new CanvasPropertyPrimitive(0);
+    sp<CanvasPropertyPrimitive> mRoundRectTop = new CanvasPropertyPrimitive(0);
+    sp<CanvasPropertyPrimitive> mRoundRectRight = new CanvasPropertyPrimitive(0);
+    sp<CanvasPropertyPrimitive> mRoundRectBottom = new CanvasPropertyPrimitive(0);
+    sp<CanvasPropertyPrimitive> mRoundRectRx = new CanvasPropertyPrimitive(0);
+    sp<CanvasPropertyPrimitive> mRoundRectRy = new CanvasPropertyPrimitive(0);
+
+    sp<CanvasPropertyPrimitive> mCircleX = new CanvasPropertyPrimitive(0);
+    sp<CanvasPropertyPrimitive> mCircleY = new CanvasPropertyPrimitive(0);
+    sp<CanvasPropertyPrimitive> mCircleRadius = new CanvasPropertyPrimitive(0);
+
+    sp<RenderNode> content;
+    void createContent(int width, int height, TestCanvas& canvas) override {
+        content = TestUtils::createNode(0, 0, width, height,
+                [this, width, height](RenderProperties& props, TestCanvas& canvas) {
+            mPaint->value.setAntiAlias(true);
+            mPaint->value.setColor(Color::Blue_500);
+
+            mRoundRectRight->value = width / 2;
+            mRoundRectBottom->value = height / 2;
+
+            mCircleX->value = width * 0.75;
+            mCircleY->value = height * 0.75;
+
+            canvas.drawColor(Color::White, SkXfermode::Mode::kSrcOver_Mode);
+            canvas.drawRoundRect(mRoundRectLeft.get(), mRoundRectTop.get(),
+                    mRoundRectRight.get(), mRoundRectBottom.get(),
+                    mRoundRectRx.get(), mRoundRectRy.get(), mPaint.get());
+            canvas.drawCircle(mCircleX.get(), mCircleY.get(), mCircleRadius.get(), mPaint.get());
+        });
+        canvas.drawRenderNode(content.get());
+    }
+
+    void doFrame(int frameNr) override {
+        float value = (abs((frameNr % 200) - 100)) / 100.0f;
+        mRoundRectRx->value = dp(10) + value * dp(40);
+        mRoundRectRy->value = dp(10) + value * dp(80);
+        mCircleRadius->value = value * dp(200);
+        content->setPropertyFieldsDirty(RenderNode::GENERIC);
+    }
+};
diff --git a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
new file mode 100644
index 0000000..0cba344
--- /dev/null
+++ b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2015 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 "TestSceneBase.h"
+#include "utils/Color.h"
+
+#include <cstdio>
+
+class ShapeAnimation;
+
+static TestScene::Registrar _Shapes(TestScene::Info{
+    "shapes",
+    "A grid of shape drawing test cases.",
+    TestScene::simpleCreateScene<ShapeAnimation>
+});
+
+class ShapeAnimation : public TestScene {
+public:
+    sp<RenderNode> card;
+    void createContent(int width, int height, TestCanvas& canvas) override {
+        card = TestUtils::createNode(0, 0, width, height,
+                [width](RenderProperties& props, TestCanvas& canvas) {
+            std::function<void(TestCanvas&, float, const SkPaint&)> ops[] = {
+                [](TestCanvas& canvas, float size, const SkPaint& paint) {
+                    canvas.drawArc(0, 0, size, size, 50, 189, true, paint);
+                },
+                [](TestCanvas& canvas, float size, const SkPaint& paint) {
+                    canvas.drawOval(0, 0, size, size, paint);
+                },
+                [](TestCanvas& canvas, float size, const SkPaint& paint) {
+                    SkPath diamondPath;
+                    diamondPath.moveTo(size / 2, 0);
+                    diamondPath.lineTo(size, size / 2);
+                    diamondPath.lineTo(size / 2, size);
+                    diamondPath.lineTo(0, size / 2);
+                    diamondPath.close();
+                    canvas.drawPath(diamondPath, paint);
+                },
+                [](TestCanvas& canvas, float size, const SkPaint& paint) {
+                    float data[] = {0, 0, size, size, 0, size, size, 0 };
+                    canvas.drawLines(data, sizeof(data) / sizeof(float), paint);
+                },
+                [](TestCanvas& canvas, float size, const SkPaint& paint) {
+                    float data[] = {0, 0, size, size, 0, size, size, 0 };
+                    canvas.drawPoints(data, sizeof(data) / sizeof(float), paint);
+                },
+                [](TestCanvas& canvas, float size, const SkPaint& paint) {
+                    canvas.drawRect(0, 0, size, size, paint);
+                },
+                [](TestCanvas& canvas, float size, const SkPaint& paint) {
+                    float rad = size / 4;
+                    canvas.drawRoundRect(0, 0, size, size, rad, rad, paint);
+                }
+            };
+            float cellSpace = dp(4);
+            float cellSize = floorf(width / 7 - cellSpace);
+
+            // each combination of strokeWidth + style gets a column
+            int outerCount = canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+            SkPaint paint;
+            paint.setAntiAlias(true);
+            SkPaint::Style styles[] = {
+                    SkPaint::kStroke_Style, SkPaint::kFill_Style, SkPaint::kStrokeAndFill_Style };
+            for (auto style : styles) {
+                paint.setStyle(style);
+                for (auto strokeWidth : { 0.0f, 0.5f, 8.0f }) {
+                    paint.setStrokeWidth(strokeWidth);
+                    // fill column with each op
+                    int middleCount = canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+                    for (auto op : ops) {
+                        int innerCount = canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+                        canvas.clipRect(0, 0, cellSize, cellSize, SkRegion::kIntersect_Op);
+                        canvas.drawColor(Color::White, SkXfermode::Mode::kSrcOver_Mode);
+                        op(canvas, cellSize, paint);
+                        canvas.restoreToCount(innerCount);
+                        canvas.translate(cellSize + cellSpace, 0);
+                    }
+                    canvas.restoreToCount(middleCount);
+                    canvas.translate(0, cellSize + cellSpace);
+                }
+            }
+            canvas.restoreToCount(outerCount);
+        });
+        canvas.drawColor(Color::Grey_500, SkXfermode::Mode::kSrcOver_Mode);
+        canvas.drawRenderNode(card.get());
+    }
+
+    void doFrame(int frameNr) override {
+        card->mutateStagingProperties().setTranslationY(frameNr % 150);
+        card->setPropertyFieldsDirty(RenderNode::Y);
+    }
+};
diff --git a/libs/hwui/tests/unit/BakedOpStateTests.cpp b/libs/hwui/tests/unit/BakedOpStateTests.cpp
index de14abf..8321ff9 100644
--- a/libs/hwui/tests/unit/BakedOpStateTests.cpp
+++ b/libs/hwui/tests/unit/BakedOpStateTests.cpp
@@ -23,41 +23,130 @@
 namespace android {
 namespace uirenderer {
 
-TEST(ResolvedRenderState, resolution) {
-    Matrix4 identity;
-    identity.loadIdentity();
-
+TEST(ResolvedRenderState, construct) {
     Matrix4 translate10x20;
     translate10x20.loadTranslate(10, 20, 0);
 
     SkPaint paint;
-    RectOp recordedOp(Rect(30, 40, 100, 200), translate10x20, Rect(0, 0, 100, 200), &paint);
+    RectOp recordedOp(Rect(30, 40, 100, 200), translate10x20, Rect(100, 200), &paint);
     {
         // recorded with transform, no parent transform
-        auto parentSnapshot = TestUtils::makeSnapshot(identity, Rect(0, 0, 100, 200));
-        ResolvedRenderState state(*parentSnapshot, recordedOp);
+        auto parentSnapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
+        ResolvedRenderState state(*parentSnapshot, recordedOp, false);
         EXPECT_MATRIX_APPROX_EQ(state.transform, translate10x20);
-        EXPECT_EQ(state.clipRect, Rect(0, 0, 100, 200));
-        EXPECT_EQ(state.clippedBounds, Rect(40, 60, 100, 200)); // translated and also clipped
+        EXPECT_EQ(Rect(0, 0, 100, 200), state.clipRect);
+        EXPECT_EQ(Rect(40, 60, 100, 200), state.clippedBounds); // translated and also clipped
+        EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom, state.clipSideFlags);
     }
     {
         // recorded with transform and parent transform
-        auto parentSnapshot = TestUtils::makeSnapshot(translate10x20, Rect(0, 0, 100, 200));
-        ResolvedRenderState state(*parentSnapshot, recordedOp);
+        auto parentSnapshot = TestUtils::makeSnapshot(translate10x20, Rect(100, 200));
+        ResolvedRenderState state(*parentSnapshot, recordedOp, false);
 
         Matrix4 expectedTranslate;
         expectedTranslate.loadTranslate(20, 40, 0);
-        EXPECT_MATRIX_APPROX_EQ(state.transform, expectedTranslate);
+        EXPECT_MATRIX_APPROX_EQ(expectedTranslate, state.transform);
 
         // intersection of parent & transformed child clip
-        EXPECT_EQ(state.clipRect, Rect(10, 20, 100, 200));
+        EXPECT_EQ(Rect(10, 20, 100, 200), state.clipRect);
 
         // translated and also clipped
-        EXPECT_EQ(state.clippedBounds, Rect(50, 80, 100, 200));
+        EXPECT_EQ(Rect(50, 80, 100, 200), state.clippedBounds);
+        EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom, state.clipSideFlags);
     }
 }
 
-TEST(BakedOpState, constructAndReject) {
+const float HAIRLINE = 0.0f;
+
+// Note: bounds will be conservative, but not precise for non-hairline
+// - use approx bounds checks for these
+const float SEMI_HAIRLINE = 0.3f;
+
+struct StrokeTestCase {
+    float scale;
+    float strokeWidth;
+    const std::function<void(const ResolvedRenderState&)> validator;
+};
+
+const static StrokeTestCase sStrokeTestCases[] = {
+    {
+        1, HAIRLINE, [](const ResolvedRenderState& state) {
+            EXPECT_EQ(Rect(49.5f, 49.5f, 150.5f, 150.5f), state.clippedBounds);
+        }
+    },
+    {
+        1, SEMI_HAIRLINE, [](const ResolvedRenderState& state) {
+            EXPECT_TRUE(state.clippedBounds.contains(49.5f, 49.5f, 150.5f, 150.5f));
+            EXPECT_TRUE(Rect(49, 49, 151, 151).contains(state.clippedBounds));
+        }
+    },
+    {
+        1, 20, [](const ResolvedRenderState& state) {
+            EXPECT_EQ(Rect(40, 40, 160, 160), state.clippedBounds);
+        }
+    },
+
+    // 3x3 scale:
+    {
+        3, HAIRLINE, [](const ResolvedRenderState& state) {
+            EXPECT_EQ(Rect(149.5f, 149.5f, 200, 200), state.clippedBounds);
+            EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom, state.clipSideFlags);
+        }
+    },
+    {
+        3, SEMI_HAIRLINE, [](const ResolvedRenderState& state) {
+            EXPECT_TRUE(state.clippedBounds.contains(149.5f, 149.5f, 200, 200));
+            EXPECT_TRUE(Rect(149, 149, 200, 200).contains(state.clippedBounds));
+        }
+    },
+    {
+        3, 20, [](const ResolvedRenderState& state) {
+            EXPECT_TRUE(state.clippedBounds.contains(120, 120, 200, 200));
+            EXPECT_TRUE(Rect(119, 119, 200, 200).contains(state.clippedBounds));
+        }
+    },
+
+    // 0.5f x 0.5f scale
+    {
+        0.5f, HAIRLINE, [](const ResolvedRenderState& state) {
+            EXPECT_EQ(Rect(24.5f, 24.5f, 75.5f, 75.5f), state.clippedBounds);
+        }
+    },
+    {
+        0.5f, SEMI_HAIRLINE, [](const ResolvedRenderState& state) {
+            EXPECT_TRUE(state.clippedBounds.contains(24.5f, 24.5f, 75.5f, 75.5f));
+            EXPECT_TRUE(Rect(24, 24, 76, 76).contains(state.clippedBounds));
+        }
+    },
+    {
+        0.5f, 20, [](const ResolvedRenderState& state) {
+            EXPECT_TRUE(state.clippedBounds.contains(19.5f, 19.5f, 80.5f, 80.5f));
+            EXPECT_TRUE(Rect(19, 19, 81, 81).contains(state.clippedBounds));
+        }
+    }
+};
+
+TEST(ResolvedRenderState, construct_expandForStroke) {
+    // Loop over table of test cases and verify different combinations of stroke width and transform
+    for (auto&& testCase : sStrokeTestCases) {
+        SkPaint strokedPaint;
+        strokedPaint.setAntiAlias(true);
+        strokedPaint.setStyle(SkPaint::kStroke_Style);
+        strokedPaint.setStrokeWidth(testCase.strokeWidth);
+
+        RectOp recordedOp(Rect(50, 50, 150, 150),
+                Matrix4::identity(), Rect(200, 200), &strokedPaint);
+
+        Matrix4 snapshotMatrix;
+        snapshotMatrix.loadScale(testCase.scale, testCase.scale, 1);
+        auto parentSnapshot = TestUtils::makeSnapshot(snapshotMatrix, Rect(200, 200));
+
+        ResolvedRenderState state(*parentSnapshot, recordedOp, true);
+        testCase.validator(state);
+    }
+}
+
+TEST(BakedOpState, tryConstruct) {
     LinearAllocator allocator;
 
     Matrix4 translate100x0;
@@ -65,41 +154,85 @@
 
     SkPaint paint;
     {
-        RectOp rejectOp(Rect(30, 40, 100, 200), translate100x0, Rect(0, 0, 100, 200), &paint);
-        auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(0, 0, 100, 200));
-        BakedOpState* bakedOp = BakedOpState::tryConstruct(allocator, *snapshot, rejectOp);
+        RectOp rejectOp(Rect(30, 40, 100, 200), translate100x0, Rect(100, 200), &paint);
+        auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
+        BakedOpState* bakedState = BakedOpState::tryConstruct(allocator, *snapshot, rejectOp);
 
-        EXPECT_EQ(bakedOp, nullptr); // rejected by clip, so not constructed
-        EXPECT_LE(allocator.usedSize(), 8u); // no significant allocation space used for rejected op
+        EXPECT_EQ(nullptr, bakedState); // rejected by clip, so not constructed
+        EXPECT_GT(8u, allocator.usedSize()); // no significant allocation space used for rejected op
     }
     {
-        RectOp successOp(Rect(30, 40, 100, 200), Matrix4::identity(), Rect(0, 0, 100, 200), &paint);
-        auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(0, 0, 100, 200));
-        BakedOpState* bakedOp = BakedOpState::tryConstruct(allocator, *snapshot, successOp);
+        RectOp successOp(Rect(30, 40, 100, 200), Matrix4::identity(), Rect(100, 200), &paint);
+        auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
+        BakedOpState* bakedState = BakedOpState::tryConstruct(allocator, *snapshot, successOp);
 
-        EXPECT_NE(bakedOp, nullptr); // NOT rejected by clip, so will be constructed
-        EXPECT_GT(allocator.usedSize(), 64u); // relatively large alloc for non-rejected op
+        EXPECT_NE(nullptr, bakedState); // NOT rejected by clip, so will be constructed
+        EXPECT_LE(64u, allocator.usedSize()); // relatively large alloc for non-rejected op
     }
 }
 
-TEST(BakedOpState, oplessConstructAndReject) {
+TEST(BakedOpState, tryShadowOpConstruct) {
     LinearAllocator allocator;
     {
-        auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(0, 0, 0, 0)); // empty
-        BakedOpState* bakedOp = BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234);
+        auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect()); // Note: empty clip
+        BakedOpState* bakedState = BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234);
 
-        EXPECT_EQ(bakedOp, nullptr); // rejected by clip, so not constructed
-        EXPECT_LE(allocator.usedSize(), 8u); // no significant allocation space used for rejected op
+        EXPECT_EQ(nullptr, bakedState); // rejected by clip, so not constructed
+        EXPECT_GT(8u, allocator.usedSize()); // no significant allocation space used for rejected op
     }
     {
-        auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(0, 0, 100, 200));
-        BakedOpState* bakedOp = BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234);
+        auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
+        BakedOpState* bakedState = BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234);
 
-        EXPECT_NE(bakedOp, nullptr); // NOT rejected by clip, so will be constructed
-        EXPECT_GT(allocator.usedSize(), 64u); // relatively large alloc for non-rejected op
-        EXPECT_EQ((ShadowOp*)0x1234, bakedOp->op);
+        ASSERT_NE(nullptr, bakedState); // NOT rejected by clip, so will be constructed
+        EXPECT_LE(64u, allocator.usedSize()); // relatively large alloc for non-rejected op
     }
 }
 
+TEST(BakedOpState, tryStrokeableOpConstruct) {
+    LinearAllocator allocator;
+    {
+        // check regular rejection
+        SkPaint paint;
+        paint.setStyle(SkPaint::kStrokeAndFill_Style);
+        paint.setStrokeWidth(0.0f);
+        RectOp rejectOp(Rect(0, 0, 100, 200), Matrix4::identity(), Rect(100, 200), &paint);
+        auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect()); // Note: empty clip
+        auto bakedState = BakedOpState::tryStrokeableOpConstruct(allocator, *snapshot, rejectOp,
+                BakedOpState::StrokeBehavior::StyleDefined);
+
+        EXPECT_EQ(nullptr, bakedState);
+        EXPECT_GT(8u, allocator.usedSize()); // no significant allocation space used for rejected op
+    }
+    {
+        // check simple unscaled expansion
+        SkPaint paint;
+        paint.setStyle(SkPaint::kStrokeAndFill_Style);
+        paint.setStrokeWidth(10.0f);
+        RectOp rejectOp(Rect(50, 50, 150, 150), Matrix4::identity(), Rect(200, 200), &paint);
+        auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(200, 200));
+        auto bakedState = BakedOpState::tryStrokeableOpConstruct(allocator, *snapshot, rejectOp,
+                BakedOpState::StrokeBehavior::StyleDefined);
+
+        ASSERT_NE(nullptr, bakedState);
+        EXPECT_EQ(Rect(45, 45, 155, 155), bakedState->computedState.clippedBounds);
+        EXPECT_EQ(0, bakedState->computedState.clipSideFlags);
+    }
+    {
+        // check simple unscaled expansion, and fill style with stroke forced
+        SkPaint paint;
+        paint.setStyle(SkPaint::kFill_Style);
+        paint.setStrokeWidth(10.0f);
+        RectOp rejectOp(Rect(50, 50, 150, 150), Matrix4::identity(), Rect(200, 200), &paint);
+        auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(200, 200));
+        auto bakedState = BakedOpState::tryStrokeableOpConstruct(allocator, *snapshot, rejectOp,
+                BakedOpState::StrokeBehavior::Forced);
+
+        ASSERT_NE(nullptr, bakedState);
+        EXPECT_EQ(Rect(45, 45, 155, 155), bakedState->computedState.clippedBounds);
+        EXPECT_EQ(0, bakedState->computedState.clipSideFlags);
+    }
 }
-}
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/tests/unit/ClipAreaTests.cpp b/libs/hwui/tests/unit/ClipAreaTests.cpp
index d6192df..c4d305e 100644
--- a/libs/hwui/tests/unit/ClipAreaTests.cpp
+++ b/libs/hwui/tests/unit/ClipAreaTests.cpp
@@ -92,12 +92,10 @@
 
 TEST(ClipArea, paths) {
     ClipArea area(createClipArea());
-    Matrix4 transform;
-    transform.loadIdentity();
     SkPath path;
     SkScalar r = 100;
     path.addCircle(r, r, r);
-    area.clipPathWithTransform(path, &transform, SkRegion::kIntersect_Op);
+    area.clipPathWithTransform(path, &Matrix4::identity(), SkRegion::kIntersect_Op);
     EXPECT_FALSE(area.isEmpty());
     EXPECT_FALSE(area.isSimple());
     EXPECT_FALSE(area.isRectangleList());
@@ -116,11 +114,10 @@
     ClipArea area(createClipArea());
     area.setClip(0, 0, 100, 100);
 
-    Matrix4 transform;
-    transform.loadIdentity();
     Rect expected(-50, -50, 50, 50);
-    area.clipRectWithTransform(expected, &transform, SkRegion::kReplace_Op);
+    area.clipRectWithTransform(expected, &Matrix4::identity(), SkRegion::kReplace_Op);
     EXPECT_EQ(expected, area.getClipRect());
 }
+
 }
 }
diff --git a/libs/hwui/tests/unit/DamageAccumulatorTests.cpp b/libs/hwui/tests/unit/DamageAccumulatorTests.cpp
index 29354a7..7700138 100644
--- a/libs/hwui/tests/unit/DamageAccumulatorTests.cpp
+++ b/libs/hwui/tests/unit/DamageAccumulatorTests.cpp
@@ -31,13 +31,11 @@
 // as the output.
 TEST(DamageAccumulator, identity) {
     DamageAccumulator da;
-    Matrix4 identity;
     SkRect curDirty;
-    identity.loadIdentity();
-    da.pushTransform(&identity);
+    da.pushTransform(&Matrix4::identity());
     da.dirty(50, 50, 100, 100);
     {
-        da.pushTransform(&identity);
+        da.pushTransform(&Matrix4::identity());
         da.peekAtDirty(&curDirty);
         ASSERT_EQ(SkRect(), curDirty);
         da.popTransform();
@@ -68,15 +66,13 @@
 // Test that dirty rectangles are being unioned across "siblings
 TEST(DamageAccumulator, union) {
     DamageAccumulator da;
-    Matrix4 identity;
     SkRect curDirty;
-    identity.loadIdentity();
-    da.pushTransform(&identity);
+    da.pushTransform(&Matrix4::identity());
     {
-        da.pushTransform(&identity);
+        da.pushTransform(&Matrix4::identity());
         da.dirty(50, 50, 100, 100);
         da.popTransform();
-        da.pushTransform(&identity);
+        da.pushTransform(&Matrix4::identity());
         da.dirty(150, 50, 200, 125);
         da.popTransform();
     }
diff --git a/libs/hwui/tests/unit/OpReordererTests.cpp b/libs/hwui/tests/unit/OpReordererTests.cpp
index 068e832..5eac498 100644
--- a/libs/hwui/tests/unit/OpReordererTests.cpp
+++ b/libs/hwui/tests/unit/OpReordererTests.cpp
@@ -144,6 +144,32 @@
     EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end
 }
 
+TEST(OpReorderer, simpleStroke) {
+    class SimpleStrokeTestRenderer : public TestRendererBase {
+    public:
+        void onPointsOp(const PointsOp& op, const BakedOpState& state) override {
+            EXPECT_EQ(0, mIndex++);
+            // even though initial bounds are empty...
+            EXPECT_TRUE(op.unmappedBounds.isEmpty())
+                    << "initial bounds should be empty, since they're unstroked";
+            EXPECT_EQ(Rect(45, 45, 55, 55), state.computedState.clippedBounds)
+                    << "final bounds should account for stroke";
+        }
+    };
+
+    auto node = TestUtils::createNode(0, 0, 100, 200,
+            [](RenderProperties& props, RecordingCanvas& canvas) {
+        SkPaint strokedPaint;
+        strokedPaint.setStrokeWidth(10);
+        canvas.drawPoint(50, 50, strokedPaint);
+    });
+    OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
+            createSyncedNodeList(node), sLightCenter);
+    SimpleStrokeTestRenderer renderer;
+    reorderer.replayBakedOps<TestDispatcher>(renderer);
+    EXPECT_EQ(1, renderer.getIndex());
+}
+
 TEST(OpReorderer, simpleRejection) {
     auto node = TestUtils::createNode(0, 0, 200, 200,
             [](RenderProperties& props, RecordingCanvas& canvas) {
diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
index 2449ce8..ba9d185 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -44,7 +44,7 @@
 TEST(RecordingCanvas, drawLines) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
         SkPaint paint;
-        paint.setStrokeWidth(20);
+        paint.setStrokeWidth(20); // doesn't affect recorded bounds - would be resolved at bake time
         float points[] = { 0, 0, 20, 10, 30, 40, 90 }; // NB: only 1 valid line
         canvas.drawLines(&points[0], 7, paint);
     });
@@ -54,8 +54,8 @@
     ASSERT_EQ(RecordedOpId::LinesOp, op->opId);
     EXPECT_EQ(4, ((LinesOp*)op)->floatCount)
             << "float count must be rounded down to closest multiple of 4";
-    EXPECT_EQ(Rect(-10, -10, 30, 20), op->unmappedBounds)
-            << "unmapped bounds must be size of line, outset by 1/2 stroke width";
+    EXPECT_EQ(Rect(0, 0, 20, 10), op->unmappedBounds)
+            << "unmapped bounds must be size of line, and not outset for stroke width";
 }
 
 TEST(RecordingCanvas, drawRect) {
diff --git a/libs/hwui/utils/RingBuffer.h b/libs/hwui/utils/RingBuffer.h
index 6895f07..06bcdcd 100644
--- a/libs/hwui/utils/RingBuffer.h
+++ b/libs/hwui/utils/RingBuffer.h
@@ -32,7 +32,7 @@
     ~RingBuffer() {}
 
     constexpr size_t capacity() const { return SIZE; }
-    size_t size() { return mCount; }
+    size_t size() const { return mCount; }
 
     T& next() {
         mHead = (mHead + 1) % SIZE;
@@ -54,6 +54,10 @@
         return mBuffer[(mHead + index + 1) % mCount];
     }
 
+    const T& operator[](size_t index) const {
+        return mBuffer[(mHead + index + 1) % mCount];
+    }
+
     void clear() {
         mCount = 0;
         mHead = -1;
diff --git a/libs/hwui/utils/TestWindowContext.cpp b/libs/hwui/utils/TestWindowContext.cpp
index 84aae75..05b4a72 100644
--- a/libs/hwui/utils/TestWindowContext.cpp
+++ b/libs/hwui/utils/TestWindowContext.cpp
@@ -180,6 +180,10 @@
 TestWindowContext::TestWindowContext() :
     mData (nullptr) { }
 
+TestWindowContext::~TestWindowContext() {
+    delete mData;
+}
+
 void TestWindowContext::initialize(int width, int height)  {
     mData = new TestWindowData(SkISize::Make(width, height));
 }
diff --git a/libs/hwui/utils/TestWindowContext.h b/libs/hwui/utils/TestWindowContext.h
index 445a11b..48ec952 100644
--- a/libs/hwui/utils/TestWindowContext.h
+++ b/libs/hwui/utils/TestWindowContext.h
@@ -35,6 +35,7 @@
 public:
 
     TestWindowContext();
+    ~TestWindowContext();
 
     /// We need to know the size of the window.
     void initialize(int width, int height);
diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java
index 045216b..3e75759 100644
--- a/media/java/android/media/Image.java
+++ b/media/java/android/media/Image.java
@@ -141,6 +141,16 @@
      *     {@link android.hardware.camera2.CameraDevice CameraDevice}.
      *   </td>
      * </tr>
+     * <tr>
+     *   <td>{@link android.graphics.ImageFormat#RAW_PRIVATE RAW_PRIVATE}</td>
+     *   <td>1</td>
+     *   <td>A single plane of raw sensor image data of private layout.
+     *   The details of the layout is implementation specific. Row stride and
+     *   pixel stride are undefined for this format. Calling {@link Plane#getRowStride()}
+     *   or {@link Plane#getPixelStride()} on RAW_PRIVATE image will cause
+     *   UnSupportedOperationException being thrown.
+     *   </td>
+     * </tr>
      * </table>
      *
      * @see android.graphics.ImageFormat
@@ -341,7 +351,13 @@
          * <p>The row stride for this color plane, in bytes.</p>
          *
          * <p>This is the distance between the start of two consecutive rows of
-         * pixels in the image. The row stride is always greater than 0.</p>
+         * pixels in the image. Note that row stried is undefined for some formats
+         * such as
+         * {@link android.graphics.ImageFormat#RAW_PRIVATE RAW_PRIVATE},
+         * and calling getRowStride on images of these formats will
+         * cause an UnsupportedOperationException being thrown.
+         * For formats where row stride is well defined, the row stride
+         * is always greater than 0.</p>
          */
         public abstract int getRowStride();
         /**
@@ -350,7 +366,12 @@
          * <p>This is the distance between two consecutive pixel values in a row
          * of pixels. It may be larger than the size of a single pixel to
          * account for interleaved image data or padded formats.
-         * The pixel stride is always greater than 0.</p>
+         * Note that pixel stride is undefined for some formats such as
+         * {@link android.graphics.ImageFormat#RAW_PRIVATE RAW_PRIVATE},
+         * and calling getPixelStride on images of these formats will
+         * cause an UnsupportedOperationException being thrown.
+         * For formats where pixel stride is well defined, the pixel stride
+         * is always greater than 0.</p>
          */
         public abstract int getPixelStride();
         /**
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index 66a174c..397ab15 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -570,7 +570,7 @@
 
         nativeDetachImage(image);
         si.setDetached(true);
-   }
+    }
 
     private boolean isImageOwnedbyMe(Image image) {
         if (!(image instanceof SurfaceImage)) {
@@ -675,6 +675,7 @@
             switch(getFormat()) {
                 case ImageFormat.JPEG:
                 case ImageFormat.DEPTH_POINT_CLOUD:
+                case ImageFormat.RAW_PRIVATE:
                     width = ImageReader.this.getWidth();
                     break;
                 default:
@@ -690,6 +691,7 @@
             switch(getFormat()) {
                 case ImageFormat.JPEG:
                 case ImageFormat.DEPTH_POINT_CLOUD:
+                case ImageFormat.RAW_PRIVATE:
                     height = ImageReader.this.getHeight();
                     break;
                 default:
@@ -791,12 +793,20 @@
             @Override
             public int getPixelStride() {
                 SurfaceImage.this.throwISEIfImageIsInvalid();
+                if (ImageReader.this.mFormat == ImageFormat.RAW_PRIVATE) {
+                    throw new UnsupportedOperationException(
+                            "getPixelStride is not supported for RAW_PRIVATE plane");
+                }
                 return mPixelStride;
             }
 
             @Override
             public int getRowStride() {
                 SurfaceImage.this.throwISEIfImageIsInvalid();
+                if (ImageReader.this.mFormat == ImageFormat.RAW_PRIVATE) {
+                    throw new UnsupportedOperationException(
+                            "getRowStride is not supported for RAW_PRIVATE plane");
+                }
                 return mRowStride;
             }
 
diff --git a/media/java/android/media/ImageUtils.java b/media/java/android/media/ImageUtils.java
index eefd69c..abf6b20 100644
--- a/media/java/android/media/ImageUtils.java
+++ b/media/java/android/media/ImageUtils.java
@@ -57,6 +57,7 @@
             case ImageFormat.Y8:
             case ImageFormat.Y16:
             case ImageFormat.RAW_SENSOR:
+            case ImageFormat.RAW_PRIVATE:
             case ImageFormat.RAW10:
             case ImageFormat.RAW12:
             case ImageFormat.DEPTH16:
@@ -98,6 +99,10 @@
                 dst.getFormat() == ImageFormat.PRIVATE) {
             throw new IllegalArgumentException("PRIVATE format images are not copyable");
         }
+        if (src.getFormat() == ImageFormat.RAW_PRIVATE) {
+            throw new IllegalArgumentException(
+                    "Copy of RAW_OPAQUE format has not been implemented");
+        }
         if (!(dst.getOwner() instanceof ImageWriter)) {
             throw new IllegalArgumentException("Destination image is not from ImageWriter. Only"
                     + " the images from ImageWriter are writable");
@@ -193,7 +198,8 @@
             case ImageFormat.YV12:
             case ImageFormat.YUV_420_888:
             case ImageFormat.NV21:
-            case ImageFormat.PRIVATE: // A really rough estimate because the real size is unknown.
+            case ImageFormat.RAW12:
+            case ImageFormat.PRIVATE: // A rough estimate because the real size is unknown.
                 estimatedBytePerPixel = 1.5;
                 break;
             case ImageFormat.NV16:
@@ -201,6 +207,7 @@
             case ImageFormat.YUY2:
             case ImageFormat.Y16:
             case ImageFormat.RAW_SENSOR:
+            case ImageFormat.RAW_PRIVATE: // round estimate, real size is unknown
             case ImageFormat.DEPTH16:
                 estimatedBytePerPixel = 2.0;
                 break;
@@ -245,6 +252,7 @@
             case ImageFormat.Y16:
             case ImageFormat.RAW_SENSOR:
             case ImageFormat.RAW10:
+            case ImageFormat.RAW12:
                 return new Size(image.getWidth(), image.getHeight());
             case ImageFormat.PRIVATE:
                 return new Size(0, 0);
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 4101935..e6bc8f1 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -1735,7 +1735,8 @@
             CodecProfileLevel[] profileLevels = mParent.profileLevels;
             String mime = mParent.getMimeType();
 
-            if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_AVC)) {
+            if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_AVC) ||
+                    mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_DOLBY_AVC)) {
                 maxBlocks = 99;
                 maxBlocksPerSecond = 1485;
                 maxBps = 64000;
@@ -2089,7 +2090,8 @@
                 applyMacroBlockLimits(Short.MAX_VALUE, Short.MAX_VALUE,
                         maxBlocks, maxBlocksPerSecond, blockSize, blockSize,
                         1 /* widthAlignment */, 1 /* heightAlignment */);
-            } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC)) {
+            } else if (mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_HEVC) ||
+                    mime.equalsIgnoreCase(MediaFormat.MIMETYPE_VIDEO_DOLBY_HEVC)) {
                 maxBlocks = 36864;
                 maxBlocksPerSecond = maxBlocks * 15;
                 maxBps = 128000;
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index b2fa0ac..a102e51 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -92,6 +92,8 @@
     public static final String MIMETYPE_VIDEO_H263 = "video/3gpp";
     public static final String MIMETYPE_VIDEO_MPEG2 = "video/mpeg2";
     public static final String MIMETYPE_VIDEO_RAW = "video/raw";
+    public static final String MIMETYPE_VIDEO_DOLBY_AVC = "video/dolby-avc";
+    public static final String MIMETYPE_VIDEO_DOLBY_HEVC = "video/dolby-hevc";
 
     public static final String MIMETYPE_AUDIO_AMR_NB = "audio/3gpp";
     public static final String MIMETYPE_AUDIO_AMR_WB = "audio/amr-wb";
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index a046512..bcc2b406 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -18,6 +18,7 @@
 
 import android.Manifest;
 import android.annotation.DrawableRes;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.app.ActivityThread;
 import android.content.BroadcastReceiver;
@@ -41,6 +42,8 @@
 import android.util.Log;
 import android.view.Display;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -201,6 +204,7 @@
                         info.mDescription = sStatic.mResources.getText(
                                 com.android.internal.R.string.bluetooth_a2dp_audio_route_name);
                         info.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO;
+                        info.mDeviceType = RouteInfo.DEVICE_TYPE_BLUETOOTH;
                         sStatic.mBluetoothA2dpRoute = info;
                         addRouteStatic(sStatic.mBluetoothA2dpRoute);
                     } else {
@@ -480,6 +484,7 @@
             route.mName = globalRoute.name;
             route.mDescription = globalRoute.description;
             route.mSupportedTypes = globalRoute.supportedTypes;
+            route.mDeviceType = globalRoute.deviceType;
             route.mEnabled = globalRoute.enabled;
             route.setRealStatusCode(globalRoute.statusCode);
             route.mPlaybackType = globalRoute.playbackType;
@@ -1411,6 +1416,7 @@
         newRoute.mDescription = sStatic.mResources.getText(
                 com.android.internal.R.string.wireless_display_route_description);
         newRoute.updatePresentationDisplay();
+        newRoute.mDeviceType = RouteInfo.DEVICE_TYPE_TV;
         return newRoute;
     }
 
@@ -1470,6 +1476,7 @@
         CharSequence mDescription;
         private CharSequence mStatus;
         int mSupportedTypes;
+        int mDeviceType;
         RouteGroup mGroup;
         final RouteCategory mCategory;
         Drawable mIcon;
@@ -1502,6 +1509,42 @@
         /** @hide */ public static final int STATUS_IN_USE = 5;
         /** @hide */ public static final int STATUS_CONNECTED = 6;
 
+        /** @hide */
+        @IntDef({DEVICE_TYPE_UNKNOWN, DEVICE_TYPE_TV, DEVICE_TYPE_SPEAKER, DEVICE_TYPE_BLUETOOTH})
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface DeviceType {}
+
+        /**
+         * The default receiver device type of the route indicating the type is unknown.
+         *
+         * @see #getDeviceType
+         */
+        public static final int DEVICE_TYPE_UNKNOWN = 0;
+
+        /**
+         * A receiver device type of the route indicating the presentation of the media is happening
+         * on a TV.
+         *
+         * @see #getDeviceType
+         */
+        public static final int DEVICE_TYPE_TV = 1;
+
+        /**
+         * A receiver device type of the route indicating the presentation of the media is happening
+         * on a speaker.
+         *
+         * @see #getDeviceType
+         */
+        public static final int DEVICE_TYPE_SPEAKER = 2;
+
+        /**
+         * A receiver device type of the route indicating the presentation of the media is happening
+         * on a bluetooth device such as a bluetooth speaker.
+         *
+         * @see #getDeviceType
+         */
+        public static final int DEVICE_TYPE_BLUETOOTH = 3;
+
         private Object mTag;
 
         /**
@@ -1533,6 +1576,7 @@
 
         RouteInfo(RouteCategory category) {
             mCategory = category;
+            mDeviceType = DEVICE_TYPE_UNKNOWN;
         }
 
         /**
@@ -1670,6 +1714,18 @@
             return mSupportedTypes;
         }
 
+        /**
+         * Gets the type of the receiver device associated with this route.
+         *
+         * @return The type of the receiver device associated with this route:
+         * {@link #DEVICE_TYPE_BLUETOOTH}, {@link #DEVICE_TYPE_TV}, {@link #DEVICE_TYPE_SPEAKER},
+         * or {@link #DEVICE_TYPE_UNKNOWN}.
+         */
+        @DeviceType
+        public int getDeviceType() {
+            return mDeviceType;
+        }
+
         /** @hide */
         public boolean matchesTypes(int types) {
             return (mSupportedTypes & types) != 0;
diff --git a/media/java/android/media/MediaRouterClientState.java b/media/java/android/media/MediaRouterClientState.java
index 54b8276..34e18f6 100644
--- a/media/java/android/media/MediaRouterClientState.java
+++ b/media/java/android/media/MediaRouterClientState.java
@@ -104,6 +104,7 @@
         public int volumeMax;
         public int volumeHandling;
         public int presentationDisplayId;
+        public @MediaRouter.RouteInfo.DeviceType int deviceType;
 
         public RouteInfo(String id) {
             this.id = id;
@@ -113,6 +114,7 @@
             playbackStream = -1;
             volumeHandling = MediaRouter.RouteInfo.PLAYBACK_VOLUME_FIXED;
             presentationDisplayId = -1;
+            deviceType = MediaRouter.RouteInfo.DEVICE_TYPE_UNKNOWN;
         }
 
         public RouteInfo(RouteInfo other) {
@@ -128,6 +130,7 @@
             volumeMax = other.volumeMax;
             volumeHandling = other.volumeHandling;
             presentationDisplayId = other.presentationDisplayId;
+            deviceType = other.deviceType;
         }
 
         RouteInfo(Parcel in) {
@@ -143,6 +146,7 @@
             volumeMax = in.readInt();
             volumeHandling = in.readInt();
             presentationDisplayId = in.readInt();
+            deviceType = in.readInt();
         }
 
         @Override
@@ -164,6 +168,7 @@
             dest.writeInt(volumeMax);
             dest.writeInt(volumeHandling);
             dest.writeInt(presentationDisplayId);
+            dest.writeInt(deviceType);
         }
 
         @Override
@@ -180,6 +185,7 @@
                     + ", volumeMax=" + volumeMax
                     + ", volumeHandling=" + volumeHandling
                     + ", presentationDisplayId=" + presentationDisplayId
+                    + ", deviceType=" + deviceType
                     + " }";
         }
 
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 9a53186..1c043e0 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -101,6 +101,8 @@
     void setOpaqueConsumer(const sp<BufferItemConsumer>& consumer) { mOpaqueConsumer = consumer; }
     BufferItemConsumer* getOpaqueConsumer() { return mOpaqueConsumer.get(); }
     // This is the only opaque format exposed in the ImageFormat public API.
+    // Note that we do support CPU access for HAL_PIXEL_FORMAT_RAW_OPAQUE
+    // (ImageFormat#RAW_PRIVATE) so it doesn't count as opaque here.
     bool isOpaque() { return mFormat == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED; }
 
     void setProducer(const sp<IGraphicBufferProducer>& producer) { mProducer = producer; }
@@ -470,7 +472,8 @@
         case HAL_PIXEL_FORMAT_BLOB:
             // Used for JPEG data, height must be 1, width == size, single plane.
             ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
-            ALOG_ASSERT(buffer->height == 1, "JPEG should has height value %d", buffer->height);
+            ALOG_ASSERT(buffer->height == 1,
+                    "JPEG should has height value one but got %d", buffer->height);
 
             pData = buffer->data;
             dataSize = Image_getJpegSize(buffer, usingRGBAOverride);
@@ -482,6 +485,14 @@
             pData = buffer->data;
             dataSize = buffer->stride * buffer->height * bytesPerPixel;
             break;
+        case HAL_PIXEL_FORMAT_RAW_OPAQUE:
+            // Used for RAW_OPAQUE data, height must be 1, width == size, single plane.
+            ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
+            ALOG_ASSERT(buffer->height == 1,
+                    "RAW_PRIVATE should has height value one but got %d", buffer->height);
+            pData = buffer->data;
+            dataSize = buffer->width;
+            break;
         case HAL_PIXEL_FORMAT_RAW10:
             // Single plane 10bpp bayer data.
             ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
@@ -593,6 +604,10 @@
             ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
             pixelStride = 3;
             break;
+        case HAL_PIXEL_FORMAT_RAW_OPAQUE:
+            ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
+            pixelStride = 0; // RAW OPAQUE doesn't have pixel stride
+            break;
         default:
             jniThrowExceptionFmt(env, "java/lang/UnsupportedOperationException",
                                  "Pixel format: 0x%x is unsupported", fmt);
@@ -669,6 +684,10 @@
             ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
             rowStride = buffer->stride * 3;
             break;
+        case HAL_PIXEL_FORMAT_RAW_OPAQUE:
+            ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
+            rowStride = 0; // RAW OPAQUE doesn't have row stride
+            break;
         default:
             ALOGE("%s Pixel format: 0x%x is unsupported", __FUNCTION__, fmt);
             jniThrowException(env, "java/lang/UnsupportedOperationException",
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
index 57969ba..6f74203 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
@@ -129,7 +129,7 @@
 
             int res = mUtils.getCameraService().supportsCameraApi(cameraId, API_VERSION_2);
 
-            if (res != CameraBinderTestUtils.NO_ERROR && res != CameraBinderTestUtils.EOPNOTSUPP) {
+            if (res != CameraBinderTestUtils.NO_ERROR && res != -android.system.OsConstants.EOPNOTSUPP) {
                 fail("Camera service returned bad value when queried if it supports camera2 api: "
                         + res + " for camera ID " + cameraId);
             }
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTestUtils.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTestUtils.java
index 6be538a..5c4b23b 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTestUtils.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTestUtils.java
@@ -2,6 +2,7 @@
 package com.android.mediaframeworktest.integration;
 
 import static org.junit.Assert.assertNotNull;
+import static android.system.OsConstants.*;
 
 import android.content.Context;
 import android.content.pm.FeatureInfo;
@@ -18,11 +19,10 @@
     static final String CAMERA_SERVICE_BINDER_NAME = "media.camera";
 
     protected static final int USE_CALLING_UID = -1;
-    protected static final int BAD_VALUE = -22;
-    protected static final int INVALID_OPERATION = -38;
-    protected static final int ALREADY_EXISTS = -17;
+    protected static final int BAD_VALUE = -EINVAL;
+    protected static final int INVALID_OPERATION = -ENOSYS;
+    protected static final int ALREADY_EXISTS = -EEXIST;
     public static final int NO_ERROR = 0;
-    public static final int EOPNOTSUPP = -95;
     private final Context mContext;
 
     public CameraBinderTestUtils(Context context) {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsBinderDecoratorTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsBinderDecoratorTest.java
index 727af78..33c6388 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsBinderDecoratorTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraUtilsBinderDecoratorTest.java
@@ -27,6 +27,7 @@
 import static org.mockito.Mockito.*;
 import static android.hardware.camera2.utils.CameraBinderDecorator.*;
 import static android.hardware.camera2.CameraAccessException.*;
+import static android.system.OsConstants.*;
 
 import junit.framework.Assert;
 
@@ -78,9 +79,9 @@
             when(mock.doSomethingAlreadyExists()).thenReturn(ALREADY_EXISTS);
             when(mock.doSomethingBadValue()).thenReturn(BAD_VALUE);
             when(mock.doSomethingDeadObject()).thenReturn(DEAD_OBJECT);
-            when(mock.doSomethingBadPolicy()).thenReturn(EACCES);
-            when(mock.doSomethingDeviceBusy()).thenReturn(EBUSY);
-            when(mock.doSomethingNoSuchDevice()).thenReturn(ENODEV);
+            when(mock.doSomethingBadPolicy()).thenReturn(-EACCES);
+            when(mock.doSomethingDeviceBusy()).thenReturn(-EBUSY);
+            when(mock.doSomethingNoSuchDevice()).thenReturn(-ENODEV);
             when(mock.doSomethingUnknownErrorCode()).thenReturn(SOME_ARBITRARY_NEGATIVE_INT);
             when(mock.doSomethingThrowDeadObjectException()).thenThrow(new DeadObjectException());
             when(mock.doSomethingThrowTransactionTooLargeException()).thenThrow(
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 2d77c6a..770d011 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -316,12 +316,8 @@
         mAdapter = new DocumentsAdapter(context);
         mRecView.setAdapter(mAdapter);
 
-        mDefaultItemColor = context.getResources().getColor(android.R.color.transparent);
-        // Get the accent color.
-        TypedValue selColor = new TypedValue();
-        context.getTheme().resolveAttribute(android.R.attr.colorAccent, selColor, true);
-        // Set the opacity to 10%.
-        mSelectedItemColor = (selColor.data & 0x00ffffff) | 0x16000000;
+        mDefaultItemColor = context.getResources().getColor(R.color.item_doc_background);
+        mSelectedItemColor = context.getResources().getColor(R.color.item_doc_background_selected);
 
         GestureDetector.SimpleOnGestureListener listener =
                 new GestureDetector.SimpleOnGestureListener() {
@@ -943,6 +939,7 @@
 
         public void setSelected(boolean selected) {
             itemView.setActivated(selected);
+            itemView.setBackgroundColor(selected ? mSelectedItemColor : mDefaultItemColor);
         }
 
         @Override
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/NetworkPolicySerializer.java b/packages/SettingsProvider/src/com/android/providers/settings/NetworkPolicySerializer.java
new file mode 100644
index 0000000..4b87da4
--- /dev/null
+++ b/packages/SettingsProvider/src/com/android/providers/settings/NetworkPolicySerializer.java
@@ -0,0 +1,186 @@
+package com.android.providers.settings;
+
+import android.net.NetworkPolicy;
+import android.net.NetworkTemplate;
+import android.util.Log;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+
+/**
+ * Backup/Restore Serializer Class for android.net.NetworkPolicy
+ */
+public class NetworkPolicySerializer {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "NetworkPolicySerializer";
+
+    private static final int NULL = 0;
+    private static final int NOT_NULL = 1;
+    /**
+     * Current Version of the Serializer.
+     */
+    private static int STATE_VERSION = 1;
+
+    /**
+     * Marshals an array of NetworkPolicy objects into a byte-array.
+     *
+     * @param policies - NetworkPolicies to be Marshaled
+     * @return byte array
+     */
+
+    public static byte[] marshalNetworkPolicies(NetworkPolicy policies[]) {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        if (policies != null && policies.length != 0) {
+            DataOutputStream out = new DataOutputStream(baos);
+            try {
+                out.writeInt(STATE_VERSION);
+                out.writeInt(policies.length);
+                for (NetworkPolicy policy : policies) {
+                    byte[] marshaledPolicy = marshalNetworkPolicy(policy);
+                    if (marshaledPolicy != null) {
+                        out.writeByte(NOT_NULL);
+                        out.writeInt(marshaledPolicy.length);
+                        out.write(marshaledPolicy);
+                    } else {
+                        out.writeByte(NULL);
+                    }
+                }
+            } catch (IOException ioe) {
+                Log.e(TAG, "Failed to Convert NetworkPolicies to byte array", ioe);
+                baos.reset();
+            }
+        }
+        return baos.toByteArray();
+    }
+
+    /**
+     * Unmarshals a byte array into an array of NetworkPolicy Objects
+     *
+     * @param data - marshaled NetworkPolicies Array
+     * @return NetworkPolicy[] array
+     */
+    public static NetworkPolicy[] unmarshalNetworkPolicies(byte[] data) {
+        if (data == null || data.length == 0) {
+            return new NetworkPolicy[0];
+        }
+        DataInputStream in = new DataInputStream(new ByteArrayInputStream(data));
+        try {
+            int version = in.readInt();
+            int length = in.readInt();
+            NetworkPolicy[] policies = new NetworkPolicy[length];
+            for (int i = 0; i < length; i++) {
+                byte isNull = in.readByte();
+                if (isNull == NULL) continue;
+                int byteLength = in.readInt();
+                byte[] policyData = new byte[byteLength];
+                in.read(policyData, 0, byteLength);
+                policies[i] = unmarshalNetworkPolicy(policyData);
+            }
+            return policies;
+        } catch (IOException ioe) {
+            Log.e(TAG, "Failed to Convert byte array to NetworkPolicies", ioe);
+            return new NetworkPolicy[0];
+        }
+    }
+
+    /**
+     * Marshals a NetworkPolicy object into a byte-array.
+     *
+     * @param networkPolicy - NetworkPolicy to be Marshaled
+     * @return byte array
+     */
+    public static byte[] marshalNetworkPolicy(NetworkPolicy networkPolicy) {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        if (networkPolicy != null) {
+            DataOutputStream out = new DataOutputStream(baos);
+            try {
+                out.writeInt(STATE_VERSION);
+                writeNetworkTemplate(out, networkPolicy.template);
+                out.writeInt(networkPolicy.cycleDay);
+                writeString(out, networkPolicy.cycleTimezone);
+                out.writeLong(networkPolicy.warningBytes);
+                out.writeLong(networkPolicy.limitBytes);
+                out.writeLong(networkPolicy.lastWarningSnooze);
+                out.writeLong(networkPolicy.lastLimitSnooze);
+                out.writeInt(networkPolicy.metered ? 1 : 0);
+                out.writeInt(networkPolicy.inferred ? 1 : 0);
+            } catch (IOException ioe) {
+                Log.e(TAG, "Failed to Convert NetworkPolicy to byte array", ioe);
+                baos.reset();
+            }
+        }
+        return baos.toByteArray();
+    }
+
+    /**
+     * Unmarshals a byte array into a NetworkPolicy Object
+     *
+     * @param data - marshaled NetworkPolicy Object
+     * @return NetworkPolicy Object
+     */
+    public static NetworkPolicy unmarshalNetworkPolicy(byte[] data) {
+        if (data == null || data.length == 0) {
+            return null;
+        }
+        DataInputStream in = new DataInputStream(new ByteArrayInputStream(data));
+        try {
+            int version = in.readInt();
+            NetworkTemplate template = readNetworkTemplate(in, version);
+            int cycleDay = in.readInt();
+            String cycleTimeZone = readString(in, version);
+            long warningBytes = in.readLong();
+            long limitBytes = in.readLong();
+            long lastWarningSnooze = in.readLong();
+            long lastLimitSnooze = in.readLong();
+            boolean metered = in.readInt() == 1;
+            boolean inferred = in.readInt() == 1;
+            return new NetworkPolicy(template, cycleDay, cycleTimeZone, warningBytes, limitBytes,
+                    lastWarningSnooze, lastLimitSnooze, metered, inferred);
+        } catch (IOException ioe) {
+            Log.e(TAG, "Failed to Convert byte array to NetworkPolicy", ioe);
+            return null;
+        }
+    }
+
+    private static NetworkTemplate readNetworkTemplate(DataInputStream in, int version)
+            throws IOException {
+        byte isNull = in.readByte();
+        if (isNull == NULL) return null;
+        int matchRule = in.readInt();
+        String subscriberId = readString(in, version);
+        String networkId = readString(in, version);
+        return new NetworkTemplate(matchRule, subscriberId, networkId);
+    }
+
+    private static void writeNetworkTemplate(DataOutputStream out, NetworkTemplate template)
+            throws IOException {
+        if (template != null) {
+            out.writeByte(NOT_NULL);
+            out.writeInt(template.getMatchRule());
+            writeString(out, template.getSubscriberId());
+            writeString(out, template.getNetworkId());
+        } else {
+            out.writeByte(NULL);
+        }
+    }
+
+    private static String readString(DataInputStream in, int version) throws IOException {
+        byte isNull = in.readByte();
+        if (isNull == NOT_NULL) {
+            return in.readUTF();
+        }
+        return null;
+    }
+
+    private static void writeString(DataOutputStream out, String val) throws IOException {
+        if (val != null) {
+            out.writeByte(NOT_NULL);
+            out.writeUTF(val);
+        } else {
+            out.writeByte(NULL);
+        }
+    }
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 2e96f18..185a03f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -24,6 +24,7 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
+import android.net.NetworkPolicyManager;
 import android.net.Uri;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiConfiguration.KeyMgmt;
@@ -81,10 +82,13 @@
     private static final String KEY_GLOBAL = "global";
     private static final String KEY_LOCALE = "locale";
     private static final String KEY_LOCK_SETTINGS = "lock_settings";
+    private static final String KEY_SOFTAP_CONFIG = "softap_config";
+    private static final String KEY_NET_POLICIES = "network_policies";
+
 
     // Versioning of the state file.  Increment this version
     // number any time the set of state items is altered.
-    private static final int STATE_VERSION = 4;
+    private static final int STATE_VERSION = 6;
 
     // Slots in the checksum array.  Never insert new items in the middle
     // of this array; new slots must be appended.
@@ -95,22 +99,28 @@
     private static final int STATE_WIFI_CONFIG     = 4;
     private static final int STATE_GLOBAL          = 5;
     private static final int STATE_LOCK_SETTINGS   = 6;
+    private static final int STATE_SOFTAP_CONFIG   = 7;
+    private static final int STATE_NET_POLICIES    = 8;
 
-    private static final int STATE_SIZE            = 7; // The current number of state items
+    private static final int STATE_SIZE            = 9; // The current number of state items
 
     // Number of entries in the checksum array at various version numbers
     private static final int STATE_SIZES[] = {
-        0,
-        4,              // version 1
-        5,              // version 2 added STATE_WIFI_CONFIG
-        6,              // version 3 added STATE_GLOBAL
-        STATE_SIZE      // version 4 added STATE_LOCK_SETTINGS
+            0,
+            4,              // version 1
+            5,              // version 2 added STATE_WIFI_CONFIG
+            6,              // version 3 added STATE_GLOBAL
+            7,              // version 4 added STATE_LOCK_SETTINGS
+            8,              // version 5 added STATE_SOFTAP_CONFIG
+            STATE_SIZE      // version 6 added STATE_NET_POLICIES
     };
 
     // Versioning of the 'full backup' format
     private static final int FULL_BACKUP_VERSION = 3;
     private static final int FULL_BACKUP_ADDED_GLOBAL = 2;  // added the "global" entry
     private static final int FULL_BACKUP_ADDED_LOCK_SETTINGS = 3; // added the "lock_settings" entry
+    private static final int FULL_BACKUP_ADDED_SOFTAP_CONF = 4; //added the "softap_config" entry
+    private static final int FULL_BACKUP_ADDED_NET_POLICIES = 5; //added the "network_policies" entry
 
     private static final int INTEGER_BYTE_COUNT = Integer.SIZE / Byte.SIZE;
 
@@ -119,8 +129,8 @@
     private static final String TAG = "SettingsBackupAgent";
 
     private static final String[] PROJECTION = {
-        Settings.NameValueTable.NAME,
-        Settings.NameValueTable.VALUE
+            Settings.NameValueTable.NAME,
+            Settings.NameValueTable.VALUE
     };
 
     private static final String FILE_WIFI_SUPPLICANT = "/data/misc/wifi/wpa_supplicant.conf";
@@ -396,7 +406,7 @@
 
     @Override
     public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
-            ParcelFileDescriptor newState) throws IOException {
+                         ParcelFileDescriptor newState) throws IOException {
 
         byte[] systemSettingsData = getSystemSettings();
         byte[] secureSettingsData = getSecureSettings();
@@ -405,26 +415,34 @@
         byte[] locale = mSettingsHelper.getLocaleData();
         byte[] wifiSupplicantData = getWifiSupplicant(FILE_WIFI_SUPPLICANT);
         byte[] wifiConfigData = getFileData(mWifiConfigFile);
+        byte[] softApConfigData = getSoftAPConfiguration();
+        byte[] netPoliciesData = getNetworkPolicies();
 
         long[] stateChecksums = readOldChecksums(oldState);
 
         stateChecksums[STATE_SYSTEM] =
-            writeIfChanged(stateChecksums[STATE_SYSTEM], KEY_SYSTEM, systemSettingsData, data);
+                writeIfChanged(stateChecksums[STATE_SYSTEM], KEY_SYSTEM, systemSettingsData, data);
         stateChecksums[STATE_SECURE] =
-            writeIfChanged(stateChecksums[STATE_SECURE], KEY_SECURE, secureSettingsData, data);
+                writeIfChanged(stateChecksums[STATE_SECURE], KEY_SECURE, secureSettingsData, data);
         stateChecksums[STATE_GLOBAL] =
-            writeIfChanged(stateChecksums[STATE_GLOBAL], KEY_GLOBAL, globalSettingsData, data);
+                writeIfChanged(stateChecksums[STATE_GLOBAL], KEY_GLOBAL, globalSettingsData, data);
         stateChecksums[STATE_LOCALE] =
-            writeIfChanged(stateChecksums[STATE_LOCALE], KEY_LOCALE, locale, data);
+                writeIfChanged(stateChecksums[STATE_LOCALE], KEY_LOCALE, locale, data);
         stateChecksums[STATE_WIFI_SUPPLICANT] =
-            writeIfChanged(stateChecksums[STATE_WIFI_SUPPLICANT], KEY_WIFI_SUPPLICANT,
-                    wifiSupplicantData, data);
+                writeIfChanged(stateChecksums[STATE_WIFI_SUPPLICANT], KEY_WIFI_SUPPLICANT,
+                        wifiSupplicantData, data);
         stateChecksums[STATE_WIFI_CONFIG] =
-            writeIfChanged(stateChecksums[STATE_WIFI_CONFIG], KEY_WIFI_CONFIG, wifiConfigData,
-                    data);
+                writeIfChanged(stateChecksums[STATE_WIFI_CONFIG], KEY_WIFI_CONFIG, wifiConfigData,
+                        data);
         stateChecksums[STATE_LOCK_SETTINGS] =
-            writeIfChanged(stateChecksums[STATE_LOCK_SETTINGS], KEY_LOCK_SETTINGS,
-                    lockSettingsData, data);
+                writeIfChanged(stateChecksums[STATE_LOCK_SETTINGS], KEY_LOCK_SETTINGS,
+                        lockSettingsData, data);
+        stateChecksums[STATE_SOFTAP_CONFIG] =
+                writeIfChanged(stateChecksums[STATE_SOFTAP_CONFIG], KEY_SOFTAP_CONFIG,
+                        softApConfigData, data);
+        stateChecksums[STATE_NET_POLICIES] =
+                writeIfChanged(stateChecksums[STATE_NET_POLICIES], KEY_NET_POLICIES,
+                        netPoliciesData, data);
 
         writeNewChecksums(stateChecksums, newState);
     }
@@ -504,7 +522,7 @@
                             restoredSupplicantData, restoredSupplicantData.length);
                     FileUtils.setPermissions(FILE_WIFI_SUPPLICANT,
                             FileUtils.S_IRUSR | FileUtils.S_IWUSR |
-                            FileUtils.S_IRGRP | FileUtils.S_IWGRP,
+                                    FileUtils.S_IRGRP | FileUtils.S_IWGRP,
                             Process.myUid(), Process.WIFI_UID);
                 }
                 if (restoredWifiConfigFile != null) {
@@ -533,7 +551,7 @@
 
     @Override
     public void onRestore(BackupDataInput data, int appVersionCode,
-            ParcelFileDescriptor newState) throws IOException {
+                          ParcelFileDescriptor newState) throws IOException {
 
         HashSet<String> movedToGlobal = new HashSet<String>();
         Settings.System.getMovedToGlobalSettings(movedToGlobal);
@@ -561,7 +579,15 @@
                 mWifiRestore.incorporateWifiConfigFile(data);
             } else if (KEY_LOCK_SETTINGS.equals(key)) {
                 restoreLockSettings(data);
-             } else {
+            } else if (KEY_SOFTAP_CONFIG.equals(key)){
+                byte[] softapData = new byte[size];
+                data.readEntityData(softapData, 0, size);
+                restoreSoftApConfiguration(softapData);
+            } else if (KEY_NET_POLICIES.equals(key)) {
+                byte[] netPoliciesData = new byte[size];
+                data.readEntityData(netPoliciesData, 0, size);
+                restoreNetworkPolicies(netPoliciesData);
+            } else {
                 data.skipEntityData();
             }
         }
@@ -589,6 +615,8 @@
         byte[] locale = mSettingsHelper.getLocaleData();
         byte[] wifiSupplicantData = getWifiSupplicant(FILE_WIFI_SUPPLICANT);
         byte[] wifiConfigData = getFileData(mWifiConfigFile);
+        byte[] softApConfigData = getSoftAPConfiguration();
+        byte[] netPoliciesData = getNetworkPolicies();
 
         // Write the data to the staging file, then emit that as our tarfile
         // representation of the backed-up settings.
@@ -623,6 +651,12 @@
             if (DEBUG_BACKUP) Log.d(TAG, lockSettingsData.length + " bytes of lock settings data");
             out.writeInt(lockSettingsData.length);
             out.write(lockSettingsData);
+            if (DEBUG_BACKUP) Log.d(TAG, softApConfigData.length + " bytes of softap config data");
+            out.writeInt(softApConfigData.length);
+            out.write(softApConfigData);
+            if (DEBUG_BACKUP) Log.d(TAG, netPoliciesData.length + " bytes of network policies data");
+            out.writeInt(netPoliciesData.length);
+            out.write(netPoliciesData);
 
             out.flush();    // also flushes downstream
 
@@ -635,7 +669,7 @@
 
     @Override
     public void onRestoreFile(ParcelFileDescriptor data, long size,
-            int type, String domain, String relpath, long mode, long mtime)
+                              int type, String domain, String relpath, long mode, long mtime)
             throws IOException {
         if (DEBUG_BACKUP) Log.d(TAG, "onRestoreFile() invoked");
         // Our data is actually a blob of flattened settings data identical to that
@@ -692,7 +726,7 @@
             restoreWifiSupplicant(FILE_WIFI_SUPPLICANT, buffer, nBytes);
             FileUtils.setPermissions(FILE_WIFI_SUPPLICANT,
                     FileUtils.S_IRUSR | FileUtils.S_IWUSR |
-                    FileUtils.S_IRGRP | FileUtils.S_IWGRP,
+                            FileUtils.S_IRGRP | FileUtils.S_IWGRP,
                     Process.myUid(), Process.WIFI_UID);
             // retain the previous WIFI state.
             enableWifi(retainedWifiState == WifiManager.WIFI_STATE_ENABLED ||
@@ -715,6 +749,26 @@
                 }
             }
 
+            if (version >= FULL_BACKUP_ADDED_SOFTAP_CONF){
+                nBytes = in.readInt();
+                if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of softap config data");
+                if (nBytes > buffer.length) buffer = new byte[nBytes];
+                if (nBytes > 0) {
+                    in.readFully(buffer, 0, nBytes);
+                    restoreSoftApConfiguration(buffer);
+                }
+            }
+
+            if (version >= FULL_BACKUP_ADDED_NET_POLICIES){
+                nBytes = in.readInt();
+                if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of network policies data");
+                if (nBytes > buffer.length) buffer = new byte[nBytes];
+                if (nBytes > 0) {
+                    in.readFully(buffer, 0, nBytes);
+                    restoreNetworkPolicies(buffer);
+                }
+            }
+
             if (DEBUG_BACKUP) Log.d(TAG, "Full restore complete.");
         } else {
             data.close();
@@ -754,7 +808,7 @@
     }
 
     private long writeIfChanged(long oldChecksum, String key, byte[] data,
-            BackupDataOutput output) {
+                                BackupDataOutput output) {
         CRC32 checkSummer = new CRC32();
         checkSummer.update(data);
         long newChecksum = checkSummer.getValue();
@@ -829,7 +883,7 @@
     }
 
     private void restoreSettings(BackupDataInput data, Uri contentUri,
-            HashSet<String> movedToGlobal) {
+                                 HashSet<String> movedToGlobal) {
         byte[] settings = new byte[data.getDataSize()];
         try {
             data.readEntityData(settings, 0, settings.length);
@@ -841,7 +895,7 @@
     }
 
     private void restoreSettings(byte[] settings, int bytes, Uri contentUri,
-            HashSet<String> movedToGlobal) {
+                                 HashSet<String> movedToGlobal) {
         if (DEBUG) {
             Log.i(TAG, "restoreSettings: " + contentUri);
         }
@@ -1160,6 +1214,30 @@
         }
     }
 
+    private byte[] getSoftAPConfiguration(){
+        WifiManager wifiManager = (WifiManager)getSystemService(Context.WIFI_SERVICE);
+        return WiFiConfigurationSerializer.marshalWifiConfig(wifiManager.getWifiApConfiguration());
+    }
+
+    private void restoreSoftApConfiguration(byte[] data){
+        WifiManager wifiManager = (WifiManager)getSystemService(Context.WIFI_SERVICE);
+        wifiManager.setWifiApConfiguration(WiFiConfigurationSerializer.unmarshalWifiConfig(data));
+    }
+
+    private byte[] getNetworkPolicies(){
+        NetworkPolicyManager networkPolicyManager =
+                (NetworkPolicyManager)getSystemService(NETWORK_POLICY_SERVICE);
+        return NetworkPolicySerializer
+                .marshalNetworkPolicies(networkPolicyManager.getNetworkPolicies());
+    }
+
+    private void restoreNetworkPolicies(byte[] data){
+        NetworkPolicyManager networkPolicyManager =
+                (NetworkPolicyManager)getSystemService(NETWORK_POLICY_SERVICE);
+        networkPolicyManager
+                .setNetworkPolicies(NetworkPolicySerializer.unmarshalNetworkPolicies(data));
+    }
+
     /**
      * Write an int in BigEndian into the byte array.
      * @param out byte array
@@ -1181,8 +1259,7 @@
     }
 
     private int readInt(byte[] in, int pos) {
-        int result =
-                ((in[pos    ] & 0xFF) << 24) |
+        int result =    ((in[pos    ] & 0xFF) << 24) |
                 ((in[pos + 1] & 0xFF) << 16) |
                 ((in[pos + 2] & 0xFF) <<  8) |
                 ((in[pos + 3] & 0xFF) <<  0);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/WiFiConfigurationSerializer.java b/packages/SettingsProvider/src/com/android/providers/settings/WiFiConfigurationSerializer.java
new file mode 100644
index 0000000..f9f1d3f
--- /dev/null
+++ b/packages/SettingsProvider/src/com/android/providers/settings/WiFiConfigurationSerializer.java
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2015 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.providers.settings;
+
+import android.net.IpConfiguration;
+import android.net.LinkAddress;
+import android.net.ProxyInfo;
+import android.net.StaticIpConfiguration;
+import android.net.wifi.WifiConfiguration;
+import android.util.Log;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.util.BitSet;
+
+
+/**
+ * Backup/Restore Serializer Class for com.android.net.wifi.WifiConfiguration
+ */
+public class WiFiConfigurationSerializer {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "WiFiConfigSerializer";
+
+    private static final int NULL = 0;
+    private static final int NOT_NULL = 1;
+    /**
+     * Current Version of the Serializer.
+     */
+    private static int STATE_VERSION = 1;
+
+
+    /**
+     * Marshals a WifiConfig object into a byte-array.
+     *
+     * @param wifiConfig - WifiConfiguration to be Marshalled
+     * @return byte array
+     */
+
+    public static byte[] marshalWifiConfig(WifiConfiguration wifiConfig) {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        if(wifiConfig != null) {
+            DataOutputStream out = new DataOutputStream(baos);
+            try {
+                out.writeInt(STATE_VERSION);
+                out.writeInt(wifiConfig.networkId);
+                out.writeInt(wifiConfig.status);
+                out.writeInt(wifiConfig.disableReason);
+                writeString(out, wifiConfig.SSID);
+                writeString(out, wifiConfig.BSSID);
+                out.writeInt(wifiConfig.apBand);
+                out.writeInt(wifiConfig.apChannel);
+                writeString(out, wifiConfig.autoJoinBSSID);
+                writeString(out, wifiConfig.FQDN);
+                writeString(out, wifiConfig.providerFriendlyName);
+                out.writeInt(wifiConfig.roamingConsortiumIds.length);
+                for (long id : wifiConfig.roamingConsortiumIds) {
+                    out.writeLong(id);
+                }
+                writeString(out, wifiConfig.preSharedKey);
+                for (String wepKey : wifiConfig.wepKeys) {
+                    writeString(out, wepKey);
+                }
+                out.writeInt(wifiConfig.wepTxKeyIndex);
+                out.writeInt(wifiConfig.priority);
+                out.writeInt(wifiConfig.hiddenSSID ? 1 : 0);
+                out.writeInt(wifiConfig.requirePMF ? 1 : 0);
+                writeString(out, wifiConfig.updateIdentifier);
+
+                writeBitSet(out, wifiConfig.allowedKeyManagement);
+                writeBitSet(out, wifiConfig.allowedProtocols);
+                writeBitSet(out, wifiConfig.allowedAuthAlgorithms);
+                writeBitSet(out, wifiConfig.allowedPairwiseCiphers);
+                writeBitSet(out, wifiConfig.allowedGroupCiphers);
+
+
+                //IpConfiguration
+                writeIpConfiguration(out, wifiConfig.getIpConfiguration());
+
+                writeString(out, wifiConfig.dhcpServer);
+                writeString(out, wifiConfig.defaultGwMacAddress);
+                out.writeInt(wifiConfig.autoJoinStatus);
+                out.writeInt(wifiConfig.selfAdded ? 1 : 0);
+                out.writeInt(wifiConfig.didSelfAdd ? 1 : 0);
+                out.writeInt(wifiConfig.validatedInternetAccess ? 1 : 0);
+                out.writeInt(wifiConfig.ephemeral ? 1 : 0);
+                out.writeInt(wifiConfig.creatorUid);
+                out.writeInt(wifiConfig.lastConnectUid);
+                out.writeInt(wifiConfig.lastUpdateUid);
+                writeString(out, wifiConfig.creatorName);
+                writeString(out, wifiConfig.lastUpdateName);
+                out.writeLong(wifiConfig.blackListTimestamp);
+                out.writeLong(wifiConfig.lastConnectionFailure);
+                out.writeLong(wifiConfig.lastRoamingFailure);
+                out.writeInt(wifiConfig.lastRoamingFailureReason);
+                out.writeLong(wifiConfig.roamingFailureBlackListTimeMilli);
+                out.writeLong(wifiConfig.numConnectionFailures);
+                out.writeLong(wifiConfig.numIpConfigFailures);
+                out.writeInt(wifiConfig.numAuthFailures);
+                out.writeInt(wifiConfig.numScorerOverride);
+                out.writeInt(wifiConfig.numScorerOverrideAndSwitchedNetwork);
+                out.writeInt(wifiConfig.numAssociation);
+                out.writeInt(wifiConfig.numUserTriggeredWifiDisableLowRSSI);
+                out.writeInt(wifiConfig.numUserTriggeredWifiDisableBadRSSI);
+                out.writeInt(wifiConfig.numUserTriggeredWifiDisableNotHighRSSI);
+                out.writeInt(wifiConfig.numTicksAtLowRSSI);
+                out.writeInt(wifiConfig.numTicksAtBadRSSI);
+                out.writeInt(wifiConfig.numTicksAtNotHighRSSI);
+                out.writeInt(wifiConfig.numUserTriggeredJoinAttempts);
+                out.writeInt(wifiConfig.autoJoinUseAggressiveJoinAttemptThreshold);
+                out.writeInt(wifiConfig.autoJoinBailedDueToLowRssi ? 1 : 0);
+                out.writeInt(wifiConfig.userApproved);
+                out.writeInt(wifiConfig.numNoInternetAccessReports);
+                out.writeInt(wifiConfig.noInternetAccessExpected ? 1 : 0);
+            } catch (IOException ioe) {
+                Log.e(TAG, "Failed to Convert WifiConfiguration to byte array", ioe);
+                baos.reset();
+            }
+        }
+        return baos.toByteArray();
+    }
+
+    /**
+     * Unmarshals a byte array into a WifiConfig Object
+     *
+     * @param data - marshalled WifiConfig Object
+     * @return WifiConfiguration Object
+     */
+
+    public static WifiConfiguration unmarshalWifiConfig(byte[] data) {
+        if (data == null ||  data.length == 0) {
+            return null;
+        }
+        DataInputStream in = new DataInputStream(new ByteArrayInputStream(data));
+        WifiConfiguration config = new WifiConfiguration();
+        try {
+            int version = in.readInt();
+
+            config.networkId = in.readInt();
+            config.status = in.readInt();
+            config.disableReason = in.readInt();
+            config.SSID = readString(in, version);
+            config.BSSID = readString(in, version);
+            config.apBand = in.readInt();
+            config.apChannel = in.readInt();
+            config.autoJoinBSSID = readString(in, version);
+            config.FQDN = readString(in, version);
+            config.providerFriendlyName = readString(in, version);
+            int numRoamingConsortiumIds = in.readInt();
+            config.roamingConsortiumIds = new long[numRoamingConsortiumIds];
+            for (int i = 0; i < numRoamingConsortiumIds; i++) {
+                config.roamingConsortiumIds[i] = in.readLong();
+            }
+            config.preSharedKey = readString(in, version);
+            for (int i = 0; i < config.wepKeys.length; i++) {
+                config.wepKeys[i] = readString(in, version);
+            }
+            config.wepTxKeyIndex = in.readInt();
+            config.priority = in.readInt();
+            config.hiddenSSID = in.readInt() != 0;
+            config.requirePMF = in.readInt() != 0;
+            config.updateIdentifier = readString(in, version);
+
+            config.allowedKeyManagement = readBitSet(in, version);
+            config.allowedProtocols = readBitSet(in, version);
+            config.allowedAuthAlgorithms = readBitSet(in, version);
+            config.allowedPairwiseCiphers = readBitSet(in, version);
+            config.allowedGroupCiphers = readBitSet(in, version);
+
+            //Not backed-up because EnterpriseConfig involves
+            //Certificates which are device specific.
+            //config.enterpriseConfig = new WifiEnterpriseConfig();
+
+            config.setIpConfiguration(readIpConfiguration(in, version));
+
+
+            config.dhcpServer = readString(in, version);
+            config.defaultGwMacAddress = readString(in, version);
+            config.autoJoinStatus = in.readInt();
+            config.selfAdded = in.readInt() != 0;
+            config.didSelfAdd = in.readInt() != 0;
+            config.validatedInternetAccess = in.readInt() != 0;
+            config.ephemeral = in.readInt() != 0;
+            config.creatorUid = in.readInt();
+            config.lastConnectUid = in.readInt();
+            config.lastUpdateUid = in.readInt();
+            config.creatorName = readString(in, version);
+            config.lastUpdateName = readString(in, version);
+            config.blackListTimestamp = in.readLong();
+            config.lastConnectionFailure = in.readLong();
+            config.lastRoamingFailure = in.readLong();
+            config.lastRoamingFailureReason = in.readInt();
+            config.roamingFailureBlackListTimeMilli = in.readLong();
+            config.numConnectionFailures = in.readInt();
+            config.numIpConfigFailures = in.readInt();
+            config.numAuthFailures = in.readInt();
+            config.numScorerOverride = in.readInt();
+            config.numScorerOverrideAndSwitchedNetwork = in.readInt();
+            config.numAssociation = in.readInt();
+            config.numUserTriggeredWifiDisableLowRSSI = in.readInt();
+            config.numUserTriggeredWifiDisableBadRSSI = in.readInt();
+            config.numUserTriggeredWifiDisableNotHighRSSI = in.readInt();
+            config.numTicksAtLowRSSI = in.readInt();
+            config.numTicksAtBadRSSI = in.readInt();
+            config.numTicksAtNotHighRSSI = in.readInt();
+            config.numUserTriggeredJoinAttempts = in.readInt();
+            config.autoJoinUseAggressiveJoinAttemptThreshold = in.readInt();
+            config.autoJoinBailedDueToLowRssi = in.readInt() != 0;
+            config.userApproved = in.readInt();
+            config.numNoInternetAccessReports = in.readInt();
+            config.noInternetAccessExpected = in.readInt() != 0;
+        } catch (IOException ioe) {
+            Log.e(TAG, "Failed to convert byte array to WifiConfiguration object", ioe);
+            return null;
+        }
+        return config;
+    }
+
+    private static ProxyInfo readProxyInfo(DataInputStream in, int version) throws IOException {
+        int isNull = in.readByte();
+        if (isNull == NULL) return null;
+        String host = readString(in, version);
+        int port = in.readInt();
+        String exclusionList = readString(in, version);
+        return new ProxyInfo(host, port, exclusionList);
+    }
+
+    private static void writeProxyInfo(DataOutputStream out, ProxyInfo proxyInfo) throws IOException {
+        if (proxyInfo != null) {
+            out.writeByte(NOT_NULL);
+            writeString(out, proxyInfo.getHost());
+            out.writeInt(proxyInfo.getPort());
+            writeString(out, proxyInfo.getExclusionListAsString());
+        } else {
+            out.writeByte(NULL);
+        }
+    }
+
+    private static InetAddress readInetAddress(DataInputStream in, int version) throws IOException {
+        int isNull = in.readByte();
+        if (isNull == NULL) return null;
+        InetAddress address = null;
+        int addressLength = in.readInt();
+        if (addressLength < 1) return address;
+        byte[] addressBytes = new byte[addressLength];
+        in.read(addressBytes, 0, addressLength);
+        try {
+            address = InetAddress.getByAddress(addressBytes);
+        } catch (UnknownHostException unknownHostException) {
+            return null;
+        }
+        return address;
+    }
+
+    private static void writeInetAddress(DataOutputStream out, InetAddress address) throws IOException {
+        if (address.getAddress() != null) {
+            out.writeByte(NOT_NULL);
+            out.writeInt(address.getAddress().length);
+            out.write(address.getAddress(), 0, address.getAddress().length);
+        } else {
+            out.writeByte(NULL);
+        }
+    }
+
+    private static LinkAddress readLinkAddress(DataInputStream in, int version) throws IOException {
+        int isNull = in.readByte();
+        if (isNull == NULL) return null;
+        InetAddress address = readInetAddress(in, version);
+        int prefixLength = in.readInt();
+        int flags = in.readInt();
+        int scope = in.readInt();
+        return new LinkAddress(address, prefixLength, flags, scope);
+    }
+
+    private static void writeLinkAddress(DataOutputStream out, LinkAddress address) throws IOException {
+        if (address != null) {
+            out.writeByte(NOT_NULL);
+            writeInetAddress(out, address.getAddress());
+            out.writeInt(address.getPrefixLength());
+            out.writeInt(address.getFlags());
+            out.writeInt(address.getScope());
+        } else {
+            out.writeByte(NULL);
+        }
+    }
+
+    private static StaticIpConfiguration readStaticIpConfiguration(DataInputStream in, int version) throws IOException {
+        int isNull = in.readByte();
+        if (isNull == NULL) return null;
+        StaticIpConfiguration staticIpConfiguration = new StaticIpConfiguration();
+        staticIpConfiguration.ipAddress = readLinkAddress(in, version);
+        staticIpConfiguration.gateway = readInetAddress(in, version);
+        int dnsServersLength = in.readInt();
+        for (int i = 0; i < dnsServersLength; i++) {
+            staticIpConfiguration.dnsServers.add(readInetAddress(in, version));
+        }
+        staticIpConfiguration.domains = readString(in, version);
+        return staticIpConfiguration;
+    }
+
+    private static void writeStaticIpConfiguration(DataOutputStream out, StaticIpConfiguration staticIpConfiguration) throws IOException {
+        if (staticIpConfiguration != null) {
+            out.writeByte(NOT_NULL);
+            writeLinkAddress(out, staticIpConfiguration.ipAddress);
+            writeInetAddress(out, staticIpConfiguration.gateway);
+            out.writeInt(staticIpConfiguration.dnsServers.size());
+            for (InetAddress inetAddress : staticIpConfiguration.dnsServers) {
+                writeInetAddress(out, inetAddress);
+            }
+            writeString(out, staticIpConfiguration.domains);
+        } else {
+            out.writeByte(NULL);
+        }
+    }
+
+    private static IpConfiguration readIpConfiguration(DataInputStream in, int version) throws IOException {
+        int isNull = in.readByte();
+        if (isNull == NULL) return null;
+        IpConfiguration ipConfiguration = new IpConfiguration();
+        String tmp = readString(in, version);
+        ipConfiguration.ipAssignment = tmp == null ? null : IpConfiguration.IpAssignment.valueOf(tmp);
+        tmp = readString(in, version);
+        ipConfiguration.proxySettings = tmp == null ? null : IpConfiguration.ProxySettings.valueOf(tmp);
+        ipConfiguration.staticIpConfiguration = readStaticIpConfiguration(in, version);
+        ipConfiguration.httpProxy = readProxyInfo(in, version);
+        return ipConfiguration;
+    }
+
+
+    private static void writeIpConfiguration(DataOutputStream out, IpConfiguration ipConfiguration) throws IOException {
+        if (ipConfiguration != null) {
+            out.writeByte(NOT_NULL);
+            writeString(out, ipConfiguration.ipAssignment != null ? ipConfiguration.ipAssignment.name() : null);
+            writeString(out, ipConfiguration.proxySettings != null ? ipConfiguration.proxySettings.name() : null);
+            writeStaticIpConfiguration(out, ipConfiguration.staticIpConfiguration);
+            writeProxyInfo(out, ipConfiguration.httpProxy);
+        } else {
+            out.writeByte(NULL);
+        }
+
+    }
+
+    private static String readString(DataInputStream in, int version) throws IOException {
+        byte isNull = in.readByte();
+        if (isNull == NOT_NULL) {
+            return in.readUTF();
+        }
+        return null;
+    }
+
+    private static void writeString(DataOutputStream out, String val) throws IOException {
+        if (val != null) {
+            out.writeByte(NOT_NULL);
+            out.writeUTF(val);
+        } else {
+            out.writeByte(NULL);
+        }
+    }
+
+    private static BitSet readBitSet(DataInputStream in, int version) throws IOException {
+        byte isNull = in.readByte();
+        if (isNull == NOT_NULL) {
+            int length = in.readInt();
+            byte[] bytes = new byte[length];
+            in.read(bytes, 0, length);
+            return BitSet.valueOf(bytes);
+        }
+        return new BitSet();
+    }
+
+    private static void writeBitSet(DataOutputStream out, BitSet val) throws IOException {
+        if (val != null) {
+            out.writeByte(NOT_NULL);
+            byte[] byteArray = val.toByteArray();
+            out.writeInt(byteArray.length);
+            out.write(byteArray);
+        } else {
+            out.writeByte(NULL);
+        }
+    }
+}
diff --git a/packages/SettingsProvider/test/Android.mk b/packages/SettingsProvider/test/Android.mk
index ef863e7..f278967 100644
--- a/packages/SettingsProvider/test/Android.mk
+++ b/packages/SettingsProvider/test/Android.mk
@@ -5,7 +5,10 @@
 # Note we statically link SettingsState to do some unit tests.  It's not accessible otherwise
 # because this test is not an instrumentation test. (because the target runs in the system process.)
 LOCAL_SRC_FILES := $(call all-subdir-java-files) \
-    ../src/com/android/providers/settings/SettingsState.java
+    ../src/com/android/providers/settings/SettingsState.java \
+    ../src/com/android/providers/settings/WiFiConfigurationSerializer.java \
+    ../src/com/android/providers/settings/NetworkPolicySerializer.java
+
 
 LOCAL_PACKAGE_NAME := SettingsProviderTest
 
@@ -13,4 +16,4 @@
 
 LOCAL_CERTIFICATE := platform
 
-include $(BUILD_PACKAGE)
\ No newline at end of file
+include $(BUILD_PACKAGE)
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/NetworkPolicySerializerTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/NetworkPolicySerializerTest.java
new file mode 100644
index 0000000..1986596
--- /dev/null
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/NetworkPolicySerializerTest.java
@@ -0,0 +1,117 @@
+package com.android.providers.settings;
+
+import android.net.NetworkPolicy;
+import android.net.NetworkTemplate;
+import android.test.AndroidTestCase;
+
+import java.util.Random;
+
+/**
+ * Tests for NetworkPolicySerializer
+ */
+public class NetworkPolicySerializerTest extends AndroidTestCase {
+    static Random sRandom = new Random();
+
+    public void testMarshallAndUnmarshalNetworkPolicy() {
+        NetworkPolicy policy = getDummyNetworkPolicy();
+        byte[] data = NetworkPolicySerializer.marshalNetworkPolicy(policy);
+        assertNotNull("Got Null data from marshal", data);
+        assertFalse("Got back an empty byte[] from marshal", data.length == 0);
+
+        NetworkPolicy unmarshaled = NetworkPolicySerializer.unmarshalNetworkPolicy(data);
+        assertNotNull("Got Null data from unmarshaled", unmarshaled);
+        assertTrue("NetworkPolicy Marshall and Unmarshal Failed!", policy.equals(unmarshaled));
+    }
+
+    public void testMarshallNetworkPolicyEdgeCases() {
+        byte[] data = NetworkPolicySerializer.marshalNetworkPolicy(null);
+        assertNotNull("NetworkPolicy marshal returned null. Expected: byte[0]", data);
+        assertEquals("NetworkPolicy marshal returned incomplete byte array. Expected: byte[0]",
+                data.length, 0);
+    }
+
+    public void testUnmarshallNetworkPolicyEdgeCases() {
+        NetworkPolicy policy = NetworkPolicySerializer.unmarshalNetworkPolicy(null);
+        assertNull("Non null NetworkPolicy returned for null byte[] input", policy);
+
+        policy = NetworkPolicySerializer.unmarshalNetworkPolicy(new byte[0]);
+        assertNull("Non null NetworkPolicy returned for empty byte[] input", policy);
+
+        policy = NetworkPolicySerializer.unmarshalNetworkPolicy(new byte[]{10, 20, 30, 40, 50, 60});
+        assertNull("Non null NetworkPolicy returned for incomplete byte[] input", policy);
+    }
+
+    public void testMarshallAndUnmarshalNetworkPolicies() {
+        NetworkPolicy[] policies = getDummyNetworkPolicies(5);
+        byte[] data = NetworkPolicySerializer.marshalNetworkPolicies(policies);
+        assertNotNull("Got Null data from marshal", data);
+        assertFalse("Got back an empty byte[] from marshal", data.length == 0);
+
+        NetworkPolicy[] unmarshaled = NetworkPolicySerializer.unmarshalNetworkPolicies(data);
+        assertNotNull("Got Null data from unmarshaled", unmarshaled);
+        try {
+            for (int i = 0; i < policies.length; i++) {
+                assertTrue("NetworkPolicies Marshall and Unmarshal Failed!",
+                        policies[i].equals(unmarshaled[i]));
+            }
+        } catch (NullPointerException npe) {
+            assertTrue("Some policies were not marshaled/unmarshaled correctly", false);
+        }
+    }
+
+    public void testMarshallNetworkPoliciesEdgeCases() {
+        byte[] data = NetworkPolicySerializer.marshalNetworkPolicies(null);
+        assertNotNull("NetworkPolicies marshal returned null!", data);
+        assertEquals("NetworkPolicies marshal returned incomplete byte array", data.length, 0);
+
+        data = NetworkPolicySerializer.marshalNetworkPolicies(new NetworkPolicy[0]);
+        assertNotNull("NetworkPolicies marshal returned null for empty NetworkPolicy[]", data);
+        assertEquals("NetworkPolicies marshal returned incomplete byte array for empty NetworkPolicy[]"
+                , data.length, 0);
+    }
+
+    public void testUnmarshalNetworkPoliciesEdgeCases() {
+        NetworkPolicy[] policies = NetworkPolicySerializer.unmarshalNetworkPolicies(null);
+        assertNotNull("NetworkPolicies unmarshal returned null for null input. Expected: byte[0] ",
+                policies);
+        assertEquals("Non Empty NetworkPolicy[] returned for null input Expected: byte[0]",
+                policies.length, 0);
+
+        policies = NetworkPolicySerializer.unmarshalNetworkPolicies(new byte[0]);
+        assertNotNull("NetworkPolicies unmarshal returned null for empty byte[] input. Expected: byte[0]",
+                policies);
+        assertEquals("Non Empty NetworkPolicy[] returned for empty byte[] input. Expected: byte[0]",
+                policies.length, 0);
+
+        policies = NetworkPolicySerializer.unmarshalNetworkPolicies(new byte[]{10, 20, 30, 40, 50, 60});
+        assertNotNull("NetworkPolicies unmarshal returned null for incomplete byte[] input. " +
+                "Expected: byte[0] ", policies);
+        assertEquals("Non Empty NetworkPolicy[] returned for incomplete byte[] input Expected: byte[0]",
+                policies.length, 0);
+
+    }
+
+    private NetworkPolicy[] getDummyNetworkPolicies(int num) {
+        NetworkPolicy[] policies = new NetworkPolicy[num];
+        for (int i = 0; i < num; i++) {
+            policies[i] = getDummyNetworkPolicy();
+        }
+        return policies;
+    }
+
+    private NetworkPolicy getDummyNetworkPolicy() {
+        NetworkTemplate template = new NetworkTemplate(NetworkTemplate.MATCH_MOBILE_ALL, "subId",
+                "GoogleGuest");
+        int cycleDay = sRandom.nextInt();
+        String cycleTimezone = "timezone";
+        long warningBytes = sRandom.nextLong();
+        long limitBytes = sRandom.nextLong();
+        long lastWarningSnooze = sRandom.nextLong();
+        long lastLimitSnooze = sRandom.nextLong();
+        boolean metered = sRandom.nextInt() % 2 == 0;
+        boolean inferred = sRandom.nextInt() % 2 == 0;
+        return new NetworkPolicy(template, cycleDay, cycleTimezone, warningBytes, limitBytes,
+                lastWarningSnooze, lastLimitSnooze, metered, inferred);
+    }
+
+}
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 78e373b..b604768 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -66,7 +66,7 @@
 import android.widget.Toast;
 
 /**
- * Service used to keep progress of bug reports processes ({@code dumpstate}).
+ * Service used to keep progress of bugreport processes ({@code dumpstate}).
  * <p>
  * The workflow is:
  * <ol>
@@ -84,7 +84,7 @@
  * <li>{@link BugreportReceiver} receives the intent and delegates it to this service, which in
  * turn:
  * <ol>
- * <li>Updates the system notification so user can share the bug report.
+ * <li>Updates the system notification so user can share the bugreport.
  * <li>Stops monitoring that {@code dumpstate} process.
  * <li>Stops itself if it doesn't have any process left to monitor.
  * </ol>
@@ -96,9 +96,13 @@
 
     private static final String AUTHORITY = "com.android.shell";
 
+    // External intents sent by dumpstate.
     static final String INTENT_BUGREPORT_STARTED = "android.intent.action.BUGREPORT_STARTED";
     static final String INTENT_BUGREPORT_FINISHED = "android.intent.action.BUGREPORT_FINISHED";
+
+    // Internal intents used on notification actions.
     static final String INTENT_BUGREPORT_CANCEL = "android.intent.action.BUGREPORT_CANCEL";
+    static final String INTENT_BUGREPORT_SHARE = "android.intent.action.BUGREPORT_SHARE";
 
     static final String EXTRA_BUGREPORT = "android.intent.extra.BUGREPORT";
     static final String EXTRA_SCREENSHOT = "android.intent.extra.SCREENSHOT";
@@ -111,7 +115,7 @@
     private static final int MSG_POLL = 2;
 
     /** Polling frequency, in milliseconds. */
-    private static final long POLLING_FREQUENCY = 500;
+    static final long POLLING_FREQUENCY = 2000;
 
     /** How long (in ms) a dumpstate process will be monitored if it didn't show progress. */
     private static final long INACTIVITY_TIMEOUT = 3 * DateUtils.MINUTE_IN_MILLIS;
@@ -169,10 +173,16 @@
 
     @Override
     protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
-        writer.printf("Monitored dumpstate processes: \n");
         synchronized (mProcesses) {
-            for (int i = 0; i < mProcesses.size(); i++) {
-              writer.printf("\t%s\n", mProcesses.valueAt(i));
+            final int size = mProcesses.size();
+            if (size == 0) {
+                writer.printf("No monitored processes");
+                return;
+            }
+            writer.printf("Monitored dumpstate processes\n");
+            writer.printf("-----------------------------\n");
+            for (int i = 0; i < size; i++) {
+              writer.printf("%s\n", mProcesses.valueAt(i));
             }
         }
     }
@@ -180,7 +190,6 @@
     private final class ServiceHandler extends Handler {
         public ServiceHandler(Looper looper) {
             super(looper);
-            poll();
         }
 
         @Override
@@ -196,25 +205,24 @@
                 return;
             }
 
-            // At this point it's handling onStartCommand(), whose intent contains the extras
-            // originally received by BugreportReceiver.
+            // At this point it's handling onStartCommand(), with the intent passed as an Extra.
             if (!(msg.obj instanceof Intent)) {
                 // Sanity check.
                 Log.e(TAG, "Internal error: invalid msg.obj: " + msg.obj);
                 return;
             }
             final Parcelable parcel = ((Intent) msg.obj).getParcelableExtra(EXTRA_ORIGINAL_INTENT);
-            if (!(parcel instanceof Intent)) {
-                // Sanity check.
-                Log.e(TAG, "Internal error: msg.obj is missing extra " + EXTRA_ORIGINAL_INTENT);
-                return;
+            final Intent intent;
+            if (parcel instanceof Intent) {
+                // The real intent was passed to BugreportReceiver, which delegated to the service.
+                intent = (Intent) parcel;
+            } else {
+                intent = (Intent) msg.obj;
             }
-
-            final Intent intent = (Intent) parcel;
             final String action = intent.getAction();
-            int pid = intent.getIntExtra(EXTRA_PID, 0);
-            int max = intent.getIntExtra(EXTRA_MAX, -1);
-            String name = intent.getStringExtra(EXTRA_NAME);
+            final int pid = intent.getIntExtra(EXTRA_PID, 0);
+            final int max = intent.getIntExtra(EXTRA_MAX, -1);
+            final String name = intent.getStringExtra(EXTRA_NAME);
 
             if (DEBUG) Log.v(TAG, "action: " + action + ", name: " + name + ", pid: " + pid
                     + ", max: "+ max);
@@ -224,14 +232,18 @@
                         stopSelfWhenDone();
                         return;
                     }
+                    poll();
                     break;
                 case INTENT_BUGREPORT_FINISHED:
-                    if (pid == -1) {
+                    if (pid == 0) {
                         // Shouldn't happen, unless BUGREPORT_FINISHED is received from a legacy,
                         // out-of-sync dumpstate process.
                         Log.w(TAG, "Missing " + EXTRA_PID + " on intent " + intent);
                     }
-                    stopProgress(pid, intent);
+                    onBugreportFinished(pid, intent);
+                    break;
+                case INTENT_BUGREPORT_SHARE:
+                    shareBugreport(pid);
                     break;
                 case INTENT_BUGREPORT_CANCEL:
                     cancel(pid);
@@ -247,6 +259,8 @@
             if (pollProgress()) {
                 // Keep polling...
                 sendEmptyMessageDelayed(MSG_POLL, POLLING_FREQUENCY);
+            } else {
+                Log.i(TAG, "Stopped polling");
             }
         }
     }
@@ -283,7 +297,7 @@
     }
 
     /**
-     * Updates the system notification for a given bug report.
+     * Updates the system notification for a given bugreport.
      */
     private void updateProgress(BugreportInfo info) {
         if (info.max <= 0 || info.progress < 0) {
@@ -296,14 +310,8 @@
         nf.setMinimumFractionDigits(2);
         nf.setMaximumFractionDigits(2);
         final String percentText = nf.format((double) info.progress / info.max);
-
-        final Intent cancelIntent = new Intent(context, BugreportReceiver.class);
-        cancelIntent.setAction(INTENT_BUGREPORT_CANCEL);
-        cancelIntent.putExtra(EXTRA_PID, info.pid);
-        final Action cancelAction = new Action.Builder(null,
-                context.getString(com.android.internal.R.string.cancel),
-                PendingIntent.getBroadcast(context, info.pid, cancelIntent,
-                        PendingIntent.FLAG_CANCEL_CURRENT)).build();
+        final Action cancelAction = new Action.Builder(null, context.getString(
+                com.android.internal.R.string.cancel), newCancelIntent(context, info)).build();
 
         final String title = context.getString(R.string.bugreport_in_progress_title);
         final String name =
@@ -327,9 +335,19 @@
     }
 
     /**
-     * Finalizes the progress on a given process and sends the finished intent.
+     * Creates a {@link PendingIntent} for a notification action used to cancel a bugreport.
      */
-    private void stopProgress(int pid, Intent intent) {
+    private static PendingIntent newCancelIntent(Context context, BugreportInfo info) {
+        final Intent intent = new Intent(INTENT_BUGREPORT_CANCEL);
+        intent.setClass(context, BugreportProgressService.class);
+        intent.putExtra(EXTRA_PID, info.pid);
+        return PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+    }
+
+    /**
+     * Finalizes the progress on a given bugreport and cancel its notification.
+     */
+    private void stopProgress(int pid) {
         synchronized (mProcesses) {
             if (mProcesses.indexOfKey(pid) < 0) {
                 Log.w(TAG, "PID not watched: " + pid);
@@ -340,11 +358,6 @@
         }
         if (DEBUG) Log.v(TAG, "stopProgress(" + pid + "): cancel notification");
         NotificationManager.from(getApplicationContext()).cancel(TAG, pid);
-        if (intent != null) {
-            // Bug report finished fine: send a new, different notification.
-            if (DEBUG) Log.v(TAG, "stopProgress(" + pid + "): finish bug report");
-            onBugreportFinished(pid, intent);
-        }
     }
 
     /**
@@ -353,7 +366,7 @@
     private void cancel(int pid) {
         Log.i(TAG, "Cancelling PID " + pid + " on user's request");
         SystemProperties.set(CTL_STOP, BUGREPORT_SERVICE);
-        stopProgress(pid, null);
+        stopProgress(pid);
     }
 
     /**
@@ -363,11 +376,19 @@
      */
     private boolean pollProgress() {
         synchronized (mProcesses) {
-            if (mProcesses.size() == 0) {
+            final int total = mProcesses.size();
+            if (total == 0) {
                 Log.d(TAG, "No process to poll progress.");
             }
-            for (int i = 0; i < mProcesses.size(); i++) {
+            int activeProcesses = 0;
+            for (int i = 0; i < total; i++) {
                 final int pid = mProcesses.keyAt(i);
+                final BugreportInfo info = mProcesses.valueAt(i);
+                if (info.finished) {
+                    if (DEBUG) Log.v(TAG, "Skipping finished process " + pid);
+                    continue;
+                }
+                activeProcesses++;
                 final String progressKey = DUMPSTATE_PREFIX + pid + PROGRESS_SUFFIX;
                 final int progress = SystemProperties.getInt(progressKey, 0);
                 if (progress == 0) {
@@ -375,7 +396,6 @@
                     continue;
                 }
                 final int max = SystemProperties.getInt(DUMPSTATE_PREFIX + pid + MAX_SUFFIX, 0);
-                final BugreportInfo info = mProcesses.valueAt(i);
                 final boolean maxChanged = max > 0 && max != info.max;
                 final boolean progressChanged = progress > 0 && progress != info.progress;
 
@@ -397,11 +417,12 @@
                     if (inactiveTime >= INACTIVITY_TIMEOUT) {
                         Log.w(TAG, "No progress update for process " + pid + " since "
                                 + info.getFormattedLastUpdate());
-                        stopProgress(info.pid, null);
+                        stopProgress(info.pid);
                     }
                 }
             }
-            return true;
+            if (DEBUG) Log.v(TAG, "pollProgress() total=" + total + ", actives=" + activeProcesses);
+            return activeProcesses > 0;
         }
     }
 
@@ -421,37 +442,46 @@
 
     private void onBugreportFinished(int pid, Intent intent) {
         final Context context = getApplicationContext();
-        final Configuration conf = context.getResources().getConfiguration();
-        final File bugreportFile = getFileExtra(intent, EXTRA_BUGREPORT);
-        final File screenshotFile = getFileExtra(intent, EXTRA_SCREENSHOT);
+        BugreportInfo info;
+        synchronized (mProcesses) {
+            info = mProcesses.get(pid);
+            if (info == null) {
+                // Happens when BUGREPORT_FINISHED was received without a BUGREPORT_STARTED
+                Log.v(TAG, "Creating info for untracked pid " + pid);
+                info = new BugreportInfo(context, pid);
+                mProcesses.put(pid, info);
+            }
+            info.bugreportFile = getFileExtra(intent, EXTRA_BUGREPORT);
+            info.screenshotFile = getFileExtra(intent, EXTRA_SCREENSHOT);
+        }
 
+        final Configuration conf = context.getResources().getConfiguration();
         if ((conf.uiMode & Configuration.UI_MODE_TYPE_MASK) != Configuration.UI_MODE_TYPE_WATCH) {
-            triggerLocalNotification(context, pid, bugreportFile, screenshotFile);
+            triggerLocalNotification(context, info);
         }
     }
 
     /**
      * Responsible for triggering a notification that allows the user to start a "share" intent with
-     * the bug report. On watches we have other methods to allow the user to start this intent
+     * the bugreport. On watches we have other methods to allow the user to start this intent
      * (usually by triggering it on another connected device); we don't need to display the
      * notification in this case.
      */
-    private static void triggerLocalNotification(final Context context, final int pid,
-            final File bugreportFile, final File screenshotFile) {
-        if (!bugreportFile.exists() || !bugreportFile.canRead()) {
-            Log.e(TAG, "Could not read bugreport file " + bugreportFile);
+    private static void triggerLocalNotification(final Context context, final BugreportInfo info) {
+        if (!info.bugreportFile.exists() || !info.bugreportFile.canRead()) {
+            Log.e(TAG, "Could not read bugreport file " + info.bugreportFile);
             Toast.makeText(context, context.getString(R.string.bugreport_unreadable_text),
                     Toast.LENGTH_LONG).show();
             return;
         }
 
-        boolean isPlainText = bugreportFile.getName().toLowerCase().endsWith(".txt");
+        boolean isPlainText = info.bugreportFile.getName().toLowerCase().endsWith(".txt");
         if (!isPlainText) {
             // Already zipped, send it right away.
-            sendBugreportNotification(context, pid, bugreportFile, screenshotFile);
+            sendBugreportNotification(context, info);
         } else {
             // Asynchronously zip the file first, then send it.
-            sendZippedBugreportNotification(context, pid, bugreportFile, screenshotFile);
+            sendZippedBugreportNotification(context, info);
         }
     }
 
@@ -498,17 +528,27 @@
     }
 
     /**
-     * Sends a bugreport notitication.
+     * Shares the bugreport upon user's request by issuing a {@link Intent#ACTION_SEND_MULTIPLE}
+     * intent, but issuing a warning dialog the first time.
      */
-    private static void sendBugreportNotification(Context context, int pid, File bugreportFile,
-            File screenshotFile) {
+    private void shareBugreport(int pid) {
+        final Context context = getApplicationContext();
+        final BugreportInfo info;
+        synchronized (mProcesses) {
+            info = mProcesses.get(pid);
+            if (info == null) {
+                // Should not happen, so log if it does...
+                Log.e(TAG, "INTERNAL ERROR: no info for PID " + pid + ": " + mProcesses);
+                return;
+            }
+        }
         // Files are kept on private storage, so turn into Uris that we can
         // grant temporary permissions for.
-        final Uri bugreportUri = getUri(context, bugreportFile);
-        final Uri screenshotUri = getUri(context, screenshotFile);
+        final Uri bugreportUri = getUri(context, info.bugreportFile);
+        final Uri screenshotUri = getUri(context, info.screenshotFile);
 
-        Intent sendIntent = buildSendIntent(context, bugreportUri, screenshotUri);
-        Intent notifIntent;
+        final Intent sendIntent = buildSendIntent(context, bugreportUri, screenshotUri);
+        final Intent notifIntent;
 
         // Send through warning dialog by default
         if (getWarningState(context, STATE_SHOW) == STATE_SHOW) {
@@ -518,32 +558,48 @@
         }
         notifIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
+        // Send the share intent...
+        context.startActivity(notifIntent);
+
+        // ... and stop watching this process.
+        stopProgress(pid);
+    }
+
+    /**
+     * Sends a notitication indicating the bugreport has finished so use can share it.
+     */
+    private static void sendBugreportNotification(Context context, BugreportInfo info) {
+        final Intent shareIntent = new Intent(INTENT_BUGREPORT_SHARE);
+        shareIntent.setClass(context, BugreportProgressService.class);
+        shareIntent.setAction(INTENT_BUGREPORT_SHARE);
+        shareIntent.putExtra(EXTRA_PID, info.pid);
+
         final String title = context.getString(R.string.bugreport_finished_title);
         final Notification.Builder builder = new Notification.Builder(context)
                 .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
                 .setContentTitle(title)
                 .setTicker(title)
                 .setContentText(context.getString(R.string.bugreport_finished_text))
-                .setContentIntent(PendingIntent.getActivity(
-                        context, 0, notifIntent, PendingIntent.FLAG_CANCEL_CURRENT))
-                .setAutoCancel(true)
+                .setContentIntent(PendingIntent.getService(context, 0, shareIntent,
+                        PendingIntent.FLAG_CANCEL_CURRENT))
+                .setDeleteIntent(newCancelIntent(context, info))
                 .setLocalOnly(true)
                 .setColor(context.getColor(
                         com.android.internal.R.color.system_notification_accent_color));
 
-        NotificationManager.from(context).notify(TAG, pid, builder.build());
+        NotificationManager.from(context).notify(TAG, info.pid, builder.build());
     }
 
     /**
      * Sends a zipped bugreport notification.
      */
     private static void sendZippedBugreportNotification(final Context context,
-            final int pid, final File bugreportFile, final File screenshotFile) {
+            final BugreportInfo info) {
         new AsyncTask<Void, Void, Void>() {
             @Override
             protected Void doInBackground(Void... params) {
-                File zippedFile = zipBugreport(bugreportFile);
-                sendBugreportNotification(context, pid, zippedFile, screenshotFile);
+                info.bugreportFile = zipBugreport(info.bugreportFile);
+                sendBugreportNotification(context, info);
                 return null;
             }
         }.execute();
@@ -629,31 +685,31 @@
     }
 
     /**
-     * Information about a bug report process while its in progress.
+     * Information about a bugreport process while its in progress.
      */
     private static final class BugreportInfo {
         private final Context context;
 
         /**
-         * {@code pid} of the {@code dumpstate} process generating the bug report.
+         * {@code pid} of the {@code dumpstate} process generating the bugreport.
          */
         final int pid;
 
         /**
-         * Name of the bug report, will be used to rename the final files.
+         * Name of the bugreport, will be used to rename the final files.
          * <p>
-         * Initial value is the bug report filename reported by {@code dumpstate}, but user can
+         * Initial value is the bugreport filename reported by {@code dumpstate}, but user can
          * change it later to a more meaningful name.
          */
         String name;
 
         /**
-         * Maximum progress of the bug report generation.
+         * Maximum progress of the bugreport generation.
          */
         int max;
 
         /**
-         * Current progress of the bug report generation.
+         * Current progress of the bugreport generation.
          */
         int progress;
 
@@ -662,6 +718,24 @@
          */
         long lastUpdate = System.currentTimeMillis();
 
+        /**
+         * Path of the main bugreport file.
+         */
+        File bugreportFile;
+
+        /**
+         * Path of the screenshot file.
+         */
+        File screenshotFile;
+
+        /**
+         * Whether dumpstate sent an intent informing it has finished.
+         */
+        boolean finished;
+
+        /**
+         * Constructor for tracked bugreports - typically called upon receiving BUGREPORT_STARTED.
+         */
         BugreportInfo(Context context, int pid, String name, int max) {
             this.context = context;
             this.pid = pid;
@@ -669,6 +743,15 @@
             this.max = max;
         }
 
+        /**
+         * Constructor for untracked bugreports - typically called upon receiving BUGREPORT_FINISHED
+         * without a previous call to BUGREPORT_STARTED.
+         */
+        BugreportInfo(Context context, int pid) {
+            this(context, pid, null, 0);
+            this.finished = true;
+        }
+
         String getFormattedLastUpdate() {
             return DateUtils.formatDateTime(context, lastUpdate,
                     DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME);
@@ -677,8 +760,10 @@
         @Override
         public String toString() {
             final float percent = ((float) progress * 100 / max);
-            return "Progress for " + name + " (pid=" + pid + "): " + progress + "/" + max
-                    + " (" + percent + "%) Last update: " + getFormattedLastUpdate();
+            return "pid: " + pid + ", name: " + name + ", finished: " + finished
+                    + "\n\tfile: " + bugreportFile + "\n\tscreenshot: " + screenshotFile
+                    + "\n\tprogress: " + progress + "/" + max + "(" + percent + ")"
+                    + "\n\tlast_update: " + getFormattedLastUpdate();
         }
     }
 }
diff --git a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
index 0e31cdf..cd0fcfe 100644
--- a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
+++ b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
@@ -40,6 +40,8 @@
 import java.util.zip.ZipOutputStream;
 
 import libcore.io.Streams;
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningServiceInfo;
 import android.app.Instrumentation;
 import android.app.NotificationManager;
 import android.content.Context;
@@ -51,6 +53,7 @@
 import android.support.test.uiautomator.UiDevice;
 import android.support.test.uiautomator.UiObject;
 import android.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
 import android.util.Log;
 
 import com.android.shell.ActionSendMultipleConsumerActivity.CustomActionSendMultipleListener;
@@ -71,12 +74,13 @@
  * <p>
  * <strong>NOTE</strong>: these tests only work if the device is unlocked.
  */
+@LargeTest
 public class BugreportReceiverTest extends InstrumentationTestCase {
 
     private static final String TAG = "BugreportReceiverTest";
 
     // Timeout for UI operations, in milliseconds.
-    private static final int TIMEOUT = 1000;
+    private static final int TIMEOUT = (int) BugreportProgressService.POLLING_FREQUENCY * 4;
 
     private static final String ROOT_DIR = "/data/data/com.android.shell/files/bugreports";
     private static final String BUGREPORT_FILE = "test_bugreport.txt";
@@ -130,7 +134,8 @@
         Bundle extras = sendBugreportFinishedIntent(42, PLAIN_TEXT_PATH, SCREENSHOT_PATH);
         assertActionSendMultiple(extras, BUGREPORT_CONTENT, SCREENSHOT_CONTENT);
 
-        // TODO: assert service is down
+        String service = BugreportProgressService.class.getName();
+        assertFalse("Service '" + service + "' is still running", isServiceRunning(service));
     }
 
     public void testBugreportFinished_withWarning() throws Exception {
@@ -306,6 +311,17 @@
         fail("Did not find entry '" + entryName + "' on file '" + uri + "'");
     }
 
+    private boolean isServiceRunning(String name) {
+        ActivityManager manager = (ActivityManager) mContext
+                .getSystemService(Context.ACTIVITY_SERVICE);
+        for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
+            if (service.service.getClassName().equals(name)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private static void createTextFile(String path, String content) throws IOException {
         Log.v(TAG, "createFile(" + path + ")");
         try (Writer writer = new BufferedWriter(new OutputStreamWriter(
diff --git a/packages/Shell/tests/src/com/android/shell/UiBot.java b/packages/Shell/tests/src/com/android/shell/UiBot.java
index 5e8bab1..7d37137 100644
--- a/packages/Shell/tests/src/com/android/shell/UiBot.java
+++ b/packages/Shell/tests/src/com/android/shell/UiBot.java
@@ -118,7 +118,8 @@
     // TODO: UI Automator should provide such logic.
     public void chooseActivity(String name) {
         // First check if the activity is the default option.
-        String shareText = String.format("Share with %s", name);
+        String shareText = "Share with " + name;
+        Log.v(TAG, "Waiting for ActivityChooser text: '" + shareText + "'");
         boolean gotIt = mDevice.wait(Until.hasObject(By.text(shareText)), mTimeout);
 
         if (gotIt) {
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 2e65656..51b84f5 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -136,6 +136,9 @@
             android:protectionLevel="signature" />
     <uses-permission android:name="com.android.systemui.permission.SELF" />
 
+    <!-- Adding Quick Settings tiles -->
+    <uses-permission android:name="android.permission.BIND_QUICK_SETTINGS_TILE" />
+
     <application
         android:name=".SystemUIApplication"
         android:persistent="true"
diff --git a/packages/SystemUI/res/color/remote_input_send.xml b/packages/SystemUI/res/color/remote_input_send.xml
new file mode 100644
index 0000000..fe2ffaa
--- /dev/null
+++ b/packages/SystemUI/res/color/remote_input_send.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 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
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="true" android:color="@android:color/white" />
+    <item android:color="#4dffffff" /> <!-- 30% white -->
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/remote_input_text.xml b/packages/SystemUI/res/color/remote_input_text.xml
new file mode 100644
index 0000000..11ce0b7
--- /dev/null
+++ b/packages/SystemUI/res/color/remote_input_text.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 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
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="true" android:color="@android:color/white" />
+    <item android:color="#99ffffff" /> <!-- 60% white -->
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/header_dot.xml b/packages/SystemUI/res/drawable/header_dot.xml
new file mode 100644
index 0000000..568a9c2
--- /dev/null
+++ b/packages/SystemUI/res/drawable/header_dot.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+** Copyright 2015, 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.
+-->
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="oval">
+
+    <solid 
+        android:color="#FFFFFF"/>
+
+    <size 
+        android:width="3dp"
+        android:height="3dp"/>
+</shape>
diff --git a/packages/SystemUI/res/drawable/recents_freeform_workspace_bg.xml b/packages/SystemUI/res/drawable/recents_freeform_workspace_bg.xml
new file mode 100644
index 0000000..5f9341c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recents_freeform_workspace_bg.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <corners android:topLeftRadius="@dimen/recents_task_view_rounded_corners_radius"
+             android:topRightRadius="@dimen/recents_task_view_rounded_corners_radius"/>
+    <solid android:color="#00000000" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 1873168..bb37b83 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -19,7 +19,6 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:background="@drawable/qs_background_primary"
-        android:paddingTop="8dp"
         android:paddingBottom="8dp"
         android:elevation="2dp">
 
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index 8124eb7..a995ec7 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -33,16 +33,6 @@
     android:focusable="true"
     >
 
-    <com.android.systemui.qs.QuickQSPanel
-        android:id="@+id/quick_qs_panel"
-        android:background="#0000"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:layout_alignParentEnd="true"
-        android:clipChildren="false"
-        android:clipToPadding="false"
-        android:layout_marginEnd="12dp" />
-
     <LinearLayout
         android:id="@+id/expanded_group"
         android:layout_width="wrap_content"
@@ -52,7 +42,8 @@
         android:clipToPadding="false"
         android:orientation="horizontal"
         android:layout_alignParentEnd="true"
-        android:layout_marginEnd="12dp">
+        android:layout_marginTop="30dp"
+        android:layout_marginEnd="16dp">
 
         <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
             android:id="@+id/settings_button_container"
@@ -87,48 +78,76 @@
             android:tint="@android:color/white" />
     </LinearLayout>
 
-    <FrameLayout
-        android:id="@+id/date_group"
+    <TextView
+        android:id="@+id/header_emergency_calls_only"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:layout_alignParentStart="true"
+        android:layout_alignParentTop="true"
+        android:paddingStart="16dp"
+        android:paddingEnd="16dp"
+        android:paddingTop="8dp"
+        android:visibility="gone"
+        android:textAppearance="@style/TextAppearance.StatusBar.Expanded.EmergencyCallsOnly"
+        android:text="@*android:string/emergency_calls_only"
+        android:singleLine="true"
+        android:gravity="center_vertical" />
+
+    <LinearLayout
+        android:id="@+id/date_time_group"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginBottom="@dimen/clock_collapsed_bottom_margin"
-        android:layout_alignParentBottom="true">
-        <com.android.systemui.statusbar.policy.DateView android:id="@+id/date_collapsed"
+        android:layout_alignParentStart="true"
+        android:layout_alignParentTop="true"
+        android:orientation="horizontal">
+
+        <include layout="@layout/split_clock_view"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginStart="16dp"
+            android:layout_marginTop="2dp"
+            android:id="@+id/clock" />
+
+        <com.android.systemui.statusbar.policy.DateView
+            android:id="@+id/date"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="6dp"
+            android:layout_marginTop="8dp"
+            android:layout_alignParentTop="true"
+            android:drawableStart="@drawable/header_dot"
+            android:drawablePadding="6dp"
             android:singleLine="true"
+            android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
+            android:textSize="@dimen/qs_time_collapsed_size"
+            systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm" />
+
+        <com.android.systemui.statusbar.AlphaOptimizedButton
+            android:id="@+id/alarm_status"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentTop="true"
+            android:drawablePadding="6dp"
+            android:drawableStart="@drawable/ic_access_alarms_small"
+            android:textColor="#64ffffff"
             android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date"
-            android:layout_below="@id/clock"
-            systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm"
-            />
-    </FrameLayout>
+            android:minHeight="36dp"
+            android:paddingStart="6dp"
+            android:background="?android:attr/selectableItemBackground"
+            android:visibility="gone" />
+    </LinearLayout>
 
-    <include layout="@layout/split_clock_view"
-        android:layout_width="wrap_content"
+    <com.android.systemui.qs.QuickQSPanel
+        android:id="@+id/quick_qs_panel"
+        android:background="#0000"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginStart="16dp"
-        android:layout_above="@id/date_group"
-        android:id="@+id/clock"
-        />
-
-    <com.android.systemui.statusbar.AlphaOptimizedButton android:id="@+id/alarm_status"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_alignParentBottom="true"
-        android:layout_toEndOf="@id/date_group"
-        android:layout_marginBottom="4dp"
-        android:drawablePadding="6dp"
-        android:drawableStart="@drawable/ic_access_alarms_small"
-        android:textColor="#64ffffff"
-        android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date"
-        android:paddingEnd="6dp"
-        android:paddingStart="6dp"
-        android:paddingTop="16dp"
-        android:paddingBottom="16dp"
-        android:background="?android:attr/selectableItemBackground"
-        android:visibility="gone"
-        />
+        android:layout_marginTop="30dp"
+        android:layout_marginStart="8dp"
+        android:layout_marginEnd="8dp"
+        android:layout_alignParentEnd="true"
+        android:clipChildren="false"
+        android:clipToPadding="false" />
 
     <include
         android:id="@+id/qs_detail_header"
diff --git a/packages/SystemUI/res/layout/remote_input.xml b/packages/SystemUI/res/layout/remote_input.xml
index 74092c1..83cfc76 100644
--- a/packages/SystemUI/res/layout/remote_input.xml
+++ b/packages/SystemUI/res/layout/remote_input.xml
@@ -36,7 +36,8 @@
             android:paddingEnd="12dp"
             android:gravity="start|center_vertical"
             android:textAppearance="?android:attr/textAppearance"
-            android:textColor="#deffffff"
+            android:textColor="@color/remote_input_text"
+            android:textColorHint="@color/remote_input_hint"
             android:textSize="16sp"
             android:background="@null"
             android:singleLine="true"
@@ -58,8 +59,8 @@
                 android:paddingBottom="12dp"
                 android:id="@+id/remote_input_send"
                 android:src="@drawable/ic_send"
-                android:tint="@android:color/white"
-                android:tintMode="src_atop"
+                android:tint="@color/remote_input_send"
+                android:tintMode="src_in"
                 android:background="@drawable/ripple_drawable" />
 
         <ProgressBar
diff --git a/packages/SystemUI/res/layout/split_clock_view.xml b/packages/SystemUI/res/layout/split_clock_view.xml
index d1269da..ae5136f 100644
--- a/packages/SystemUI/res/layout/split_clock_view.xml
+++ b/packages/SystemUI/res/layout/split_clock_view.xml
@@ -27,6 +27,7 @@
         android:layout_height="wrap_content"
         android:singleLine="true"
         android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
+        android:textSize="@dimen/qs_time_collapsed_size"
         />
     <TextClock
         android:id="@+id/am_pm_view"
diff --git a/packages/SystemUI/res/values-h560dp/config.xml b/packages/SystemUI/res/values-h560dp/config.xml
index f210d7b..8b576b9 100644
--- a/packages/SystemUI/res/values-h560dp/config.xml
+++ b/packages/SystemUI/res/values-h560dp/config.xml
@@ -18,6 +18,6 @@
 
 <resources>
     <!-- The maximum number of items to be displayed in quick settings -->
-    <integer name="quick_settings_detail_max_item_count">8</integer>
+    <integer name="quick_settings_detail_max_item_count">6</integer>
 </resources>
 
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 5618e9b..8875515 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -68,6 +68,8 @@
     <color name="recents_task_view_lock_to_app_button_background_color">#ffe6e6e6</color>
     <!-- The lock to task button foreground color. -->
     <color name="recents_task_view_lock_to_app_button_color">#ff666666</color>
+    <!-- The background color for the freeform workspace. -->
+    <color name="recents_freeform_workspace_bg_color">#66000000</color>
 
     <color name="keyguard_affordance">#ffffffff</color>
 
@@ -144,4 +146,7 @@
 
     <color name="docked_divider_background">#ff000000</color>
     <color name="docked_divider_handle">#ffffff</color>
+
+    <color name="default_remote_input_background">@*android:color/notification_default_color</color>
+    <color name="remote_input_hint">#4dffffff</color>
 </resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index a0052ce..40e8b50 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -103,7 +103,7 @@
 
     <!-- The default tiles to display in QuickSettings -->
     <string name="quick_settings_tiles_default" translatable="false">
-        wifi,bt,inversion,dnd,cell,airplane,rotation,flashlight,location,cast,hotspot
+        wifi,bt,flashlight,dnd,cell,battery,rotation,airplane,location,cast
     </string>
 
     <!-- The tiles to display in QuickSettings -->
@@ -117,7 +117,7 @@
     <integer name="quick_settings_brightness_dialog_long_timeout">4000</integer>
 
     <!-- The maximum number of items to be displayed in quick settings -->
-    <integer name="quick_settings_detail_max_item_count">7</integer>
+    <integer name="quick_settings_detail_max_item_count">5</integer>
 
     <!-- Should "4G" be shown instead of "LTE" when the network is NETWORK_TYPE_LTE? -->
     <bool name="config_show4GForLTE">true</bool>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 65b0c45..4f070d6 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -45,8 +45,11 @@
     <!-- Height of a large notification in the status bar -->
     <dimen name="notification_max_height">276dp</dimen>
 
-    <!-- Height of a medium notification in the status bar -->
-    <dimen name="notification_mid_height">128dp</dimen>
+    <!-- Height of a heads up notification in the status bar for legacy custom views -->
+    <dimen name="notification_max_heads_up_height_legacy">128dp</dimen>
+
+    <!-- Height of a heads up notification in the status bar -->
+    <dimen name="notification_max_heads_up_height">140dp</dimen>
 
     <!-- Height of a the summary ("more card") notification on keyguard. -->
     <dimen name="notification_summary_height">44dp</dimen>
@@ -95,7 +98,7 @@
     <dimen name="close_handle_underlap">32dp</dimen>
 
     <!-- Height of the status bar header bar -->
-    <dimen name="status_bar_header_height">60dp</dimen>
+    <dimen name="status_bar_header_height">90dp</dimen>
 
     <!-- Height of the status bar header bar when expanded -->
     <dimen name="status_bar_header_height_expanded">116dp</dimen>
@@ -133,6 +136,7 @@
     <dimen name="qs_quick_actions_padding">25dp</dimen>
     <dimen name="qs_quick_tile_size">48dp</dimen>
     <dimen name="qs_quick_tile_padding">12dp</dimen>
+    <dimen name="qs_date_anim_translation">44.5dp</dimen>
     <dimen name="qs_page_indicator_size">12dp</dimen>
     <dimen name="qs_tile_icon_size">24dp</dimen>
     <dimen name="qs_tile_text_size">12sp</dimen>
diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml
index 8dcf8a7..e31927e 100644
--- a/packages/SystemUI/res/xml/tuner_prefs.xml
+++ b/packages/SystemUI/res/xml/tuner_prefs.xml
@@ -18,25 +18,10 @@
     xmlns:sysui="http://schemas.android.com/apk/res-auto"
     android:title="@string/system_ui_tuner">
 
-    <PreferenceScreen
-        android:title="@string/quick_settings">
-
-        <PreferenceCategory
-            android:title="@string/experimental">
-
-            <com.android.systemui.tuner.TunerSwitch
-                android:key="qs_show_brightness"
-                android:title="@string/show_brightness"
-                sysui:defValue="true" />
-
-            <com.android.systemui.tuner.QSPagingSwitch
-                android:key="qs_paged_panel"
-                android:title="@string/qs_paging" />
-
-        </PreferenceCategory>
-
-    </PreferenceScreen>
-
+    <com.android.systemui.tuner.TunerSwitch
+        android:key="qs_show_brightness"
+        android:title="@string/show_brightness"
+        sysui:defValue="true" />
 
     <PreferenceScreen
         android:title="@string/status_bar" >
diff --git a/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java b/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
index 5b8d3d6..c9ba885 100644
--- a/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
@@ -19,6 +19,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
+import android.content.Context;
 import android.graphics.ColorMatrix;
 import android.graphics.ColorMatrixColorFilter;
 import android.graphics.Paint;
@@ -35,13 +36,19 @@
 
     private final Paint mDarkPaint = new Paint();
     private final Interpolator mLinearOutSlowInInterpolator;
-    private final ArrayList<View> mTargets;
     private final ColorMatrix mMatrix = new ColorMatrix();
     private final ColorMatrix mGrayscaleMatrix = new ColorMatrix();
     private final long mFadeDuration;
+    private final ArrayList<View> mTargets = new ArrayList<>();
 
-    public ViewInvertHelper(View target, long fadeDuration) {
-        this(constructArray(target), fadeDuration);
+    public ViewInvertHelper(View v, long fadeDuration) {
+        this(v.getContext(), fadeDuration);
+        addTarget(v);
+    }
+    public ViewInvertHelper(Context context, long fadeDuration) {
+        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+                android.R.interpolator.linear_out_slow_in);
+        mFadeDuration = fadeDuration;
     }
 
     private static ArrayList<View> constructArray(View target) {
@@ -50,11 +57,12 @@
         return views;
     }
 
-    public ViewInvertHelper(ArrayList<View> targets, long fadeDuration) {
-        mTargets = targets;
-        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(mTargets.get(0).getContext(),
-                android.R.interpolator.linear_out_slow_in);
-        mFadeDuration = fadeDuration;
+    public void clearTargets() {
+        mTargets.clear();
+    }
+
+    public void addTarget(View target) {
+        mTargets.add(target);
     }
 
     public void fade(final boolean invert, long delay) {
@@ -112,4 +120,12 @@
         mMatrix.preConcat(mGrayscaleMatrix);
         mDarkPaint.setColorFilter(new ColorMatrixColorFilter(mMatrix));
     }
+
+    public void setInverted(boolean invert, boolean fade, long delay) {
+        if (fade) {
+            fade(invert, delay);
+        } else {
+            update(invert);
+        }
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java b/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java
index cb6708e..699273a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PseudoGridView.java
@@ -98,7 +98,7 @@
             }
         }
 
-        setMeasuredDimension(width, getDefaultSize(totalHeight, heightMeasureSpec));
+        setMeasuredDimension(width, resolveSizeAndState(totalHeight, heightMeasureSpec, 0));
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 5d3fbe2..ae8542a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -19,14 +19,12 @@
 import android.animation.Animator;
 import android.animation.Animator.AnimatorListener;
 import android.animation.AnimatorListenerAdapter;
-import android.app.ActivityManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.os.Handler;
 import android.os.Message;
-import android.provider.Settings;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -36,7 +34,6 @@
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
-
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.FontSizeUtils;
 import com.android.systemui.R;
@@ -56,7 +53,6 @@
 public class QSPanel extends FrameLayout implements Tunable {
 
     public static final String QS_SHOW_BRIGHTNESS = "qs_show_brightness";
-    public static final String QS_THE_NEW_QS = "qs_paged_panel";
 
     protected final Context mContext;
     protected final ArrayList<TileRecord> mRecords = new ArrayList<TileRecord>();
@@ -102,20 +98,25 @@
         updateDetailText();
         mDetail.setVisibility(GONE);
         mDetail.setClickable(true);
-        mBrightnessView = LayoutInflater.from(context).inflate(
-                R.layout.quick_settings_brightness_dialog, this, false);
-        mFooter = new QSFooter(this, context);
         addView(mDetail);
 
         mQsContainer = new LinearLayout(mContext);
         mQsContainer.setOrientation(LinearLayout.VERTICAL);
         mQsContainer.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
                 LayoutParams.WRAP_CONTENT));
-
         addView(mQsContainer);
 
+        mBrightnessView = LayoutInflater.from(context).inflate(
+                R.layout.quick_settings_brightness_dialog, this, false);
         mQsContainer.addView(mBrightnessView);
+
+        mTileLayout = (QSTileLayout) LayoutInflater.from(mContext).inflate(
+                R.layout.qs_paged_tile_layout, mQsContainer, false);
+        mQsContainer.addView((View) mTileLayout);
+
+        mFooter = new QSFooter(this, context);
         mQsContainer.addView(mFooter.getView());
+
         mClipper = new QSDetailClipper(mDetail);
         updateResources();
 
@@ -136,7 +137,7 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        TunerService.get(mContext).addTunable(this, QS_SHOW_BRIGHTNESS, QS_THE_NEW_QS);
+        TunerService.get(mContext).addTunable(this, QS_SHOW_BRIGHTNESS);
     }
 
     @Override
@@ -150,36 +151,15 @@
         if (QS_SHOW_BRIGHTNESS.equals(key)) {
             mBrightnessView.setVisibility(newValue == null || Integer.parseInt(newValue) != 0
                     ? VISIBLE : GONE);
-        } else if (QS_THE_NEW_QS.equals(key)) {
-            boolean theNewQs = newValue != null && Integer.parseInt(newValue) != 0;
-            if (mTileLayout != null) {
-                for (int i = 0; i < mRecords.size(); i++) {
-                    mTileLayout.removeTile(mRecords.get(i));
-                }
-                mQsContainer.removeView((View) mTileLayout);
-            }
-            int layout = theNewQs
-                    ? R.layout.qs_paged_tile_layout : R.layout.qs_tile_layout;
-            mTileLayout =
-                    (QSTileLayout) LayoutInflater.from(mContext).inflate(layout, mQsContainer, false);
-            mQsContainer.addView((View) mTileLayout, 1 /* Between brightness and footer */);
-            for (int i = 0; i < mRecords.size(); i++) {
-                mTileLayout.addTile(mRecords.get(i));
-            }
-            if (theNewQs) {
-                mCustomizePanel = (QSCustomizer) LayoutInflater.from(mContext)
-                        .inflate(R.layout.qs_customize_panel, null);
-                mCustomizePanel.setHost(mHost);
-            } else {
-                if (mCustomizePanel != null && mCustomizePanel.isCustomizing()) {
-                    mCustomizePanel.hide(mCustomizePanel.getWidth() / 2,
-                            mCustomizePanel.getHeight() / 2);
-                }
-                mCustomizePanel = null;
-            }
         }
     }
 
+    protected void createCustomizePanel() {
+        mCustomizePanel = (QSCustomizer) LayoutInflater.from(mContext)
+                .inflate(R.layout.qs_customize_panel, null);
+        mCustomizePanel.setHost(mHost);
+    }
+
     private void updateDetailText() {
         mDetailDoneButton.setText(R.string.quick_settings_done);
         mDetailSettingsButton.setText(R.string.quick_settings_more_settings);
@@ -200,6 +180,7 @@
     public void setHost(QSTileHost host) {
         mHost = host;
         mFooter.setHost(host);
+        createCustomizePanel();
     }
 
     public QSTileHost getHost() {
@@ -608,9 +589,4 @@
         int getOffsetTop(TileRecord tile);
         void updateResources();
     }
-
-    public static boolean isTheNewQS(Context context) {
-        return Settings.Secure.getIntForUser(context.getContentResolver(), QS_THE_NEW_QS,
-                ActivityManager.getCurrentUser(), 0) != 0;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index fe8ce9b..bda4675 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -128,9 +128,10 @@
         }
 
         private LayoutParams generateLayoutParams() {
-            int size =
-                    mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size);
-            LayoutParams lp = new LayoutParams(size, size);
+            int size = mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size);
+            LayoutParams lp = new LayoutParams(0, size);
+            lp.weight = 1;
+            lp.gravity = Gravity.CENTER;
             return lp;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java
index 8dda9ba..87c2973 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java
@@ -81,6 +81,11 @@
         }
     }
 
+    @Override
+    protected void createCustomizePanel() {
+        // Already in CustomizePanel.
+    }
+
     public void tileSelected(QSTile<?> tile, ClipData currentClip) {
         String sourceSpec = getSpec(currentClip);
         String destSpec = tile.getTileSpec();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index baad370..4a7d67f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -37,9 +37,7 @@
 import android.widget.ListView;
 import android.widget.Toolbar;
 import android.widget.Toolbar.OnMenuItemClickListener;
-
 import com.android.systemui.R;
-import com.android.systemui.SystemUIApplication;
 import com.android.systemui.qs.QSDetailClipper;
 import com.android.systemui.qs.QSTile.Host.Callback;
 import com.android.systemui.qs.customize.DropButton.OnDropListener;
@@ -80,14 +78,13 @@
 
     public QSCustomizer(Context context, AttributeSet attrs) {
         super(new ContextThemeWrapper(context, android.R.style.Theme_Material), attrs);
-        mPhoneStatusBar = ((SystemUIApplication) mContext.getApplicationContext())
-                .getComponent(PhoneStatusBar.class);
         mClipper = new QSDetailClipper(this);
     }
 
     public void setHost(QSTileHost host) {
         mHost = host;
         mHost.addCallback(this);
+        mPhoneStatusBar = host.getPhoneStatusBar();
         mQsPanel.setTiles(mHost.getTiles());
         mQsPanel.setHost(mHost);
         mQsPanel.setSavedTiles();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 5fb76c8..6c7b337 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -86,6 +86,14 @@
     }
 
     @Override
+    protected void handleSecondaryClick() {
+        boolean dataEnabled = mDataController.isMobileDataSupported()
+                && mDataController.isMobileDataEnabled();
+        MetricsLogger.action(mContext, MetricsLogger.QS_CELLULAR_TOGGLE, !dataEnabled);
+        mDataController.setMobileDataEnabled(!dataEnabled);
+    }
+
+    @Override
     protected void handleUpdateState(SignalState state, Object arg) {
         CallbackInfo cb = (CallbackInfo) arg;
         if (cb == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
index 757d2aa..f646a92 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
@@ -46,19 +46,6 @@
                 }
             };
 
-    public static final Property<AnimateableViewBounds, Integer> CLIP_RIGHT =
-            new IntProperty<AnimateableViewBounds>("clipRight") {
-                @Override
-                public void setValue(AnimateableViewBounds object, int clip) {
-                    object.setClipRight(clip, false /* force */);
-                }
-
-                @Override
-                public Integer get(AnimateableViewBounds object) {
-                    return object.getClipRight();
-                }
-            };
-
     public AnimateableViewBounds(View source, int cornerRadius) {
         mSourceView = source;
         mCornerRadius = cornerRadius;
@@ -102,19 +89,6 @@
         return mClipRect.bottom;
     }
 
-    /** Sets the right clip. */
-    public void setClipRight(int right, boolean force) {
-        if (right != mClipRect.right || force) {
-            mClipRect.right = right;
-            updateClipBounds();
-        }
-    }
-
-    /** Returns the right clip. */
-    public int getClipRight() {
-        return mClipRect.right;
-    }
-
     private void updateClipBounds() {
         mClipBounds.set(mClipRect.left, mClipRect.top,
                 mSourceView.getWidth() - mClipRect.right,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
index 9b9d58c..2351aa3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FreeformWorkspaceLayoutAlgorithm.java
@@ -19,7 +19,6 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.util.Log;
-import com.android.systemui.recents.misc.Utilities;
 import com.android.systemui.recents.model.Task;
 
 import java.util.Collections;
@@ -80,7 +79,6 @@
                 float width = normalizedTaskWidths[i] * rowScale;
                 if (rowWidth + width > normalizedWorkspaceWidth) {
                     // That is too long for this row, create new row
-                    rowWidth = 0f;
                     if ((rowCount + 1) * rowScale > normalizedWorkspaceHeight) {
                         // The new row is too high, so we need to try fitting again.  Update the
                         // scale to be the smaller of the scale needed to fit the task in the
@@ -88,9 +86,11 @@
                         rowScale = Math.min(normalizedWorkspaceWidth / (rowWidth + width),
                                 normalizedWorkspaceHeight / (rowCount + 1));
                         rowCount = 1;
+                        rowWidth = 0;
                         i = 0;
                     } else {
                         // The new row fits, so continue
+                        rowWidth = width;
                         rowCount++;
                         i++;
                     }
@@ -103,20 +103,20 @@
             }
 
             // Normalize each of the actual rects to that scale
-            int height = (int) (rowScale * workspaceHeight);
-            float rowTop = ((1f - (rowScale * rowCount)) * workspaceHeight) / 2f;
             float defaultRowLeft = ((1f - (maxRowWidth / normalizedWorkspaceWidth)) *
                     workspaceWidth) / 2f;
             float rowLeft = defaultRowLeft;
+            float rowTop = ((1f - (rowScale * rowCount)) * workspaceHeight) / 2f;
+            float rowHeight = rowScale * workspaceHeight;
             for (int i = 0; i < numFreeformTasks; i++) {
                 Task task = freeformTasks.get(i);
-                int width = (int) (height * normalizedTaskWidths[i]);
+                float width = rowHeight * normalizedTaskWidths[i];
                 if (rowLeft + width > workspaceWidth) {
                     // This goes on the next line
-                    rowTop += height;
+                    rowTop += rowHeight;
                     rowLeft = defaultRowLeft;
                 }
-                RectF rect = new RectF(rowLeft, rowTop, rowLeft + width, rowTop + height);
+                RectF rect = new RectF(rowLeft, rowTop, rowLeft + width, rowTop + rowHeight);
                 rowLeft += width;
                 mTaskRectMap.put(task.key, rect);
             }
@@ -140,34 +140,29 @@
     public TaskViewTransform getTransform(Task task, TaskViewTransform transformOut,
             TaskStackLayoutAlgorithm stackLayout) {
         if (mTaskRectMap.containsKey(task.key)) {
-            Rect taskRect = stackLayout.mTaskRect;
-            RectF ffRect = mTaskRectMap.get(task.key);
-            float scale = Math.max(ffRect.width() / taskRect.width(),
-                    ffRect.height() / taskRect.height());
-            int topOffset = (stackLayout.mFreeformRect.top - taskRect.top);
-            int scaleXOffset = (int) (((1f - scale) * taskRect.width()) / 2);
-            int scaleYOffset = (int) (((1f - scale) * taskRect.height()) / 2);
+            final Rect taskRect = stackLayout.mTaskRect;
+            final RectF ffRect = mTaskRectMap.get(task.key);
 
-            transformOut.scale = scale * 0.95f;
-            transformOut.translationX = (int) (ffRect.left - scaleXOffset);
-            transformOut.translationY = (int) (topOffset + ffRect.top - scaleYOffset);
+            transformOut.scale = 1f;
+            transformOut.alpha = 1f;
             transformOut.translationZ = stackLayout.mMaxTranslationZ;
-            transformOut.clipBottom = (int) (taskRect.height() - (ffRect.height() / scale));
-            transformOut.clipRight = (int) (taskRect.width() - (ffRect.width() / scale));
             if (task.thumbnail != null) {
-                transformOut.thumbnailScale = Math.min(
-                        ((float) taskRect.width() - transformOut.clipRight) /
-                                task.thumbnail.getWidth(),
-                        ((float) taskRect.height() - transformOut.clipBottom) /
-                                task.thumbnail.getHeight());
+                if (task.bounds == null) {
+                    // This is a stack task that has no freeform thumbnail, so keep the same bitmap
+                    // scale as it had in the stack
+                    transformOut.thumbnailScale = (float) taskRect.width() /
+                            task.thumbnail.getWidth();
+                } else {
+                    // This is a freeform rect so fit the bitmap to the task bounds
+                    transformOut.thumbnailScale = Math.min(
+                            ffRect.width() / task.thumbnail.getWidth(),
+                            ffRect.height() / task.thumbnail.getHeight());
+                }
             } else {
                 transformOut.thumbnailScale = 1f;
             }
-            transformOut.rect.set(taskRect);
-            transformOut.rect.offset(transformOut.translationX, transformOut.translationY);
-            Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
-            transformOut.rect.right -= transformOut.clipRight * scale;
-            transformOut.rect.bottom -= transformOut.clipBottom * scale;
+            transformOut.rect.set(ffRect);
+            transformOut.rect.offset(stackLayout.mFreeformRect.left, stackLayout.mFreeformRect.top);
             transformOut.visible = true;
             transformOut.p = 1f;
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index 7d5daae..f599f52 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -507,6 +507,15 @@
     }
 
     /**
+     * Returns the task progress that would put the task just off the back of the stack.
+     */
+    public float getStackBackTaskProgress(float stackScroll) {
+        float min = mUnfocusedRange.relativeMin +
+                mFocusState * (mFocusedRange.relativeMin - mUnfocusedRange.relativeMin);
+        return stackScroll + min;
+    }
+
+    /**
      * Returns the task progress that would put the task just off the front of the stack.
      */
     public float getStackFrontTaskProgress(float stackScroll) {
@@ -647,6 +656,7 @@
             return transformOut;
         }
 
+        int x = (mStackRect.width() - mTaskRect.width()) / 2;
         int y;
         float z;
         float relP;
@@ -671,16 +681,13 @@
 
         // Fill out the transform
         transformOut.scale = 1f;
-        transformOut.translationX = (mStackRect.width() - mTaskRect.width()) / 2;
-        transformOut.translationY = y;
+        transformOut.alpha = 1f;
         transformOut.translationZ = z;
         transformOut.rect.set(mTaskRect);
-        transformOut.rect.offset(transformOut.translationX, transformOut.translationY);
+        transformOut.rect.offset(x, y);
         Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
         transformOut.visible = (transformOut.rect.top < mStackRect.bottom) &&
                 (frontTransform == null || transformOut.rect.top != frontTransform.rect.top);
-        transformOut.clipBottom = 0;
-        transformOut.clipRight = 0;
         transformOut.thumbnailScale = 1f;
         transformOut.p = relP;
         return transformOut;
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 cc5aaae..350bc2b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -20,11 +20,12 @@
 import android.animation.ValueAnimator;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.RectF;
-import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
 import android.os.Bundle;
 import android.util.IntProperty;
 import android.util.Log;
@@ -94,15 +95,15 @@
     private static final float SHOW_HISTORY_BUTTON_SCROLL_THRESHOLD = 0.3f;
     private static final float HIDE_HISTORY_BUTTON_SCROLL_THRESHOLD = 0.3f;
 
-    public static final Property<ColorDrawable, Integer> COLOR_DRAWABLE_ALPHA =
-            new IntProperty<ColorDrawable>("colorDrawableAlpha") {
+    public static final Property<Drawable, Integer> DRAWABLE_ALPHA =
+            new IntProperty<Drawable>("drawableAlpha") {
                 @Override
-                public void setValue(ColorDrawable object, int alpha) {
+                public void setValue(Drawable object, int alpha) {
                     object.setAlpha(alpha);
                 }
 
                 @Override
-                public Integer get(ColorDrawable object) {
+                public Integer get(Drawable object) {
                     return object.getAlpha();
                 }
             };
@@ -118,7 +119,7 @@
     TaskStackViewScroller mStackScroller;
     TaskStackViewTouchHandler mTouchHandler;
     TaskStackViewCallbacks mCb;
-    ColorDrawable mFreeformWorkspaceBackground;
+    GradientDrawable mFreeformWorkspaceBackground;
     ObjectAnimator mFreeformWorkspaceBackgroundAnimator;
     ViewPool<TaskView, Task> mViewPool;
     ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<>();
@@ -126,6 +127,7 @@
     Task mFocusedTask;
     // Optimizations
     int mStackViewsAnimationDuration;
+    int mTaskCornerRadiusPx;
     boolean mStackViewsDirty = true;
     boolean mStackViewsClipDirty = true;
     boolean mAwaitingFirstLayout = true;
@@ -174,6 +176,9 @@
 
     public TaskStackView(Context context, TaskStack stack) {
         super(context);
+        SystemServicesProxy ssp = Recents.getSystemServices();
+        Resources res = context.getResources();
+
         // Set the stack first
         setStack(stack);
         mViewPool = new ViewPool<>(context, this);
@@ -184,6 +189,8 @@
         mTouchHandler = new TaskStackViewTouchHandler(context, this, mStackScroller);
         mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
                 com.android.internal.R.interpolator.fast_out_slow_in);
+        mTaskCornerRadiusPx = res.getDimensionPixelSize(
+                R.dimen.recents_task_view_rounded_corners_radius);
 
         int taskBarDismissDozeDelaySeconds = getResources().getInteger(
                 R.integer.recents_task_bar_dismiss_delay_seconds);
@@ -201,8 +208,12 @@
         });
         setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
 
-        mFreeformWorkspaceBackground = new ColorDrawable(0x33000000);
+        mFreeformWorkspaceBackground = (GradientDrawable) getContext().getDrawable(
+                R.drawable.recents_freeform_workspace_bg);
         mFreeformWorkspaceBackground.setCallback(this);
+        if (ssp.hasFreeformWorkspaceSupport()) {
+            setBackgroundColor(getContext().getColor(R.color.recents_freeform_workspace_bg_color));
+        }
     }
 
     /** Sets the callbacks */
@@ -364,8 +375,7 @@
     private boolean updateStackTransforms(ArrayList<TaskViewTransform> taskTransforms,
                                        ArrayList<Task> tasks,
                                        float stackScroll,
-                                       int[] visibleRangeOut,
-                                       boolean boundTranslationsToRect) {
+                                       int[] visibleRangeOut) {
         int taskTransformCount = taskTransforms.size();
         int taskCount = tasks.size();
         int frontMostVisibleIndex = -1;
@@ -411,11 +421,6 @@
                     break;
                 }
             }
-
-            if (boundTranslationsToRect) {
-                transform.translationY = Math.min(transform.translationY,
-                        mLayoutAlgorithm.mStackRect.bottom);
-            }
             frontTransform = transform;
         }
         if (visibleRangeOut != null) {
@@ -433,7 +438,7 @@
             float stackScroll = mStackScroller.getStackScroll();
             int[] visibleStackRange = mTmpVisibleRange;
             boolean isValidVisibleStackRange = updateStackTransforms(mCurrentTaskTransforms, tasks,
-                    stackScroll, visibleStackRange, false);
+                    stackScroll, visibleStackRange);
             boolean hasStackBackTransform = false;
             boolean hasStackFrontTransform = false;
             if (DEBUG) {
@@ -490,7 +495,7 @@
                 }
 
                 // Animate the task into place
-                tv.updateViewPropertiesToTaskTransform(transform, transform.clipBottom,
+                tv.updateViewPropertiesToTaskTransform(transform, 0,
                         mStackViewsAnimationDuration, mFastOutSlowInInterpolator,
                         mRequestUpdateClippingListener);
 
@@ -513,8 +518,9 @@
                         if (Float.compare(transform.p, 0f) <= 0) {
                             if (!hasStackBackTransform) {
                                 hasStackBackTransform = true;
-                                mLayoutAlgorithm.getStackTransform(0f, 0f, mTmpStackBackTransform,
-                                        null);
+                                mLayoutAlgorithm.getStackTransform(
+                                        mLayoutAlgorithm.getStackBackTaskProgress(0f), 0f,
+                                        mTmpStackBackTransform, null);
                             }
                             tv.updateViewPropertiesToTaskTransform(mTmpStackBackTransform, 0, 0,
                                     mFastOutSlowInInterpolator, mRequestUpdateClippingListener);
@@ -586,17 +592,11 @@
                 // stacked and we can make assumptions about the visibility of the this
                 // task relative to the ones in front of it.
                 if (frontTv != null) {
-                    mTmpTaskRect.set(mLayoutAlgorithm.mTaskRect);
-                    mTmpTaskRect.offset(0, tv.getTranslationY());
-                    Utilities.scaleRectAboutCenter(mTmpTaskRect, tv.getScaleX());
-                    float taskBottom = mTmpTaskRect.bottom;
-                    mTmpTaskRect.set(mLayoutAlgorithm.mTaskRect);
-                    mTmpTaskRect.offset(0, frontTv.getTranslationY());
-                    Utilities.scaleRectAboutCenter(mTmpTaskRect, frontTv.getScaleX());
-                    float frontTaskTop = mTmpTaskRect.top;
+                    float taskBottom = tv.getBottom();
+                    float frontTaskTop = frontTv.getTop();
                     if (frontTaskTop < taskBottom) {
                         // Map the stack view space coordinate (the rects) to view space
-                        clipBottom = (int) ((taskBottom - frontTaskTop) / tv.getScaleX()) - 1;
+                        clipBottom = (int) (taskBottom - frontTaskTop) - mTaskCornerRadiusPx;
                     }
                 }
             }
@@ -980,11 +980,11 @@
             onFirstLayout();
         }
 
+        requestSynchronizeStackViewsWithModel();
         if (changed) {
             if (mStackScroller.isScrollOutOfBounds()) {
                 mStackScroller.boundScroll();
             }
-            requestSynchronizeStackViewsWithModel();
             synchronizeStackViewsWithModel();
             requestUpdateStackViewsClip();
             clipTaskViews(true /* forceUpdate */);
@@ -1147,8 +1147,7 @@
         transformPointToViewLocal(point, tv);
         x = point[0];
         y = point[1];
-        return (0 <= x) && (x < (tv.getMeasuredWidth() - tv.getViewBounds().getClipRight())) &&
-                (0 <= y) && (y < (tv.getMeasuredHeight() - tv.getViewBounds().getClipBottom()));
+        return (0 <= x) && (x < tv.getWidth()) && (0 <= y) && (y < tv.getHeight());
     }
 
     @Override
@@ -1221,14 +1220,13 @@
             } else if (pullStackForward) {
                 // Otherwise, offset the scroll by the movement of the anchor task
                 float anchorTaskScroll = mLayoutAlgorithm.getStackScrollForTask(anchorTask);
-                float newStackScroll = mStackScroller.getStackScroll() +
-                        (anchorTaskScroll - prevAnchorTaskScroll);
+                float stackScrollOffset = (anchorTaskScroll - prevAnchorTaskScroll);
                 if (mLayoutAlgorithm.getFocusState() != TaskStackLayoutAlgorithm.STATE_FOCUSED) {
                     // If we are focused, we don't want the front task to move, but otherwise, we
                     // allow the back task to move up, and the front task to move back
-                    newStackScroll /= 2;
+                    stackScrollOffset /= 2;
                 }
-                mStackScroller.setStackScroll(newStackScroll);
+                mStackScroller.setStackScroll(mStackScroller.getStackScroll() + stackScrollOffset);
                 mStackScroller.boundScroll();
             }
 
@@ -1498,6 +1496,18 @@
         event.taskView.animate()
                 .withEndAction(event.postAnimationTrigger.decrementAsRunnable());
 
+        // We translated the view but we need to animate it back from the current layout-space rect
+        // to its final layout-space rect
+        int x = (int) event.taskView.getTranslationX();
+        int y = (int) event.taskView.getTranslationY();
+        Rect taskViewRect = new Rect(event.taskView.getLeft(), event.taskView.getTop(),
+                event.taskView.getRight(), event.taskView.getBottom());
+        taskViewRect.offset(x, y);
+        event.taskView.setTranslationX(0);
+        event.taskView.setTranslationY(0);
+        event.taskView.setLeftTopRightBottom(taskViewRect.left, taskViewRect.top,
+                taskViewRect.right, taskViewRect.bottom);
+
         // Animate the tack view back into position
         requestSynchronizeStackViewsWithModel(250);
     }
@@ -1582,7 +1592,7 @@
 
         Utilities.cancelAnimationWithoutCallbacks(mFreeformWorkspaceBackgroundAnimator);
         mFreeformWorkspaceBackgroundAnimator = ObjectAnimator.ofInt(mFreeformWorkspaceBackground,
-                COLOR_DRAWABLE_ALPHA, mFreeformWorkspaceBackground.getAlpha(), targetAlpha);
+                DRAWABLE_ALPHA, mFreeformWorkspaceBackground.getAlpha(), targetAlpha);
         mFreeformWorkspaceBackgroundAnimator.setDuration(duration);
         mFreeformWorkspaceBackgroundAnimator.setInterpolator(interpolator);
         mFreeformWorkspaceBackgroundAnimator.start();
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 813a1fc..1e2227e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -179,6 +179,13 @@
     }
 
     @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+        mHeaderView.onTaskViewSizeChanged(w, h);
+        mThumbnailView.onTaskViewSizeChanged(w, h);
+    }
+
+    @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
             mDownTouchPos.set((int) (ev.getX() * getScaleX()), (int) (ev.getY() * getScaleY()));
@@ -232,8 +239,14 @@
             mClipAnimation.playTogether(
                     ObjectAnimator.ofInt(mViewBounds, AnimateableViewBounds.CLIP_BOTTOM,
                             mViewBounds.getClipBottom(), clipBottom),
-                    ObjectAnimator.ofInt(mViewBounds, AnimateableViewBounds.CLIP_RIGHT,
-                            mViewBounds.getClipRight(), toTransform.clipRight),
+                    ObjectAnimator.ofInt(this, TaskViewTransform.LEFT, getLeft(),
+                            (int) toTransform.rect.left),
+                    ObjectAnimator.ofInt(this, TaskViewTransform.TOP, getTop(),
+                            (int) toTransform.rect.top),
+                    ObjectAnimator.ofInt(this, TaskViewTransform.RIGHT, getRight(),
+                            (int) toTransform.rect.right),
+                    ObjectAnimator.ofInt(this, TaskViewTransform.BOTTOM, getBottom(),
+                            (int) toTransform.rect.bottom),
                     ObjectAnimator.ofFloat(mThumbnailView, TaskViewThumbnail.BITMAP_SCALE,
                             mThumbnailView.getBitmapScale(), toTransform.thumbnailScale));
             mClipAnimation.setStartDelay(toTransform.startDelay);
@@ -242,8 +255,9 @@
             mClipAnimation.start();
         } else {
             mViewBounds.setClipBottom(clipBottom, false /* forceUpdate */);
-            mViewBounds.setClipRight(toTransform.clipRight, false /* forceUpdate */);
             mThumbnailView.setBitmapScale(toTransform.thumbnailScale);
+            setLeftTopRightBottom((int) toTransform.rect.left, (int) toTransform.rect.top,
+                    (int) toTransform.rect.right, (int) toTransform.rect.bottom);
         }
         if (!config.useHardwareLayers) {
             mThumbnailView.updateThumbnailVisibility(clipBottom - getPaddingBottom());
@@ -336,10 +350,10 @@
             } else {
                 // Animate the task up if it was occluding the launch target
                 if (ctx.currentTaskOccludesLaunchTarget) {
-                    setTranslationY(transform.translationY + taskViewAffiliateGroupEnterOffset);
+                    setTranslationY(taskViewAffiliateGroupEnterOffset);
                     setAlpha(0f);
                     animate().alpha(1f)
-                            .translationY(transform.translationY)
+                            .translationY(0)
                             .setUpdateListener(null)
                             .setListener(new AnimatorListenerAdapter() {
                                 private boolean hasEnded;
@@ -372,7 +386,7 @@
                 animate().translationZ(transform.translationZ);
             }
             animate()
-                    .translationY(transform.translationY)
+                    .translationY(0)
                     .setStartDelay(delay)
                     .setUpdateListener(ctx.updateListener)
                     .setListener(new AnimatorListenerAdapter() {
@@ -644,7 +658,6 @@
         }
 
         SystemServicesProxy ssp = Recents.getSystemServices();
-        mHeaderView.onTaskViewFocusChanged(isFocused, animated);
         if (isFocused) {
             if (requestViewFocus && !isFocused()) {
                 requestFocus();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index 78a2c7f..d8220fd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -31,6 +31,7 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
 import android.graphics.drawable.RippleDrawable;
+import android.graphics.drawable.ShapeDrawable;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewOutlineProvider;
@@ -55,8 +56,6 @@
 public class TaskViewHeader extends FrameLayout
         implements View.OnClickListener, View.OnLongClickListener {
 
-    private static final float FOCUS_TRANSLATION_Z = 4f;
-
     Task mTask;
 
     // Header views
@@ -66,13 +65,13 @@
     TextView mActivityDescription;
 
     // Header drawables
+    Rect mTaskViewRect = new Rect();
     int mCornerRadius;
     int mHighlightHeight;
     Drawable mLightDismissDrawable;
     Drawable mDarkDismissDrawable;
     RippleDrawable mBackground;
     GradientDrawable mBackgroundColorDrawable;
-    ObjectAnimator mFocusAnimator;
     String mDismissContentDescription;
 
     // Static highlight that we draw at the top of each view
@@ -99,13 +98,6 @@
     public TaskViewHeader(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
         setWillNotDraw(false);
-        setClipToOutline(true);
-        setOutlineProvider(new ViewOutlineProvider() {
-            @Override
-            public void getOutline(View view, Outline outline) {
-                outline.setRect(0, 0, getMeasuredWidth(), getMeasuredHeight());
-            }
-        });
 
         // Load the dismiss resources
         mDimLayerPaint.setColor(Color.argb(0, 0, 0, 0));
@@ -148,8 +140,8 @@
             mApplicationIcon.setBackground(null);
         }
 
-        mBackgroundColorDrawable = (GradientDrawable) getContext().getDrawable(R.drawable
-                .recents_task_view_header_bg_color);
+        mBackgroundColorDrawable = (GradientDrawable) getContext().getDrawable(
+                R.drawable.recents_task_view_header_bg_color);
         // Copy the ripple drawable since we are going to be manipulating it
         mBackground = (RippleDrawable)
                 getContext().getDrawable(R.drawable.recents_task_view_header_bg);
@@ -159,14 +151,37 @@
         setBackground(mBackground);
     }
 
+    /**
+     * Called when the task view frame changes, allowing us to move the contents of the header
+     * to match the frame changes.
+     */
+    public void onTaskViewSizeChanged(int width, int height) {
+        mTaskViewRect.set(0, 0, width, height);
+        if (mDismissButton.getMeasuredWidth() > (width - mApplicationIcon.getMeasuredWidth())) {
+            mDismissButton.setAlpha(0f);
+        } else {
+            mDismissButton.setAlpha(1f);
+            if (mDismissButton != null) {
+                mDismissButton.setTranslationX(width - getMeasuredWidth());
+            }
+        }
+        if (mActivityDescription.getMeasuredWidth() > (width -
+                (mApplicationIcon.getMeasuredWidth() + mDismissButton.getMeasuredWidth()))) {
+            mActivityDescription.setAlpha(0f);
+        } else {
+            mActivityDescription.setAlpha(1f);
+        }
+        invalidate();
+    }
+
     @Override
     protected void onDraw(Canvas canvas) {
         // Draw the highlight at the top edge (but put the bottom edge just out of view)
         float offset = (float) Math.ceil(mHighlightHeight / 2f);
         float radius = mCornerRadius;
         int count = canvas.save(Canvas.CLIP_SAVE_FLAG);
-        canvas.clipRect(0, 0, getMeasuredWidth(), getMeasuredHeight());
-        canvas.drawRoundRect(-offset, 0f, (float) getMeasuredWidth() + offset,
+        canvas.clipRect(0, 0, mTaskViewRect.width(), getMeasuredHeight());
+        canvas.drawRoundRect(-offset, 0f, (float) mTaskViewRect.width() + offset,
                 getMeasuredHeight() + radius, radius, radius, sHighlightPaint);
         canvas.restoreToCount(count);
     }
@@ -180,12 +195,6 @@
         invalidate();
     }
 
-    /** Returns the secondary color for a primary color. */
-    int getSecondaryColor(int primaryColor, boolean useLightOverlayColor) {
-        int overlayColor = useLightOverlayColor ? Color.WHITE : Color.BLACK;
-        return Utilities.getColorWithOverlay(primaryColor, overlayColor, 0.8f);
-    }
-
     /** Binds the bar view to the task */
     public void rebindToTask(Task t) {
         mTask = t;
@@ -236,9 +245,6 @@
         mApplicationIcon.setImageDrawable(null);
         mApplicationIcon.setOnClickListener(null);
         mMoveTaskButton.setOnClickListener(null);
-
-        // Stop any focus animations
-        Utilities.cancelAnimationWithoutCallbacks(mFocusAnimator);
     }
 
     /** Updates the resize task bar button. */
@@ -332,39 +338,9 @@
     protected void dispatchDraw(Canvas canvas) {
         super.dispatchDraw(canvas);
 
-        // Draw the thumbnail with the rounded corners
-        canvas.drawRoundRect(0, 0, getWidth(), getHeight(),
-                mCornerRadius,
-                mCornerRadius, mDimLayerPaint);
-    }
-
-    /** Notifies the associated TaskView has been focused. */
-    void onTaskViewFocusChanged(boolean focused, boolean animateFocusedState) {
-        boolean isRunning = false;
-        if (mFocusAnimator != null) {
-            isRunning = mFocusAnimator.isRunning();
-        }
-        Utilities.cancelAnimationWithoutCallbacks(mFocusAnimator);
-
-        if (focused) {
-            if (animateFocusedState) {
-                // Bump up the translation
-                mFocusAnimator = ObjectAnimator.ofFloat(this, "translationZ", FOCUS_TRANSLATION_Z);
-                mFocusAnimator.setDuration(200);
-                mFocusAnimator.start();
-            } else {
-                setTranslationZ(FOCUS_TRANSLATION_Z);
-            }
-        } else {
-            if (isRunning) {
-                // Restore the translation
-                mFocusAnimator = ObjectAnimator.ofFloat(this, "translationZ", 0f);
-                mFocusAnimator.setDuration(150);
-                mFocusAnimator.start();
-            } else {
-                setTranslationZ(0f);
-            }
-        }
+        // Draw the dim layer with the rounded corners
+        canvas.drawRoundRect(0, 0, mTaskViewRect.width(), getHeight(),
+                mCornerRadius, mCornerRadius, mDimLayerPaint);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
index 7bb2c7b..37d8cd6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -56,6 +56,7 @@
             };
 
     // Drawing
+    Rect mTaskViewRect = new Rect();
     int mCornerRadius;
     float mDimAlpha;
     Matrix mScaleMatrix = new Matrix();
@@ -98,13 +99,22 @@
                 com.android.internal.R.interpolator.fast_out_slow_in);
     }
 
+    /**
+     * Called when the task view frame changes, allowing us to move the contents of the header
+     * to match the frame changes.
+     */
+    public void onTaskViewSizeChanged(int width, int height) {
+        mTaskViewRect.set(0, 0, width, height);
+        invalidate();
+    }
+
     @Override
     protected void onDraw(Canvas canvas) {
         if (mInvisible) {
             return;
         }
         // Draw the thumbnail with the rounded corners
-        canvas.drawRoundRect(0, 0, getWidth(), getHeight(),
+        canvas.drawRoundRect(0, 0, mTaskViewRect.width(), mTaskViewRect.height(),
                 mCornerRadius,
                 mCornerRadius, mDrawPaint);
     }
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 c3e0906..3ee50ac 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
@@ -18,6 +18,9 @@
 
 import android.animation.ValueAnimator;
 import android.graphics.RectF;
+import android.util.IntProperty;
+import android.util.Property;
+import android.view.View;
 import android.view.ViewPropertyAnimator;
 import android.view.animation.Interpolator;
 
@@ -25,26 +28,70 @@
 /* The transform state for a task view */
 public class TaskViewTransform {
 
+    public static final Property<View, Integer> LEFT =
+            new IntProperty<View>("left") {
+                @Override
+                public void setValue(View object, int v) {
+                    object.setLeft(v);
+                }
+
+                @Override
+                public Integer get(View object) {
+                    return object.getLeft();
+                }
+            };
+
+    public static final Property<View, Integer> TOP =
+            new IntProperty<View>("top") {
+                @Override
+                public void setValue(View object, int v) {
+                    object.setTop(v);
+                }
+
+                @Override
+                public Integer get(View object) {
+                    return object.getTop();
+                }
+            };
+
+    public static final Property<View, Integer> RIGHT =
+            new IntProperty<View>("right") {
+                @Override
+                public void setValue(View object, int v) {
+                    object.setRight(v);
+                }
+
+                @Override
+                public Integer get(View object) {
+                    return object.getRight();
+                }
+            };
+
+    public static final Property<View, Integer> BOTTOM =
+            new IntProperty<View>("bottom") {
+                @Override
+                public void setValue(View object, int v) {
+                    object.setBottom(v);
+                }
+
+                @Override
+                public Integer get(View object) {
+                    return object.getBottom();
+                }
+            };
+
     // TODO: Move this out of the transform
     public int startDelay = 0;
 
-    public int translationX = 0;
-    public int translationY = 0;
     public float translationZ = 0;
     public float scale = 1f;
     public float alpha = 1f;
-
-    // Clip and thumbnail scale are untransformed layout-space properties
-    // The bottom clip is only used for freeform workspace tasks
-    public int clipBottom = 0;
-    public int clipRight = 0;
     public float thumbnailScale = 1f;
 
     public boolean visible = false;
     float p = 0f;
 
-    // This is a window-space rect that is purely used for coordinating the animation of an app
-    // window into Recents.
+    // This is a window-space rect used for positioning the task in the stack and freeform workspace
     public RectF rect = new RectF();
 
     public TaskViewTransform() {
@@ -56,13 +103,9 @@
      */
     public void reset() {
         startDelay = 0;
-        translationX = 0;
-        translationY = 0;
         translationZ = 0;
         scale = 1f;
         alpha = 1f;
-        clipBottom = 0;
-        clipRight = 0;
         thumbnailScale = 1f;
         visible = false;
         rect.setEmpty();
@@ -76,12 +119,6 @@
     public boolean hasScaleChangedFrom(float v) {
         return (Float.compare(scale, v) != 0);
     }
-    public boolean hasTranslationXChangedFrom(float v) {
-        return (Float.compare(translationX, v) != 0);
-    }
-    public boolean hasTranslationYChangedFrom(float v) {
-        return (Float.compare(translationY, v) != 0);
-    }
     public boolean hasTranslationZChangedFrom(float v) {
         return (Float.compare(translationZ, v) != 0);
     }
@@ -95,12 +132,6 @@
             boolean requiresLayers = false;
 
             // Animate to the final state
-            if (hasTranslationXChangedFrom(v.getTranslationX())) {
-                anim.translationX(translationX);
-            }
-            if (hasTranslationYChangedFrom(v.getTranslationY())) {
-                anim.translationY(translationY);
-            }
             if (allowShadows && hasTranslationZChangedFrom(v.getTranslationZ())) {
                 anim.translationZ(translationZ);
             }
@@ -129,12 +160,6 @@
                     .start();
         } else {
             // Set the changed properties
-            if (hasTranslationXChangedFrom(v.getTranslationX())) {
-                v.setTranslationX(translationX);
-            }
-            if (hasTranslationYChangedFrom(v.getTranslationY())) {
-                v.setTranslationY(translationY);
-            }
             if (allowShadows && hasTranslationZChangedFrom(v.getTranslationZ())) {
                 v.setTranslationZ(translationZ);
             }
@@ -150,7 +175,8 @@
 
     /** Reset the transform on a view. */
     public static void reset(TaskView v) {
-        // Cancel any running animations
+        // Cancel any running animations and reset the translation in case something else (like a
+        // dismiss animation) changes it
         v.animate().cancel();
         v.setTranslationX(0f);
         v.setTranslationY(0f);
@@ -158,16 +184,15 @@
         v.setScaleX(1f);
         v.setScaleY(1f);
         v.setAlpha(1f);
-        v.getViewBounds().setClipRight(0, false /* forceUpdate */);
         v.getViewBounds().setClipBottom(0, false /* forceUpdate */);
+        v.setLeftTopRightBottom(0, 0, 0, 0);
         v.mThumbnailView.setBitmapScale(1f);
     }
 
     @Override
     public String toString() {
-        return "TaskViewTransform delay: " + startDelay +
-                " x: " + translationX + " y: " + translationY + " z: " + translationZ +
-                " scale: " + scale + " alpha: " + alpha + " visible: " + visible + " rect: " + rect +
-                " p: " + p;
+        return "TaskViewTransform delay: " + startDelay + " z: " + translationZ +
+                " scale: " + scale + " alpha: " + alpha + " visible: " + visible +
+                " rect: " + rect + " p: " + p;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
index 58de5d5..ef47d8d 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -62,7 +62,7 @@
         @Override
         public void run() {
             try {
-                ActivityManagerNative.getDefault().removeStack(DOCKED_STACK_ID);
+                ActivityManagerNative.getDefault().moveTasksToFullscreenStack(DOCKED_STACK_ID);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed to remove stack: " + e);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 879624e..cc6a29a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -127,8 +127,8 @@
 
     public static final boolean ENABLE_REMOTE_INPUT =
             SystemProperties.getBoolean("debug.enable_remote_input", true);
-    public static final boolean ENABLE_CHILD_NOTIFICATIONS = Build.IS_DEBUGGABLE
-                    && SystemProperties.getBoolean("debug.child_notifs", false);
+    public static final boolean ENABLE_CHILD_NOTIFICATIONS
+            = SystemProperties.getBoolean("debug.child_notifs", true);
 
     protected static final int MSG_SHOW_RECENT_APPS = 1019;
     protected static final int MSG_HIDE_RECENT_APPS = 1020;
@@ -1299,7 +1299,6 @@
             // Since the number of notifications is determined based on the height of the view, we
             // need to update them.
             updateRowStates();
-            mStackScroller.onHeightChanged(null, false);
         }
     }
 
@@ -1603,7 +1602,7 @@
     private void applyRemoteInput(final Entry entry) {
         if (!ENABLE_REMOTE_INPUT) return;
 
-        RemoteInput remoteInput = null;
+        boolean hasRemoteInput = false;
 
         Notification.Action[] actions = entry.notification.getNotification().actions;
         if (actions != null) {
@@ -1611,7 +1610,7 @@
                 if (a.getRemoteInputs() != null) {
                     for (RemoteInput ri : a.getRemoteInputs()) {
                         if (ri.getAllowFreeFormInput()) {
-                            remoteInput = ri;
+                            hasRemoteInput = true;
                             break;
                         }
                     }
@@ -1619,34 +1618,50 @@
             }
         }
 
-        // See if we have somewhere to put that remote input
-        if (remoteInput != null) {
-            View bigContentView = entry.getExpandedContentView();
-            if (bigContentView != null) {
-                inflateRemoteInput(bigContentView, entry);
-            }
-            View headsUpContentView = entry.getHeadsUpContentView();
-            if (headsUpContentView != null) {
-                inflateRemoteInput(headsUpContentView, entry);
-            }
+        View bigContentView = entry.getExpandedContentView();
+        if (bigContentView != null) {
+            applyRemoteInput(bigContentView, entry, hasRemoteInput);
+        }
+        View headsUpContentView = entry.getHeadsUpContentView();
+        if (headsUpContentView != null) {
+            applyRemoteInput(headsUpContentView, entry, hasRemoteInput);
         }
 
     }
 
-    private RemoteInputView inflateRemoteInput(View view, Entry entry) {
+    private RemoteInputView applyRemoteInput(View view, Entry entry, boolean hasRemoteInput) {
         View actionContainerCandidate = view.findViewById(
                 com.android.internal.R.id.actions_container);
         if (actionContainerCandidate instanceof FrameLayout) {
-            ViewGroup actionContainer = (FrameLayout) actionContainerCandidate;
-            RemoteInputView riv = inflateRemoteInputView(actionContainer, entry);
-            if (riv != null) {
-                riv.setVisibility(View.INVISIBLE);
-                actionContainer.addView(riv, new FrameLayout.LayoutParams(
-                        ViewGroup.LayoutParams.MATCH_PARENT,
-                        ViewGroup.LayoutParams.MATCH_PARENT)
-                );
-                riv.setBackgroundColor(entry.notification.getNotification().color);
-                return riv;
+            RemoteInputView existing = (RemoteInputView)
+                    view.findViewWithTag(RemoteInputView.VIEW_TAG);
+
+            if (hasRemoteInput) {
+                if (existing != null) {
+                    existing.onNotificationUpdate();
+                    return existing;
+                }
+
+                ViewGroup actionContainer = (FrameLayout) actionContainerCandidate;
+                RemoteInputView riv = inflateRemoteInputView(actionContainer, entry);
+                if (riv != null) {
+                    riv.setVisibility(View.INVISIBLE);
+                    actionContainer.addView(riv, new FrameLayout.LayoutParams(
+                            ViewGroup.LayoutParams.MATCH_PARENT,
+                            ViewGroup.LayoutParams.MATCH_PARENT)
+                    );
+                    int color = entry.notification.getNotification().color;
+                    if (color == Notification.COLOR_DEFAULT) {
+                        color = mContext.getColor(R.color.default_remote_input_background);
+                    }
+                    riv.setBackgroundColor(color);
+                    return riv;
+                }
+            } else {
+                if (existing != null) {
+                    existing.onNotificationUpdate();
+                    return null;
+                }
             }
         }
         return null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 5c79c7d..bd143ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -51,6 +51,8 @@
     private static final int COLORED_DIVIDER_ALPHA = 0x7B;
     private final LinearInterpolator mLinearInterpolator = new LinearInterpolator();
     private final int mNotificationMinHeightLegacy;
+    private final int mMaxHeadsUpHeightLegacy;
+    private final int mMaxHeadsUpHeight;
     private final int mNotificationMinHeight;
     private final int mNotificationMaxHeight;
     private int mRowMinHeight;
@@ -95,6 +97,7 @@
     private boolean mIsHeadsUp;
     private boolean mLastChronometerRunning = true;
     private NotificationHeaderView mNotificationHeader;
+    private NotificationViewWrapper mNotificationHeaderWrapper;
     private ViewStub mChildrenContainerStub;
     private NotificationGroupManager mGroupManager;
     private boolean mChildrenExpanded;
@@ -218,10 +221,15 @@
         boolean beforeN = mEntry.targetSdk < Build.VERSION_CODES.N;
         int minHeight = customView && beforeN && !mIsSummaryWithChildren ?
                 mNotificationMinHeightLegacy : mNotificationMinHeight;
+        boolean headsUpCustom = getPrivateLayout().getHeadsUpChild() != null &&
+                getPrivateLayout().getHeadsUpChild().getId()
+                != com.android.internal.R.id.status_bar_latest_event_content;
+        int headsUpheight = headsUpCustom && beforeN ? mMaxHeadsUpHeightLegacy
+                : mMaxHeadsUpHeight;
         mRowMinHeight = minHeight;
         mMaxViewHeight = mNotificationMaxHeight;
-        mPrivateLayout.setSmallHeight(mRowMinHeight);
-        mPublicLayout.setSmallHeight(mRowMinHeight);
+        mPrivateLayout.setHeights(mRowMinHeight, headsUpheight);
+        mPublicLayout.setHeights(mRowMinHeight, headsUpheight);
     }
 
     public StatusBarNotification getStatusBarNotification() {
@@ -385,6 +393,9 @@
     }
 
     public int getHeadsUpHeight() {
+        if (mIsSummaryWithChildren) {
+            return mChildrenContainer.getIntrinsicHeight();
+        }
         return mHeadsUpHeight;
     }
 
@@ -462,6 +473,10 @@
                 R.dimen.notification_min_height);
         mNotificationMaxHeight =  getResources().getDimensionPixelSize(
                 R.dimen.notification_max_height);
+        mMaxHeadsUpHeightLegacy =  getResources().getDimensionPixelSize(
+                R.dimen.notification_max_heads_up_height_legacy);
+        mMaxHeadsUpHeight =  getResources().getDimensionPixelSize(
+                R.dimen.notification_max_heads_up_height);
     }
 
     /**
@@ -570,6 +585,10 @@
         if (showing != null) {
             showing.setDark(dark, fade, delay);
         }
+        if (mIsSummaryWithChildren) {
+            mChildrenContainer.setDark(dark, fade, delay);
+            mNotificationHeaderWrapper.setDark(dark, fade, delay);
+        }
     }
 
     public boolean isExpandable() {
@@ -669,6 +688,9 @@
             mOnKeyguard = onKeyguard;
             logExpansionEvent(false, wasExpanded);
             if (wasExpanded != isExpanded()) {
+                if (mIsSummaryWithChildren) {
+                    mChildrenContainer.updateGroupOverflow();
+                }
                 notifyHeightChanged(false  /* needsAnimation */);
             }
         }
@@ -967,9 +989,12 @@
                     com.android.internal.R.id.expand_button);
             expandButton.setVisibility(VISIBLE);
             mNotificationHeader.setOnClickListener(mExpandClickListener);
+            mNotificationHeaderWrapper = NotificationViewWrapper.wrap(getContext(),
+                    mNotificationHeader);
             addView(mNotificationHeader);
         } else {
             header.reapply(getContext(), mNotificationHeader);
+            mNotificationHeaderWrapper.notifyContentUpdated();
         }
         updateHeaderExpandButton();
         updateChildrenHeaderAppearance();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index da01d54..2944c4f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -52,7 +52,6 @@
     private static final int VISIBLE_TYPE_SINGLELINE = 3;
 
     private final Rect mClipBounds = new Rect();
-    private final int mHeadsUpHeight;
     private final int mRoundRectRadius;
     private final Interpolator mLinearInterpolator = new LinearInterpolator();
     private final boolean mRoundRectClippingEnabled;
@@ -77,6 +76,7 @@
     private boolean mShowingLegacyBackground;
     private boolean mIsChildInGroup;
     private int mSmallHeight;
+    private int mHeadsUpHeight;
     private StatusBarNotification mStatusBarNotification;
     private NotificationGroupManager mGroupManager;
 
@@ -103,7 +103,6 @@
         super(context, attrs);
         mHybridViewManager = new HybridNotificationViewManager(getContext(), this);
         mFadePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
-        mHeadsUpHeight = getResources().getDimensionPixelSize(R.dimen.notification_mid_height);
         mRoundRectRadius = getResources().getDimensionPixelSize(
                 R.dimen.notification_material_rounded_rect_radius);
         mRoundRectClippingEnabled = getResources().getBoolean(
@@ -112,8 +111,9 @@
         setOutlineProvider(mOutlineProvider);
     }
 
-    public void setSmallHeight(int smallHeight) {
+    public void setHeights(int smallHeight, int headsUpMaxHeight) {
         mSmallHeight = smallHeight;
+        mHeadsUpHeight = headsUpMaxHeight;
     }
 
     @Override
@@ -150,7 +150,7 @@
             ViewGroup.LayoutParams layoutParams = mHeadsUpChild.getLayoutParams();
             if (layoutParams.height >= 0) {
                 // An actual height is set
-                size = Math.min(maxSize, layoutParams.height);
+                size = Math.min(size, layoutParams.height);
             }
             mHeadsUpChild.measure(widthMeasureSpec,
                     MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST));
@@ -283,10 +283,10 @@
     }
 
     public int getMaxHeight() {
-        if (mIsHeadsUp && mHeadsUpChild != null) {
-            return mHeadsUpChild.getHeight();
-        } else if (mExpandedChild != null) {
+        if (mExpandedChild != null) {
             return mExpandedChild.getHeight();
+        } else if (mIsHeadsUp && mHeadsUpChild != null) {
+            return mHeadsUpChild.getHeight();
         }
         return mSmallHeight;
     }
@@ -457,6 +457,9 @@
         if (mDark == dark || mContractedChild == null) return;
         mDark = dark;
         mContractedWrapper.setDark(dark && !mShowingLegacyBackground, fade, delay);
+        if (mSingleLineView != null) {
+            mSingleLineView.setDark(dark, fade, delay);
+        }
     }
 
     public void setHeadsUp(boolean headsUp) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderViewWrapper.java
new file mode 100644
index 0000000..ddad2e0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderViewWrapper.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2015 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.systemui.statusbar;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.drawable.Drawable;
+import android.view.NotificationHeaderView;
+import android.view.View;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.widget.ImageView;
+
+import com.android.systemui.R;
+import com.android.systemui.ViewInvertHelper;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
+
+import java.util.ArrayList;
+
+/**
+ * Wraps a notification header view.
+ */
+public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
+
+    private final ColorMatrix mGrayscaleColorMatrix = new ColorMatrix();
+    private final PorterDuffColorFilter mIconColorFilter = new PorterDuffColorFilter(
+            0, PorterDuff.Mode.SRC_ATOP);
+    private final int mIconDarkAlpha;
+    private final int mIconDarkColor = 0xffffffff;
+    protected final Interpolator mLinearOutSlowInInterpolator;
+    protected final ViewInvertHelper mInvertHelper;
+
+    protected int mColor;
+    private ImageView mIcon;
+
+    private ImageView mExpandButton;
+    private NotificationHeaderView mNotificationHeader;
+
+    protected NotificationHeaderViewWrapper(Context ctx, View view) {
+        super(view);
+        mIconDarkAlpha = ctx.getResources().getInteger(R.integer.doze_small_icon_alpha);
+        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(ctx,
+                android.R.interpolator.linear_out_slow_in);
+        mInvertHelper = new ViewInvertHelper(ctx, NotificationPanelView.DOZE_ANIMATION_DURATION);
+        resolveHeaderViews();
+    }
+
+    protected void resolveHeaderViews() {
+        mIcon = (ImageView) mView.findViewById(com.android.internal.R.id.icon);
+        mExpandButton = (ImageView) mView.findViewById(com.android.internal.R.id.expand_button);
+        mColor = resolveColor(mExpandButton);
+        mNotificationHeader = (NotificationHeaderView) mView.findViewById(
+                com.android.internal.R.id.notification_header);
+        for (int i = 0; i < mNotificationHeader.getChildCount(); i++) {
+            View child = mNotificationHeader.getChildAt(i);
+            if (child != mIcon) {
+                mInvertHelper.addTarget(child);
+            }
+        }
+    }
+
+    private int resolveColor(ImageView icon) {
+        if (icon != null && icon.getDrawable() != null) {
+            ColorFilter filter = icon.getDrawable().getColorFilter();
+            if (filter instanceof PorterDuffColorFilter) {
+                return ((PorterDuffColorFilter) filter).getColor();
+            }
+        }
+        return 0;
+    }
+
+    @Override
+    public void notifyContentUpdated() {
+        mInvertHelper.clearTargets();
+        // Reinspect the notification.
+        resolveHeaderViews();
+    }
+
+    @Override
+    public void setDark(boolean dark, boolean fade, long delay) {
+        if (fade) {
+            mInvertHelper.fade(dark, delay);
+        } else {
+            mInvertHelper.update(dark);
+        }
+        if (mIcon != null) {
+            boolean hadColorFilter = mNotificationHeader.getOriginalIconColor()
+                    != NotificationHeaderView.NO_COLOR;
+            if (fade) {
+                if (hadColorFilter) {
+                    fadeIconColorFilter(mIcon, dark, delay);
+                    fadeIconAlpha(mIcon, dark, delay);
+                } else {
+                    fadeGrayscale(mIcon, dark, delay);
+                }
+            } else {
+                if (hadColorFilter) {
+                    updateIconColorFilter(mIcon, dark);
+                    updateIconAlpha(mIcon, dark);
+                } else {
+                    updateGrayscale(mIcon, dark);
+                }
+            }
+        }
+    }
+
+    protected void startIntensityAnimation(ValueAnimator.AnimatorUpdateListener updateListener,
+            boolean dark, long delay, Animator.AnimatorListener listener) {
+        float startIntensity = dark ? 0f : 1f;
+        float endIntensity = dark ? 1f : 0f;
+        ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity);
+        animator.addUpdateListener(updateListener);
+        animator.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
+        animator.setInterpolator(mLinearOutSlowInInterpolator);
+        animator.setStartDelay(delay);
+        if (listener != null) {
+            animator.addListener(listener);
+        }
+        animator.start();
+    }
+
+    private void fadeIconColorFilter(final ImageView target, boolean dark, long delay) {
+        startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                updateIconColorFilter(target, (Float) animation.getAnimatedValue());
+            }
+        }, dark, delay, null /* listener */);
+    }
+
+    private void fadeIconAlpha(final ImageView target, boolean dark, long delay) {
+        startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                float t = (float) animation.getAnimatedValue();
+                target.setImageAlpha((int) (255 * (1f - t) + mIconDarkAlpha * t));
+            }
+        }, dark, delay, null /* listener */);
+    }
+
+    protected void fadeGrayscale(final ImageView target, final boolean dark, long delay) {
+        startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                updateGrayscaleMatrix((float) animation.getAnimatedValue());
+                target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
+            }
+        }, dark, delay, new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (!dark) {
+                    target.setColorFilter(null);
+                }
+            }
+        });
+    }
+
+    private void updateIconColorFilter(ImageView target, boolean dark) {
+        updateIconColorFilter(target, dark ? 1f : 0f);
+    }
+
+    private void updateIconColorFilter(ImageView target, float intensity) {
+        int color = interpolateColor(mColor, mIconDarkColor, intensity);
+        mIconColorFilter.setColor(color);
+        Drawable iconDrawable = target.getDrawable();
+
+        // Also, the notification might have been modified during the animation, so background
+        // might be null here.
+        if (iconDrawable != null) {
+            iconDrawable.mutate().setColorFilter(mIconColorFilter);
+        }
+    }
+
+    private void updateIconAlpha(ImageView target, boolean dark) {
+        target.setImageAlpha(dark ? mIconDarkAlpha : 255);
+    }
+
+    protected void updateGrayscale(ImageView target, boolean dark) {
+        if (dark) {
+            updateGrayscaleMatrix(1f);
+            target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
+        } else {
+            target.setColorFilter(null);
+        }
+    }
+
+    @Override
+    public void updateExpandability(boolean expandable, View.OnClickListener onClickListener) {
+        mExpandButton.setVisibility(expandable ? View.VISIBLE : View.GONE);
+        mNotificationHeader.setOnClickListener(expandable ? onClickListener : null);
+    }
+
+    private void updateGrayscaleMatrix(float intensity) {
+        mGrayscaleColorMatrix.setSaturation(1 - intensity);
+    }
+
+    private static int interpolateColor(int source, int target, float t) {
+        int aSource = Color.alpha(source);
+        int rSource = Color.red(source);
+        int gSource = Color.green(source);
+        int bSource = Color.blue(source);
+        int aTarget = Color.alpha(target);
+        int rTarget = Color.red(target);
+        int gTarget = Color.green(target);
+        int bTarget = Color.blue(target);
+        return Color.argb(
+                (int) (aSource * (1f - t) + aTarget * t),
+                (int) (rSource * (1f - t) + rTarget * t),
+                (int) (gSource * (1f - t) + gTarget * t),
+                (int) (bSource * (1f - t) + bTarget * t));
+    }
+
+    @Override
+    public NotificationHeaderView getNotificationHeader() {
+        return mNotificationHeader;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java
index fb0a419..77e8c55 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationTemplateViewWrapper.java
@@ -16,74 +16,31 @@
 
 package com.android.systemui.statusbar;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.content.Context;
-import android.content.res.ColorStateList;
 import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.text.TextUtils;
-import android.view.MotionEvent;
-import android.view.NotificationHeaderView;
 import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
 import android.widget.ImageView;
 import android.widget.ProgressBar;
-import android.widget.TextView;
-
-import com.android.systemui.R;
-import com.android.systemui.ViewInvertHelper;
-import com.android.systemui.statusbar.phone.NotificationPanelView;
-
-import java.util.ArrayList;
 
 /**
  * Wraps a notification view inflated from a template.
  */
-public class NotificationTemplateViewWrapper extends NotificationViewWrapper {
+public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapper {
 
-    private final ColorMatrix mGrayscaleColorMatrix = new ColorMatrix();
-    private final PorterDuffColorFilter mIconColorFilter = new PorterDuffColorFilter(
-            0, PorterDuff.Mode.SRC_ATOP);
-    private final int mIconDarkAlpha;
-    private final int mIconDarkColor = 0xffffffff;
-    private final int mDarkProgressTint = 0xffffffff;
-    private final Interpolator mLinearOutSlowInInterpolator;
+    private static final int mDarkProgressTint = 0xffffffff;
 
-    private int mColor;
-    private ViewInvertHelper mInvertHelper;
-    private ImageView mIcon;
     protected ImageView mPicture;
-
-    private ImageView mExpandButton;
-    private NotificationHeaderView mNotificationHeader;
     private ProgressBar mProgressBar;
 
     protected NotificationTemplateViewWrapper(Context ctx, View view) {
-        super(view);
-        mIconDarkAlpha = ctx.getResources().getInteger(R.integer.doze_small_icon_alpha);
-        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(ctx,
-                android.R.interpolator.linear_out_slow_in);
-
-        resolveViews();
+        super(ctx, view);
+        resolveTemplateViews();
     }
 
-    private void resolveViews() {
+    private void resolveTemplateViews() {
         View mainColumn = mView.findViewById(com.android.internal.R.id.notification_main_column);
-        mIcon = (ImageView) mView.findViewById(com.android.internal.R.id.icon);
         mPicture = (ImageView) mView.findViewById(com.android.internal.R.id.right_icon);
-        mExpandButton = (ImageView) mView.findViewById(com.android.internal.R.id.expand_button);
-        mColor = resolveColor(mExpandButton);
         final View progress = mView.findViewById(com.android.internal.R.id.progress);
         if (progress instanceof ProgressBar) {
             mProgressBar = (ProgressBar) progress;
@@ -91,30 +48,9 @@
             // It's still a viewstub
             mProgressBar = null;
         }
-        mNotificationHeader = (NotificationHeaderView) mView.findViewById(
-                com.android.internal.R.id.notification_header);
-        ArrayList<View> viewsToInvert = new ArrayList<>();
         if (mainColumn != null) {
-            viewsToInvert.add(mainColumn);
+            mInvertHelper.addTarget(mainColumn);
         }
-        for (int i = 0; i < mNotificationHeader.getChildCount(); i++) {
-            View child = mNotificationHeader.getChildAt(i);
-            if (child != mIcon) {
-                viewsToInvert.add(child);
-            }
-        }
-        mInvertHelper = new ViewInvertHelper(viewsToInvert,
-                NotificationPanelView.DOZE_ANIMATION_DURATION);
-    }
-
-    private int resolveColor(ImageView icon) {
-        if (icon != null && icon.getDrawable() != null) {
-            ColorFilter filter = icon.getDrawable().getColorFilter();
-            if (filter instanceof PorterDuffColorFilter) {
-                return ((PorterDuffColorFilter) filter).getColor();
-            }
-        }
-        return 0;
     }
 
     @Override
@@ -122,37 +58,12 @@
         super.notifyContentUpdated();
 
         // Reinspect the notification.
-        resolveViews();
+        resolveTemplateViews();
     }
 
     @Override
     public void setDark(boolean dark, boolean fade, long delay) {
-        if (mInvertHelper != null) {
-            if (fade) {
-                mInvertHelper.fade(dark, delay);
-            } else {
-                mInvertHelper.update(dark);
-            }
-        }
-        if (mIcon != null) {
-            boolean hadColorFilter = mNotificationHeader.getOriginalIconColor()
-                    != NotificationHeaderView.NO_COLOR;
-            if (fade) {
-                if (hadColorFilter) {
-                    fadeIconColorFilter(mIcon, dark, delay);
-                    fadeIconAlpha(mIcon, dark, delay);
-                } else {
-                    fadeGrayscale(mIcon, dark, delay);
-                }
-            } else {
-                if (hadColorFilter) {
-                    updateIconColorFilter(mIcon, dark);
-                    updateIconAlpha(mIcon, dark);
-                } else {
-                    updateGrayscale(mIcon, dark);
-                }
-            }
-        }
+        super.setDark(dark, fade, delay);
         setPictureGrayscale(dark, fade, delay);
         setProgressBarDark(dark, fade, delay);
     }
@@ -197,96 +108,6 @@
         }
     }
 
-    private void startIntensityAnimation(ValueAnimator.AnimatorUpdateListener updateListener,
-            boolean dark, long delay, Animator.AnimatorListener listener) {
-        float startIntensity = dark ? 0f : 1f;
-        float endIntensity = dark ? 1f : 0f;
-        ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity);
-        animator.addUpdateListener(updateListener);
-        animator.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
-        animator.setInterpolator(mLinearOutSlowInInterpolator);
-        animator.setStartDelay(delay);
-        if (listener != null) {
-            animator.addListener(listener);
-        }
-        animator.start();
-    }
-
-    private void fadeIconColorFilter(final ImageView target, boolean dark, long delay) {
-        startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                updateIconColorFilter(target, (Float) animation.getAnimatedValue());
-            }
-        }, dark, delay, null /* listener */);
-    }
-
-    private void fadeIconAlpha(final ImageView target, boolean dark, long delay) {
-        startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                float t = (float) animation.getAnimatedValue();
-                target.setImageAlpha((int) (255 * (1f - t) + mIconDarkAlpha * t));
-            }
-        }, dark, delay, null /* listener */);
-    }
-
-    protected void fadeGrayscale(final ImageView target, final boolean dark, long delay) {
-        startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                updateGrayscaleMatrix((float) animation.getAnimatedValue());
-                target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
-            }
-        }, dark, delay, new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                if (!dark) {
-                    target.setColorFilter(null);
-                }
-            }
-        });
-    }
-
-    private void updateIconColorFilter(ImageView target, boolean dark) {
-        updateIconColorFilter(target, dark ? 1f : 0f);
-    }
-
-    private void updateIconColorFilter(ImageView target, float intensity) {
-        int color = interpolateColor(mColor, mIconDarkColor, intensity);
-        mIconColorFilter.setColor(color);
-        Drawable iconDrawable = target.getDrawable();
-
-        // Also, the notification might have been modified during the animation, so background
-        // might be null here.
-        if (iconDrawable != null) {
-            iconDrawable.mutate().setColorFilter(mIconColorFilter);
-        }
-    }
-
-    private void updateIconAlpha(ImageView target, boolean dark) {
-        target.setImageAlpha(dark ? mIconDarkAlpha : 255);
-    }
-
-    protected void updateGrayscale(ImageView target, boolean dark) {
-        if (dark) {
-            updateGrayscaleMatrix(1f);
-            target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
-        } else {
-            target.setColorFilter(null);
-        }
-    }
-
-    @Override
-    public void updateExpandability(boolean expandable, View.OnClickListener onClickListener) {
-        mExpandButton.setVisibility(expandable ? View.VISIBLE : View.GONE);
-        mNotificationHeader.setOnClickListener(expandable ? onClickListener : null);
-    }
-
-    private void updateGrayscaleMatrix(float intensity) {
-        mGrayscaleColorMatrix.setSaturation(1 - intensity);
-    }
-
     private static int interpolateColor(int source, int target, float t) {
         int aSource = Color.alpha(source);
         int rSource = Color.red(source);
@@ -302,9 +123,4 @@
                 (int) (gSource * (1f - t) + gTarget * t),
                 (int) (bSource * (1f - t) + bTarget * t));
     }
-
-    @Override
-    public NotificationHeaderView getNotificationHeader() {
-        return mNotificationHeader;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java
index 119d57b..61499de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewWrapper.java
@@ -26,16 +26,13 @@
  */
 public abstract class NotificationViewWrapper {
 
-    private static final String TAG_BIG_MEDIA_NARROW = "bigMediaNarrow";
-    private static final String TAG_MEDIA = "media";
-    private static final String TAG_BIG_PICTURE = "bigPicture";
-
     protected final View mView;
-    private boolean mSubTextVisible = true;
 
     public static NotificationViewWrapper wrap(Context ctx, View v) {
         if (v.getId() == com.android.internal.R.id.status_bar_latest_event_content) {
             return new NotificationTemplateViewWrapper(ctx, v);
+        } else if (v instanceof NotificationHeaderView) {
+            return new NotificationHeaderViewWrapper(ctx, v);
         } else {
             return new NotificationCustomViewWrapper(v);
         }
@@ -57,9 +54,7 @@
     /**
      * Notifies this wrapper that the content of the view might have changed.
      */
-    public void notifyContentUpdated() {
-        setSubTextVisible(mSubTextVisible);
-    }
+    public void notifyContentUpdated() {};
 
     /**
      * @return true if this template might need to be clipped with a round rect to make it look
@@ -70,14 +65,6 @@
     }
 
     /**
-     * Change the subTextVisibility
-     * @param visible Should the subtext be visible
-     */
-    public void setSubTextVisible(boolean visible) {
-        mSubTextVisible = visible;
-    }
-
-    /**
      * Update the appearance of the expand button.
      *
      * @param expandable should this view be expandable
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java
index fafea98..5fb6fec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/HybridNotificationView.java
@@ -23,6 +23,8 @@
 
 import com.android.keyguard.AlphaOptimizedLinearLayout;
 import com.android.systemui.R;
+import com.android.systemui.ViewInvertHelper;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
 
 /**
  * A hybrid view which may contain information about one ore more notifications.
@@ -31,6 +33,7 @@
 
     protected TextView mTitleView;
     protected TextView mTextView;
+    private ViewInvertHelper mInvertHelper;
 
     public HybridNotificationView(Context context) {
         this(context, null);
@@ -54,6 +57,7 @@
         super.onFinishInflate();
         mTitleView = (TextView) findViewById(R.id.notification_title);
         mTextView = (TextView) findViewById(R.id.notification_text);
+        mInvertHelper = new ViewInvertHelper(this, NotificationPanelView.DOZE_ANIMATION_DURATION);
     }
 
     public void bind(CharSequence title) {
@@ -65,4 +69,8 @@
         mTextView.setText(text);
         requestLayout();
     }
+
+    public void setDark(boolean dark, boolean fade, long delay) {
+        mInvertHelper.setInverted(dark, fade, delay);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
index a2616fe..d35e57b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -270,7 +270,7 @@
     public void onTuningChanged(String key, String newValue) {
         switch (key) {
             case KEY_DOCK_WINDOW_GESTURE:
-                mDockWindowEnabled = (newValue != null) &&
+                mDockWindowEnabled = (newValue == null) ||
                         (Integer.parseInt(newValue) != 0);
                 break;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 73ee363..6a2b32d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -44,7 +44,6 @@
 import android.view.animation.PathInterpolator;
 import android.widget.FrameLayout;
 import android.widget.TextView;
-
 import com.android.internal.logging.MetricsLogger;
 import com.android.keyguard.KeyguardStatusView;
 import com.android.systemui.DejankUtils;
@@ -65,7 +64,6 @@
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.stack.StackStateAnimator;
-import com.android.systemui.tuner.TunerService;
 
 import java.util.List;
 
@@ -73,7 +71,7 @@
         ExpandableView.OnHeightChangedListener, ObservableScrollView.Listener,
         View.OnClickListener, NotificationStackScrollLayout.OnOverscrollTopChangedListener,
         KeyguardAffordanceHelper.Callback, NotificationStackScrollLayout.OnEmptySpaceClickListener,
-        HeadsUpManager.OnHeadsUpChangedListener, TunerService.Tunable {
+        HeadsUpManager.OnHeadsUpChangedListener {
 
     private static final boolean DEBUG = false;
 
@@ -221,13 +219,10 @@
     private final Interpolator mTouchResponseInterpolator =
             new PathInterpolator(0.3f, 0f, 0.1f, 1f);
 
-    private boolean mNewQs;
-
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
         setWillNotDraw(!DEBUG);
         mFalsingManager = FalsingManager.getInstance(context);
-        TunerService.get(context).addTunable(this, QSPanel.QS_THE_NEW_QS);
     }
 
     public void setStatusBar(PhoneStatusBar bar) {
@@ -235,26 +230,10 @@
     }
 
     @Override
-    public void onTuningChanged(String key, String newValue) {
-        if (QSPanel.QS_THE_NEW_QS.equals(key)) {
-            boolean b = newValue != null && Integer.parseInt(newValue) != 0;
-            if (mNewQs != b) {
-                if (mHeader != null) {
-                    // We are too late, no good way to re-initialize yet, just die and come back up.
-                    android.os.Process.killProcess(android.os.Process.myPid());
-                } else {
-                    mNewQs = b;
-                }
-            }
-        }
-    }
-
-    @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
         ViewStub stub = (ViewStub) findViewById(R.id.status_bar_header);
-        stub.setLayoutResource(mNewQs
-                ? R.layout.quick_status_bar_expanded_header : R.layout.status_bar_expanded_header);
+        stub.setLayoutResource(R.layout.quick_status_bar_expanded_header);
         mHeader = (BaseStatusBarHeader) stub.inflate();
         mHeader.setOnClickListener(this);
         mKeyguardStatusBar = (KeyguardStatusBarView) findViewById(R.id.keyguard_header);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index c740b08..7f27ba7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -133,7 +133,7 @@
         TunerService.get(mContext).addTunable(this, TILES_SETTING);
     }
 
-    PhoneStatusBar getPhoneStatusBar() {
+    public PhoneStatusBar getPhoneStatusBar() {
         return mStatusBar;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
index 26ff97a..1372cca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
@@ -24,12 +24,14 @@
 import android.graphics.drawable.Animatable;
 import android.graphics.drawable.RippleDrawable;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.Switch;
 import android.widget.TextView;
 import android.widget.Toast;
+import com.android.keyguard.KeyguardStatusView;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSPanel;
 import com.android.systemui.qs.QSTile;
@@ -42,6 +44,7 @@
 public class QuickStatusBarHeader extends BaseStatusBarHeader implements
         NextAlarmController.NextAlarmChangeCallback, View.OnClickListener {
 
+    private static final String TAG = "QuickStatusBarHeader";
     private ActivityStarter mActivityStarter;
     private NextAlarmController mNextAlarmController;
     private SettingsButton mSettingsButton;
@@ -59,11 +62,15 @@
 
     private boolean mDetailTransitioning;
     private ViewGroup mExpandedGroup;
+    private ViewGroup mDateTimeGroup;
+    private View mEmergencyOnly;
     private TextView mQsDetailHeaderTitle;
     private boolean mListening;
     private AlarmManager.AlarmClockInfo mNextAlarm;
 
     private QuickQSPanel mHeaderQsPanel;
+    private boolean mShowEmergencyCallsOnly;
+    private float mDateTimeTranslation;
 
     public QuickStatusBarHeader(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -73,6 +80,10 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
 
+        mEmergencyOnly = findViewById(R.id.header_emergency_calls_only);
+        mDateTimeTranslation = mContext.getResources().getDimension(
+                R.dimen.qs_date_anim_translation);
+        mDateTimeGroup = (ViewGroup) findViewById(R.id.date_time_group);
         mExpandedGroup = (ViewGroup) findViewById(R.id.expanded_group);
 
         mHeaderQsPanel = (QuickQSPanel) findViewById(R.id.quick_qs_panel);
@@ -118,14 +129,15 @@
     @Override
     public void setExpanded(boolean expanded) {
         mExpanded = expanded;
+        updateEverything();
     }
 
     @Override
     public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) {
         mNextAlarm = nextAlarm;
+        Log.d(TAG, "Got alarm update " + (nextAlarm != null));
         if (nextAlarm != null) {
-            // TODO:...
-//            mAlarmStatus.setText(KeyguardStatusView.formatNextAlarm(getContext(), nextAlarm));
+            mAlarmStatus.setText(KeyguardStatusView.formatNextAlarm(getContext(), nextAlarm));
         }
         mAlarmShowing = nextAlarm != null;
         updateEverything();
@@ -137,6 +149,9 @@
         mExpandedGroup.setVisibility(headerExpansionFraction > 0 ? View.VISIBLE : View.INVISIBLE);
         mHeaderQsPanel.setAlpha(1 - headerExpansionFraction);
         mHeaderQsPanel.setVisibility(headerExpansionFraction < 1 ? View.VISIBLE : View.INVISIBLE);
+
+        mDateTimeGroup.setTranslationY(headerExpansionFraction * mDateTimeTranslation);
+        mEmergencyOnly.setAlpha(headerExpansionFraction);
     }
 
     public void setListening(boolean listening) {
@@ -154,16 +169,20 @@
     }
 
     private void updateVisibilities() {
-        mAlarmStatus.setVisibility(mExpanded && mAlarmShowing ? View.VISIBLE : View.GONE);
+        mAlarmStatus.setVisibility(mAlarmShowing ? View.VISIBLE : View.GONE);
         mQsDetailHeader.setVisibility(mExpanded && mShowingDetail ? View.VISIBLE : View.INVISIBLE);
+        mEmergencyOnly.setVisibility(mExpanded && mShowEmergencyCallsOnly
+                ? View.VISIBLE : View.INVISIBLE);
         mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
                 TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE);
     }
 
     private void updateListeners() {
         if (mListening) {
+            Log.d(TAG, "Listening for Alarms");
             mNextAlarmController.addStateChangedCallback(this);
         } else {
+            Log.d(TAG, "Not listening for Alarms");
             mNextAlarmController.removeStateChangedCallback(this);
         }
     }
@@ -193,7 +212,7 @@
                 host.getBatteryController());
         mHeaderQsPanel.setQSPanelAndHeader(mQsPanel, this);
         mHeaderQsPanel.setHost(myHost);
-        mHeaderQsPanel.setMaxTiles(3);
+        mHeaderQsPanel.setMaxTiles(5);
         mHeaderQsPanel.setTiles(myHost.getTiles());
         myHost.addCallback(new QSTile.Host.Callback() {
             @Override
@@ -250,8 +269,14 @@
     }
 
     @Override
-    public void setEmergencyCallsOnly(boolean emergencyOnly) {
-        // Don't care.
+    public void setEmergencyCallsOnly(boolean show) {
+        boolean changed = show != mShowEmergencyCallsOnly;
+        if (changed) {
+            mShowEmergencyCallsOnly = show;
+            if (mExpanded) {
+                updateEverything();
+            }
+        }
     }
 
     private final QSPanel.Callback mQsPanelCallback = new QSPanel.Callback() {
@@ -308,6 +333,7 @@
 
         private void handleShowingDetail(final QSTile.DetailAdapter detail) {
             final boolean showingDetail = detail != null;
+            transition(mDateTimeGroup, !showingDetail);
             transition(mExpandedGroup, !showingDetail);
             if (mAlarmShowing) {
                 transition(mAlarmStatus, !showingDetail);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index 6cda561..3d21f44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -59,7 +59,7 @@
  */
 public class StatusBarHeaderView extends BaseStatusBarHeader implements View.OnClickListener,
         BatteryController.BatteryStateChangeCallback, NextAlarmController.NextAlarmChangeCallback,
-        EmergencyListener, TunerService.Tunable {
+        EmergencyListener {
 
     private boolean mExpanded;
     private boolean mListening;
@@ -234,28 +234,6 @@
         updateClockCollapsedMargin();
     }
 
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        TunerService.get(mContext).addTunable(this, QSPanel.QS_THE_NEW_QS);
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        TunerService.get(mContext).removeTunable(this);
-    }
-
-    @Override
-    public void onTuningChanged(String key, String newValue) {
-        if (QSPanel.QS_THE_NEW_QS.equals(key)) {
-            mAllowExpand = newValue == null || Integer.parseInt(newValue) == 0;
-            if (!mAllowExpand) {
-                setExpanded(false);
-            }
-        }
-    }
-
     private void updateClockCollapsedMargin() {
         Resources res = getResources();
         int padding = res.getDimensionPixelSize(R.dimen.clock_collapsed_bottom_margin);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 22c0cb9..65053f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -28,6 +28,8 @@
 import android.content.Intent;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextWatcher;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.KeyEvent;
@@ -49,7 +51,7 @@
 /**
  * Host for the remote input.
  */
-public class RemoteInputView extends LinearLayout implements View.OnClickListener {
+public class RemoteInputView extends LinearLayout implements View.OnClickListener, TextWatcher {
 
     private static final String TAG = "RemoteInput";
 
@@ -101,6 +103,7 @@
             }
         });
         mEditText.setOnClickListener(this);
+        mEditText.addTextChangedListener(this);
         mEditText.setInnerFocusable(false);
         mEditText.mDefocusListener = this;
     }
@@ -115,6 +118,8 @@
         mEditText.setEnabled(false);
         mSendButton.setVisibility(INVISIBLE);
         mProgressBar.setVisibility(VISIBLE);
+        mController.removeRemoteInput(mEntry);
+        mEditText.mShowImeOnInputConnection = false;
 
         try {
             mPendingIntent.send(mContext, 0, fillInIntent);
@@ -175,6 +180,40 @@
         mEditText.setText(mEntry.remoteInputText);
         mEditText.setSelection(mEditText.getText().length());
         mEditText.requestFocus();
+        updateSendButton();
+    }
+
+    public void onNotificationUpdate() {
+        boolean sending = mProgressBar.getVisibility() == VISIBLE;
+
+        if (sending) {
+            // Update came in after we sent the reply, time to reset.
+            reset();
+        }
+    }
+
+    private void reset() {
+        mEditText.getText().clear();
+        mEditText.setEnabled(true);
+        mSendButton.setVisibility(VISIBLE);
+        mProgressBar.setVisibility(INVISIBLE);
+        updateSendButton();
+        onDefocus();
+    }
+
+    private void updateSendButton() {
+        mSendButton.setEnabled(mEditText.getText().length() != 0);
+    }
+
+    @Override
+    public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+
+    @Override
+    public void onTextChanged(CharSequence s, int start, int before, int count) {}
+
+    @Override
+    public void afterTextChanged(Editable s) {
+        updateSendButton();
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index 9015a0e..2b71ce9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -23,9 +23,11 @@
 import android.view.ViewGroup;
 
 import com.android.systemui.R;
+import com.android.systemui.ViewInvertHelper;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.HybridNotificationView;
 import com.android.systemui.statusbar.notification.HybridNotificationViewManager;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -49,6 +51,7 @@
     private final int mNotificatonTopPadding;
     private final HybridNotificationViewManager mHybridViewManager;
     private final float mCollapsedBottompadding;
+    private ViewInvertHelper mOverflowInvertHelper;
     private boolean mChildrenExpanded;
     private ExpandableNotificationRow mNotificationParent;
     private HybridNotificationView mGroupOverflowContainer;
@@ -172,12 +175,17 @@
         if (hasOverflow) {
             mGroupOverflowContainer = mHybridViewManager.bindFromNotificationGroup(
                     mGroupOverflowContainer, mChildren, lastVisibleIndex + 1);
+            if (mOverflowInvertHelper == null) {
+                mOverflowInvertHelper= new ViewInvertHelper(mGroupOverflowContainer,
+                        NotificationPanelView.DOZE_ANIMATION_DURATION);
+            }
             if (mGroupOverFlowState == null) {
                 mGroupOverFlowState = new ViewState();
             }
         } else if (mGroupOverflowContainer != null) {
             removeView(mGroupOverflowContainer);
             mGroupOverflowContainer = null;
+            mOverflowInvertHelper = null;
             mGroupOverFlowState = null;
         }
     }
@@ -324,7 +332,7 @@
         if (!likeCollapsed && (mChildrenExpanded || mNotificationParent.isUserLocked())) {
             return NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED;
         }
-        if (mNotificationParent.isExpanded()) {
+        if (mNotificationParent.isExpanded() || mNotificationParent.isHeadsUp()) {
             return NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED;
         }
         return NUMBER_OF_CHILDREN_WHEN_COLLAPSED;
@@ -387,16 +395,17 @@
             boolean withDelays, long baseDelay, long duration) {
         int childCount = mChildren.size();
         ViewState tmpState = new ViewState();
-        int notGoneIndex = 0;
-        for (int i = 0; i < childCount; i++) {
+        int delayIndex = 0;
+        int maxAllowChildCount = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
+        for (int i = childCount - 1; i >= 0; i--) {
             ExpandableNotificationRow child = mChildren.get(i);
             StackViewState viewState = state.getViewStateForView(child);
             int difference = Math.min(StackStateAnimator.DELAY_EFFECT_MAX_INDEX_DIFFERENCE_CHILDREN,
-                    notGoneIndex + 1);
+                    delayIndex);
             long delay = withDelays
                     ? difference * StackStateAnimator.ANIMATION_DELAY_PER_ELEMENT_EXPAND_CHILDREN
                     : 0;
-            delay += baseDelay;
+            delay = (long) (delay * (mChildrenExpanded ? 1.0f : 0.5f) + baseDelay);
             stateAnimator.startStackAnimations(child, viewState, state, -1, delay);
 
             // layout the divider
@@ -405,11 +414,13 @@
             tmpState.yTranslation = viewState.yTranslation - mDividerHeight;
             tmpState.alpha = mChildrenExpanded && viewState.alpha != 0 ? 0.5f : 0;
             stateAnimator.startViewAnimations(divider, tmpState, delay, duration);
-
-            notGoneIndex++;
+            if (i < maxAllowChildCount) {
+                delayIndex++;
+            }
         }
         if (mGroupOverflowContainer != null) {
-            stateAnimator.startViewAnimations(mGroupOverflowContainer, mGroupOverFlowState, -1, 0);
+            stateAnimator.startViewAnimations(mGroupOverflowContainer, mGroupOverFlowState,
+                    baseDelay, duration);
         }
     }
 
@@ -443,4 +454,10 @@
     public int getMinHeight() {
         return getIntrinsicHeight(getMaxAllowedVisibleChildren(true /* forceCollapsed */));
     }
+
+    public void setDark(boolean dark, boolean fade, long delay) {
+        if (mGroupOverflowContainer != null) {
+            mOverflowInvertHelper.setInverted(dark, fade, delay);
+        }
+    }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 11fdbb5..8cf25b3 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -3987,6 +3987,9 @@
         private final Uri mDisplayDaltonizerUri = Settings.Secure.getUriFor(
                 Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER);
 
+        private final Uri mDisplayColorMatrixUri = Settings.Secure.getUriFor(
+                Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX);
+
         private final Uri mHighTextContrastUri = Settings.Secure.getUriFor(
                 Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED);
 
@@ -4017,6 +4020,8 @@
             contentResolver.registerContentObserver(
                     mDisplayDaltonizerUri, false, this, UserHandle.USER_ALL);
             contentResolver.registerContentObserver(
+                    mDisplayColorMatrixUri, false, this, UserHandle.USER_ALL);
+            contentResolver.registerContentObserver(
                     mHighTextContrastUri, false, this, UserHandle.USER_ALL);
         }
 
@@ -4066,6 +4071,8 @@
                     if (readDisplayColorAdjustmentSettingsLocked(userState)) {
                         updateDisplayColorAdjustmentSettingsLocked(userState);
                     }
+                } else if (mDisplayColorMatrixUri.equals(uri)) {
+                    updateDisplayColorAdjustmentSettingsLocked(userState);
                 } else if (mHighTextContrastUri.equals(uri)) {
                     if (readHighTextContrastEnabledSettingLocked(userState)) {
                         onUserStateChangedLocked(userState);
diff --git a/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java b/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java
index d0b5898..1a7de25 100644
--- a/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java
+++ b/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java
@@ -107,9 +107,24 @@
             setDaltonizerMode(AccessibilityManager.DALTONIZER_DISABLED);
         }
 
+        String matrix = Settings.Secure.getStringForUser(cr,
+                Settings.Secure.ACCESSIBILITY_DISPLAY_COLOR_MATRIX, userId);
+        if (matrix != null) {
+            colorMatrix = multiply(colorMatrix, getMatrix(matrix));
+        }
+
         setColorTransform(colorMatrix);
     }
 
+    private static float[] getMatrix(String matrix) {
+        String[] strValues = matrix.split(",");
+        float[] values = new float[strValues.length];
+        for (int i = 0; i < values.length; i++) {
+            values[i] = Float.parseFloat(strValues[i]);
+        }
+        return values;
+    }
+
     private static float[] multiply(float[] matrix, float[] other) {
         if (matrix == null) {
             return other;
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 960fb4b..5f57a76 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1233,14 +1233,6 @@
                 }
             }
 
-            // direct-callback alarms must be wakeup alarms (otherwise they should just be
-            // posting work to a Handler)
-            if (directReceiver != null) {
-                if (type != RTC_WAKEUP && type != ELAPSED_REALTIME_WAKEUP) {
-                    throw new IllegalArgumentException("Only wakeup alarms can use AlarmReceivers");
-                }
-            }
-
             if (workSource != null) {
                 getContext().enforcePermission(
                         android.Manifest.permission.UPDATE_DEVICE_STATS,
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index ede92fb..7fcedc6 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -788,6 +788,9 @@
 
     @Override
     public void startWatchingMode(int op, String packageName, IAppOpsCallback callback) {
+        if (callback == null) {
+            return;
+        }
         synchronized (this) {
             op = (op != AppOpsManager.OP_NONE) ? AppOpsManager.opToSwitch(op) : op;
             Callback cb = mModeWatchers.get(callback.asBinder());
@@ -816,6 +819,9 @@
 
     @Override
     public void stopWatchingMode(IAppOpsCallback callback) {
+        if (callback == null) {
+            return;
+        }
         synchronized (this) {
             Callback cb = mModeWatchers.remove(callback.asBinder());
             if (cb != null) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index c712a56..ed64c2b 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -34,6 +34,7 @@
 
 import android.annotation.Nullable;
 import android.app.AlarmManager;
+import android.app.BroadcastOptions;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -72,6 +73,7 @@
 import android.net.UidRange;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.FileUtils;
 import android.os.Handler;
@@ -1529,6 +1531,7 @@
                 log("sendStickyBroadcast: action=" + intent.getAction());
             }
 
+            Bundle options = null;
             final long ident = Binder.clearCallingIdentity();
             if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
                 final NetworkInfo ni = intent.getParcelableExtra(
@@ -1536,6 +1539,10 @@
                 if (ni.getType() == ConnectivityManager.TYPE_MOBILE_SUPL) {
                     intent.setAction(ConnectivityManager.CONNECTIVITY_ACTION_SUPL);
                     intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+                } else {
+                    BroadcastOptions opts = BroadcastOptions.makeBasic();
+                    opts.setMaxManifestReceiverApiLevel(Build.VERSION_CODES.M);
+                    options = opts.toBundle();
                 }
                 final IBatteryStats bs = BatteryStatsService.getService();
                 try {
@@ -1546,7 +1553,7 @@
                 }
             }
             try {
-                mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+                mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL, options);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 45c1ed2..ef79cfe 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -3027,7 +3027,7 @@
 
             final List<ImeSubtypeListItem> imList =
                     mSwitchingController.getSortedInputMethodAndSubtypeListLocked(
-                            true /* showSubtypes */, showAuxSubtypes, isScreenLocked);
+                            showAuxSubtypes, isScreenLocked);
 
             if (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID) {
                 final InputMethodSubtype currentSubtype = getCurrentInputMethodSubtypeLocked();
@@ -3536,6 +3536,7 @@
         private static final String ATTR_LABEL = "label";
         private static final String ATTR_ICON = "icon";
         private static final String ATTR_IME_SUBTYPE_LOCALE = "imeSubtypeLocale";
+        private static final String ATTR_IME_SUBTYPE_LANGUAGE_TAG = "languageTag";
         private static final String ATTR_IME_SUBTYPE_MODE = "imeSubtypeMode";
         private static final String ATTR_IME_SUBTYPE_EXTRA_VALUE = "imeSubtypeExtraValue";
         private static final String ATTR_IS_AUXILIARY = "isAuxiliary";
@@ -3629,6 +3630,8 @@
                         out.attribute(null, ATTR_ICON, String.valueOf(subtype.getIconResId()));
                         out.attribute(null, ATTR_LABEL, String.valueOf(subtype.getNameResId()));
                         out.attribute(null, ATTR_IME_SUBTYPE_LOCALE, subtype.getLocale());
+                        out.attribute(null, ATTR_IME_SUBTYPE_LANGUAGE_TAG,
+                                subtype.getLanguageTag());
                         out.attribute(null, ATTR_IME_SUBTYPE_MODE, subtype.getMode());
                         out.attribute(null, ATTR_IME_SUBTYPE_EXTRA_VALUE, subtype.getExtraValue());
                         out.attribute(null, ATTR_IS_AUXILIARY,
@@ -3690,6 +3693,8 @@
                                 parser.getAttributeValue(null, ATTR_LABEL));
                         final String imeSubtypeLocale =
                                 parser.getAttributeValue(null, ATTR_IME_SUBTYPE_LOCALE);
+                        final String languageTag =
+                                parser.getAttributeValue(null, ATTR_IME_SUBTYPE_LANGUAGE_TAG);
                         final String imeSubtypeMode =
                                 parser.getAttributeValue(null, ATTR_IME_SUBTYPE_MODE);
                         final String imeSubtypeExtraValue =
@@ -3700,6 +3705,7 @@
                                 .setSubtypeNameResId(label)
                                 .setSubtypeIconResId(icon)
                                 .setSubtypeLocale(imeSubtypeLocale)
+                                .setLanguageTag(languageTag)
                                 .setSubtypeMode(imeSubtypeMode)
                                 .setSubtypeExtraValue(imeSubtypeExtraValue)
                                 .setIsAuxiliary(isAuxiliary)
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e4a6f3c..9a185bb 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -490,6 +490,8 @@
 
     // Used to indicate that a task is removed it should also be removed from recents.
     private static final boolean REMOVE_FROM_RECENTS = true;
+    // Used to indicate that an app transition should be animated.
+    private static final boolean ANIMATE = true;
 
     private static native int nativeMigrateToBoost();
     private static native int nativeMigrateFromBoost();
@@ -3484,27 +3486,29 @@
                 Watchdog.getInstance().processStarted(app.processName, startResult.pid);
             }
 
-            checkTime(startTime, "startProcess: building log message");
-            StringBuilder buf = mStringBuilder;
-            buf.setLength(0);
-            buf.append("Start proc ");
-            buf.append(startResult.pid);
-            buf.append(':');
-            buf.append(app.processName);
-            buf.append('/');
-            UserHandle.formatUid(buf, uid);
-            if (!isActivityProcess) {
-                buf.append(" [");
-                buf.append(entryPoint);
-                buf.append("]");
+            if (DEBUG_PROCESSES) {
+                checkTime(startTime, "startProcess: building log message");
+                StringBuilder buf = mStringBuilder;
+                buf.setLength(0);
+                buf.append("Start proc ");
+                buf.append(startResult.pid);
+                buf.append(':');
+                buf.append(app.processName);
+                buf.append('/');
+                UserHandle.formatUid(buf, uid);
+                if (!isActivityProcess) {
+                    buf.append(" [");
+                    buf.append(entryPoint);
+                    buf.append("]");
+                }
+                buf.append(" for ");
+                buf.append(hostingType);
+                if (hostingNameStr != null) {
+                    buf.append(" ");
+                    buf.append(hostingNameStr);
+                }
+                Slog.i(TAG, buf.toString());
             }
-            buf.append(" for ");
-            buf.append(hostingType);
-            if (hostingNameStr != null) {
-                buf.append(" ");
-                buf.append(hostingNameStr);
-            }
-            Slog.i(TAG, buf.toString());
             app.setPid(startResult.pid);
             app.usingWrapper = startResult.usingWrapper;
             app.removed = false;
@@ -4331,7 +4335,7 @@
                 if (task.stack.mStackId != launchStackId) {
                     mStackSupervisor.moveTaskToStackLocked(
                             taskId, launchStackId, ON_TOP, FORCE_FOCUS, "startActivityFromRecents",
-                            true /* animate */);
+                            ANIMATE);
                 }
             }
 
@@ -9312,7 +9316,7 @@
                 if (DEBUG_STACK) Slog.d(TAG_STACK, "moveActivityToStack: moving r=" + r
                         + " to stackId=" + stackId);
                 mStackSupervisor.moveTaskToStackLocked(r.task.taskId, stackId, ON_TOP, !FORCE_FOCUS,
-                        "moveActivityToStack", true /* animate */);
+                        "moveActivityToStack", ANIMATE);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -9333,7 +9337,7 @@
                 if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToStack: moving task=" + taskId
                         + " to stackId=" + stackId + " toTop=" + toTop);
                 mStackSupervisor.moveTaskToStackLocked(taskId, stackId, toTop, !FORCE_FOCUS,
-                        "moveTaskToStack", true /* animate */);
+                        "moveTaskToStack", ANIMATE);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -17777,20 +17781,20 @@
     }
 
     @Override
-    public void removeStack(int stackId) {
+    public void moveTasksToFullscreenStack(int fromStackId) {
         enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
-                "detahStack()");
-        if (stackId == HOME_STACK_ID) {
-            throw new IllegalArgumentException("Removing home stack is not allowed.");
+                "moveTasksToFullscreenStack()");
+        if (fromStackId == HOME_STACK_ID) {
+            throw new IllegalArgumentException("You can't move tasks from the home stack.");
         }
         synchronized (this) {
-            long origId = Binder.clearCallingIdentity();
-            ActivityStack stack = mStackSupervisor.getStack(stackId);
+            final long origId = Binder.clearCallingIdentity();
+            final ActivityStack stack = mStackSupervisor.getStack(fromStackId);
             if (stack != null) {
-                ArrayList<TaskRecord> tasks = stack.getAllTasks();
+                final ArrayList<TaskRecord> tasks = stack.getAllTasks();
                 for (int i = tasks.size() - 1; i >= 0; i--) {
-                    removeTaskByIdLocked(tasks.get(i).taskId, false /* killProcess */,
-                            !REMOVE_FROM_RECENTS);
+                    mStackSupervisor.positionTaskInStackLocked(tasks.get(i).taskId,
+                            FULLSCREEN_WORKSPACE_STACK_ID, 0);
                 }
             }
             Binder.restoreCallingIdentity(origId);
@@ -19748,7 +19752,7 @@
                         } else {
                             numEmpty++;
                             if (numEmpty > emptyProcessLimit) {
-                                app.kill("empty #" + numEmpty, true);
+                                app.kill("empty #" + numEmpty, DEBUG_PROCESSES);
                             }
                         }
                         break;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index b052d17..9e32efa 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -74,6 +74,7 @@
 import android.os.UserHandle;
 import android.service.voice.IVoiceInteractionSession;
 import android.util.EventLog;
+import android.util.Log;
 import android.util.Slog;
 import android.view.Display;
 
@@ -733,7 +734,7 @@
                 "Launch completed; removing icicle of " + r.icicle);
     }
 
-    private void addRecentActivityLocked(ActivityRecord r) {
+    void addRecentActivityLocked(ActivityRecord r) {
         if (r != null) {
             mRecentTasks.addLocked(r.task);
             r.task.touchActiveTime();
@@ -2312,8 +2313,8 @@
         updateTaskMovement(task, true);
     }
 
-    final void startActivityLocked(ActivityRecord r, boolean newTask,
-            boolean doResume, boolean keepCurTransition, ActivityOptions options) {
+    final void startActivityLocked(ActivityRecord r, boolean newTask, boolean keepCurTransition,
+            ActivityOptions options) {
         TaskRecord rTask = r.task;
         final int taskId = rTask.taskId;
         // mLaunchTaskBehind tasks get placed at the back of the task stack.
@@ -2459,12 +2460,6 @@
         if (VALIDATE_TOKENS) {
             validateAppTokensLocked();
         }
-
-        if (doResume) {
-            mStackSupervisor.resumeTopActivitiesLocked(this, r, options);
-        } else {
-            addRecentActivityLocked(r);
-        }
     }
 
     final void validateAppTokensLocked() {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index f4ba19f..9fff0c8 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2628,9 +2628,14 @@
         }
         ActivityStack.logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task);
         targetStack.mLastPausedActivity = null;
-        targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options);
-        if (!launchTaskBehind && doResume) {
-            mService.setFocusedActivityLocked(r, "startedActivity");
+        targetStack.startActivityLocked(r, newTask, keepCurTransition, options);
+        if (doResume) {
+            if (!launchTaskBehind) {
+                mService.setFocusedActivityLocked(r, "startedActivity");
+            }
+            resumeTopActivitiesLocked(targetStack, r, options);
+        } else {
+            targetStack.addRecentActivityLocked(r);
         }
         updateUserStackLocked(r.userId, targetStack);
 
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 2fb71c3..622aa16 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -468,7 +468,7 @@
     }
 
     private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
-            BroadcastFilter filter, boolean ordered) {
+            BroadcastFilter filter, boolean ordered, int index) {
         boolean skip = false;
         if (filter.requiredPermission != null) {
             int perm = mService.checkComponentPermission(filter.requiredPermission,
@@ -576,64 +576,70 @@
 
         if (!mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
                 r.callingPid, r.resolvedType, filter.receiverList.uid)) {
-            return;
+            skip = true;
         }
 
-        if (filter.receiverList.app == null || filter.receiverList.app.crashing) {
+        if (!skip && (filter.receiverList.app == null || filter.receiverList.app.crashing)) {
             Slog.w(TAG, "Skipping deliver [" + mQueueName + "] " + r
                     + " to " + filter.receiverList + ": process crashing");
             skip = true;
         }
 
-        if (!skip) {
-            // If permissions need a review before any of the app components can run, we drop
-            // the broadcast and if the calling app is in the foreground and the broadcast is
-            // explicit we launch the review UI passing it a pending intent to send the skipped
-            // broadcast.
-            if (Build.PERMISSIONS_REVIEW_REQUIRED) {
-                if (!requestStartTargetPermissionsReviewIfNeededLocked(r, filter.packageName,
-                        filter.owningUserId)) {
-                    return;
-                }
-            }
+        if (skip) {
+            r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED;
+            return;
+        }
 
-            // If this is not being sent as an ordered broadcast, then we
-            // don't want to touch the fields that keep track of the current
-            // state of ordered broadcasts.
-            if (ordered) {
-                r.receiver = filter.receiverList.receiver.asBinder();
-                r.curFilter = filter;
-                filter.receiverList.curBroadcast = r;
-                r.state = BroadcastRecord.CALL_IN_RECEIVE;
-                if (filter.receiverList.app != null) {
-                    // Bump hosting application to no longer be in background
-                    // scheduling class.  Note that we can't do that if there
-                    // isn't an app...  but we can only be in that case for
-                    // things that directly call the IActivityManager API, which
-                    // are already core system stuff so don't matter for this.
-                    r.curApp = filter.receiverList.app;
-                    filter.receiverList.app.curReceiver = r;
-                    mService.updateOomAdjLocked(r.curApp);
-                }
+        // If permissions need a review before any of the app components can run, we drop
+        // the broadcast and if the calling app is in the foreground and the broadcast is
+        // explicit we launch the review UI passing it a pending intent to send the skipped
+        // broadcast.
+        if (Build.PERMISSIONS_REVIEW_REQUIRED) {
+            if (!requestStartTargetPermissionsReviewIfNeededLocked(r, filter.packageName,
+                    filter.owningUserId)) {
+                r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED;
+                return;
             }
-            try {
-                if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST,
-                        "Delivering to " + filter + " : " + r);
-                performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
-                        new Intent(r.intent), r.resultCode, r.resultData,
-                        r.resultExtras, r.ordered, r.initialSticky, r.userId);
-                if (ordered) {
-                    r.state = BroadcastRecord.CALL_DONE_RECEIVE;
-                }
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Failure sending broadcast " + r.intent, e);
-                if (ordered) {
-                    r.receiver = null;
-                    r.curFilter = null;
-                    filter.receiverList.curBroadcast = null;
-                    if (filter.receiverList.app != null) {
-                        filter.receiverList.app.curReceiver = null;
-                    }
+        }
+
+        r.delivery[index] = BroadcastRecord.DELIVERY_DELIVERED;
+
+        // If this is not being sent as an ordered broadcast, then we
+        // don't want to touch the fields that keep track of the current
+        // state of ordered broadcasts.
+        if (ordered) {
+            r.receiver = filter.receiverList.receiver.asBinder();
+            r.curFilter = filter;
+            filter.receiverList.curBroadcast = r;
+            r.state = BroadcastRecord.CALL_IN_RECEIVE;
+            if (filter.receiverList.app != null) {
+                // Bump hosting application to no longer be in background
+                // scheduling class.  Note that we can't do that if there
+                // isn't an app...  but we can only be in that case for
+                // things that directly call the IActivityManager API, which
+                // are already core system stuff so don't matter for this.
+                r.curApp = filter.receiverList.app;
+                filter.receiverList.app.curReceiver = r;
+                mService.updateOomAdjLocked(r.curApp);
+            }
+        }
+        try {
+            if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST,
+                    "Delivering to " + filter + " : " + r);
+            performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
+                    new Intent(r.intent), r.resultCode, r.resultData,
+                    r.resultExtras, r.ordered, r.initialSticky, r.userId);
+            if (ordered) {
+                r.state = BroadcastRecord.CALL_DONE_RECEIVE;
+            }
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failure sending broadcast " + r.intent, e);
+            if (ordered) {
+                r.receiver = null;
+                r.curFilter = null;
+                filter.receiverList.curBroadcast = null;
+                if (filter.receiverList.app != null) {
+                    filter.receiverList.app.curReceiver = null;
                 }
             }
         }
@@ -740,7 +746,7 @@
                     if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
                             "Delivering non-ordered on [" + mQueueName + "] to registered "
                             + target + ": " + r);
-                    deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);
+                    deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
                 }
                 addBroadcastToHistoryLocked(r);
                 if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast ["
@@ -897,7 +903,7 @@
                         "Delivering ordered ["
                         + mQueueName + "] to registered "
                         + filter + ": " + r);
-                deliverToRegisteredReceiverLocked(r, filter, r.ordered);
+                deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
                 if (r.receiver == null || !r.ordered) {
                     // The receiver has already finished, so schedule to
                     // process the next one.
@@ -925,10 +931,17 @@
                     info.activityInfo.name);
 
             boolean skip = false;
+            if (brOptions != null &&
+                    (info.activityInfo.applicationInfo.targetSdkVersion
+                            < brOptions.getMinManifestReceiverApiLevel() ||
+                    info.activityInfo.applicationInfo.targetSdkVersion
+                            > brOptions.getMaxManifestReceiverApiLevel())) {
+                skip = true;
+            }
             int perm = mService.checkComponentPermission(info.activityInfo.permission,
                     r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid,
                     info.activityInfo.exported);
-            if (perm != PackageManager.PERMISSION_GRANTED) {
+            if (!skip && perm != PackageManager.PERMISSION_GRANTED) {
                 if (!info.activityInfo.exported) {
                     Slog.w(TAG, "Permission Denial: broadcasting "
                             + r.intent.toString()
@@ -945,7 +958,7 @@
                             + " due to receiver " + component.flattenToShortString());
                 }
                 skip = true;
-            } else if (info.activityInfo.permission != null) {
+            } else if (!skip && info.activityInfo.permission != null) {
                 final int opCode = AppOpsManager.permissionToOpCode(info.activityInfo.permission);
                 if (opCode != AppOpsManager.OP_NONE
                         && mService.mAppOpsService.noteOperation(opCode, r.callingUid,
@@ -1075,9 +1088,18 @@
                 }
             }
 
+            // This is safe to do even if we are skipping the broadcast, and we need
+            // this information now to evaluate whether it is going to be allowed to run.
+            final int receiverUid = info.activityInfo.applicationInfo.uid;
+            // If it's a singleton, it needs to be the same app or a special app
+            if (r.callingUid != Process.SYSTEM_UID && isSingleton
+                    && mService.isValidSingletonCall(r.callingUid, receiverUid)) {
+                info.activityInfo = mService.getActivityInfoForUser(info.activityInfo, 0);
+            }
             String targetProcess = info.activityInfo.processName;
             ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
                     info.activityInfo.applicationInfo.uid, false);
+
             if (!skip) {
                 final int allowed = mService.checkAllowBackgroundLocked(
                         info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, -1);
@@ -1086,9 +1108,17 @@
                     // completely disabled from launches, or it is delayed and the broadcast
                     // was not explicitly sent to it and this would result in a new process
                     // for it being created.
-                    if (allowed == ActivityManager.APP_START_MODE_DISABLED
+                    if (allowed == ActivityManager.APP_START_MODE_DISABLED) {
+                        Slog.w(TAG, "Background execution disabled: receiving "
+                                + r.intent + " to "
+                                + component.flattenToShortString());
+                        skip = true;
+                    }
+                    if (((r.intent.getFlags()&Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND) != 0)
                             || (r.intent.getComponent() == null
-                            && r.intent.getPackage() == null && app == null)) {
+                                && r.intent.getPackage() == null && app == null
+                                && ((r.intent.getFlags()
+                                        & Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == 0))) {
                         Slog.w(TAG, "Background execution not allowed: receiving "
                                 + r.intent + " to "
                                 + component.flattenToShortString());
@@ -1101,6 +1131,7 @@
                 if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
                         "Skipping delivery of ordered [" + mQueueName + "] "
                         + r + " for whatever reason");
+                r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED;
                 r.receiver = null;
                 r.curFilter = null;
                 r.state = BroadcastRecord.IDLE;
@@ -1108,14 +1139,9 @@
                 return;
             }
 
+            r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED;
             r.state = BroadcastRecord.APP_RECEIVE;
             r.curComponent = component;
-            final int receiverUid = info.activityInfo.applicationInfo.uid;
-            // If it's a singleton, it needs to be the same app or a special app
-            if (r.callingUid != Process.SYSTEM_UID && isSingleton
-                    && mService.isValidSingletonCall(r.callingUid, receiverUid)) {
-                info.activityInfo = mService.getActivityInfoForUser(info.activityInfo, 0);
-            }
             r.curReceiver = info.activityInfo;
             if (DEBUG_MU && r.callingUid > UserHandle.PER_USER_RANGE) {
                 Slog.v(TAG_MU, "Updated broadcast record activity info for secondary user, "
@@ -1283,6 +1309,7 @@
         String anrMessage = null;
 
         Object curReceiver = r.receivers.get(r.nextReceiver-1);
+        r.delivery[r.nextReceiver-1] = BroadcastRecord.DELIVERY_TIMEOUT;
         Slog.w(TAG, "Receiver during timeout: " + curReceiver);
         logBroadcastReceiverDiscardLocked(r);
         if (curReceiver instanceof BroadcastFilter) {
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index b42bcff..e99cbf9 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -57,6 +57,7 @@
     final int appOp;        // an app op that is associated with this broadcast
     final BroadcastOptions options; // BroadcastOptions supplied by caller
     final List receivers;   // contains BroadcastFilter and ResolveInfo
+    final int[] delivery;   // delivery state of each receiver
     IIntentReceiver resultTo; // who receives final result if non-null
     long enqueueClockTime;  // the clock time the broadcast was enqueued
     long dispatchTime;      // when dispatch started on this set of receivers
@@ -79,6 +80,11 @@
     static final int CALL_DONE_RECEIVE = 3;
     static final int WAITING_SERVICES = 4;
 
+    static final int DELIVERY_PENDING = 0;
+    static final int DELIVERY_DELIVERED = 1;
+    static final int DELIVERY_SKIPPED = 2;
+    static final int DELIVERY_TIMEOUT = 3;
+
     // The following are set when we are calling a receiver (one that
     // was found in our list of registered receivers).
     BroadcastFilter curFilter;
@@ -183,12 +189,24 @@
         PrintWriterPrinter printer = new PrintWriterPrinter(pw);
         for (int i = 0; i < N; i++) {
             Object o = receivers.get(i);
-            pw.print(prefix); pw.print("Receiver #"); pw.print(i);
-                    pw.print(": "); pw.println(o);
-            if (o instanceof BroadcastFilter)
-                ((BroadcastFilter)o).dumpBrief(pw, p2);
-            else if (o instanceof ResolveInfo)
-                ((ResolveInfo)o).dump(printer, p2);
+            pw.print(prefix);
+            switch (delivery[i]) {
+                case DELIVERY_PENDING:   pw.print("Pending"); break;
+                case DELIVERY_DELIVERED: pw.print("Deliver"); break;
+                case DELIVERY_SKIPPED:   pw.print("Skipped"); break;
+                case DELIVERY_TIMEOUT:   pw.print("Timeout"); break;
+                default:                 pw.print("???????"); break;
+            }
+            pw.print(" #"); pw.print(i); pw.print(": ");
+            if (o instanceof BroadcastFilter) {
+                pw.println(o);
+                ((BroadcastFilter) o).dumpBrief(pw, p2);
+            } else if (o instanceof ResolveInfo) {
+                pw.println("(manifest)");
+                ((ResolveInfo) o).dump(printer, p2, 0);
+            } else {
+                pw.println(o);
+            }
         }
     }
 
@@ -211,6 +229,7 @@
         appOp = _appOp;
         options = _options;
         receivers = _receivers;
+        delivery = new int[_receivers != null ? _receivers.size() : 0];
         resultTo = _resultTo;
         resultCode = _resultCode;
         resultData = _resultData;
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index f6f82da..62e78a4 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -253,7 +253,8 @@
 
                 final Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
                 bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
-                bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
+                bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
+                        | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
                 mService.broadcastIntentLocked(null, null, bootIntent, null, null, 0, null, null,
                         new String[] { android.Manifest.permission.RECEIVE_BOOT_COMPLETED },
                         AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId);
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index a066835..78618ce 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -345,25 +345,9 @@
     }
 
     public void updateRunningAccounts() {
-        mRunningAccounts = AccountManagerService.getSingleton().getRunningAccounts();
-
-        if (mBootCompleted) {
-            doDatabaseCleanup();
-        }
-
-        AccountAndUser[] accounts = mRunningAccounts;
-        for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) {
-            if (!containsAccountAndUser(accounts,
-                    currentSyncContext.mSyncOperation.target.account,
-                    currentSyncContext.mSyncOperation.target.userId)) {
-                Log.d(TAG, "canceling sync since the account is no longer running");
-                sendSyncFinishedOrCanceledMessage(currentSyncContext,
-                        null /* no result since this is a cancel */);
-            }
-        }
-        // we must do this since we don't bother scheduling alarms when
-        // the accounts are not set yet
-        sendCheckAlarmsMessage();
+        if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "sending MESSAGE_ACCOUNTS_UPDATED");
+        // Update accounts in handler thread.
+        mSyncHandler.sendEmptyMessage(SyncHandler.MESSAGE_ACCOUNTS_UPDATED);
     }
 
     private void doDatabaseCleanup() {
@@ -2179,6 +2163,7 @@
          * obj: {@link com.android.server.content.SyncManager.ActiveSyncContext}
          */
         private static final int MESSAGE_MONITOR_SYNC = 8;
+        private static final int MESSAGE_ACCOUNTS_UPDATED = 9;
 
         public final SyncNotificationInfo mSyncNotificationInfo = new SyncNotificationInfo();
         private Long mAlarmScheduleTime = null;
@@ -2296,6 +2281,13 @@
                 // to also take into account the periodic syncs.
                 earliestFuturePollTime = scheduleReadyPeriodicSyncs();
                 switch (msg.what) {
+                    case SyncHandler.MESSAGE_ACCOUNTS_UPDATED:
+                        if (Log.isLoggable(TAG, Log.VERBOSE)) {
+                            Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_ACCOUNTS_UPDATED");
+                        }
+                        updateRunningAccountsH();
+                        break;
+
                     case SyncHandler.MESSAGE_CANCEL:
                         SyncStorageEngine.EndPoint endpoint = (SyncStorageEngine.EndPoint) msg.obj;
                         Bundle extras = msg.peekData();
@@ -2872,7 +2864,28 @@
                     mLocalDeviceIdleController.setSyncActive(active);
                 }
             }
+        }
 
+        private void updateRunningAccountsH() {
+            mRunningAccounts = AccountManagerService.getSingleton().getRunningAccounts();
+
+            if (mBootCompleted) {
+                doDatabaseCleanup();
+            }
+
+            AccountAndUser[] accounts = mRunningAccounts;
+            for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) {
+                if (!containsAccountAndUser(accounts,
+                        currentSyncContext.mSyncOperation.target.account,
+                        currentSyncContext.mSyncOperation.target.userId)) {
+                    Log.d(TAG, "canceling sync since the account is no longer running");
+                    sendSyncFinishedOrCanceledMessage(currentSyncContext,
+                            null /* no result since this is a cancel */);
+                }
+            }
+            // we must do this since we don't bother scheduling alarms when
+            // the accounts are not set yet
+            sendCheckAlarmsMessage();
         }
 
         private boolean isSyncNotUsingNetworkH(ActiveSyncContext activeSyncContext) {
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 4d7df9c..309bec8 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -88,6 +88,7 @@
     static final int MSG_JOB_EXPIRED = 0;
     static final int MSG_CHECK_JOB = 1;
     static final int MSG_STOP_JOB = 2;
+    static final int MSG_CHECK_JOB_GREEDY = 3;
 
     // Policy constants
     /**
@@ -362,7 +363,8 @@
     }
 
     void reportActive() {
-        boolean active = false;
+        // active is true if pending queue contains jobs OR some job is running.
+        boolean active = mPendingJobs.size() > 0;
         if (mPendingJobs.size() <= 0) {
             for (int i=0; i<mActiveServices.size(); i++) {
                 JobServiceContext jsc = mActiveServices.get(i);
@@ -372,9 +374,10 @@
                 }
             }
         }
-        if (mLocalDeviceIdleController != null) {
-            if (mReportedActive != active) {
-                mReportedActive = active;
+
+        if (mReportedActive != active) {
+            mReportedActive = active;
+            if (mLocalDeviceIdleController != null) {
                 mLocalDeviceIdleController.setJobsActive(active);
             }
         }
@@ -628,7 +631,8 @@
             JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
             startTrackingJob(rescheduledPeriodic);
         }
-        mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
+        reportActive();
+        mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
     }
 
     // StateChangedListener implementations.
@@ -676,8 +680,18 @@
                     break;
                 case MSG_CHECK_JOB:
                     synchronized (mJobs) {
-                        // Check the list of jobs and run some of them if we feel inclined.
-                        maybeQueueReadyJobsForExecutionLockedH();
+                        if (mReportedActive) {
+                            // if jobs are currently being run, queue all ready jobs for execution.
+                            queueReadyJobsForExecutionLockedH();
+                        } else {
+                            // Check the list of jobs and run some of them if we feel inclined.
+                            maybeQueueReadyJobsForExecutionLockedH();
+                        }
+                    }
+                    break;
+                case MSG_CHECK_JOB_GREEDY:
+                    synchronized (mJobs) {
+                        queueReadyJobsForExecutionLockedH();
                     }
                     break;
                 case MSG_STOP_JOB:
@@ -709,7 +723,6 @@
                     stopJobOnServiceContextLocked(job);
                 }
             }
-            reportActive();
             if (DEBUG) {
                 final int queuedJobs = mPendingJobs.size();
                 if (queuedJobs == 0) {
@@ -786,7 +799,6 @@
                     Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Not running anything.");
                 }
             }
-            reportActive();
             if (DEBUG) {
                 Slog.d(TAG, "idle=" + idleCount + " connectivity=" +
                 connectivityCount + " charging=" + chargingCount + " tot=" +
diff --git a/services/core/java/com/android/server/job/controllers/TimeController.java b/services/core/java/com/android/server/job/controllers/TimeController.java
index b3d7287..33b09e3 100644
--- a/services/core/java/com/android/server/job/controllers/TimeController.java
+++ b/services/core/java/com/android/server/job/controllers/TimeController.java
@@ -17,11 +17,8 @@
 package com.android.server.job.controllers;
 
 import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
+import android.app.AlarmManager.OnAlarmListener;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.os.SystemClock;
 import android.util.Slog;
 
@@ -40,15 +37,11 @@
  */
 public class TimeController extends StateController {
     private static final String TAG = "JobScheduler.Time";
-    private static final String ACTION_JOB_EXPIRED =
-            "android.content.jobscheduler.JOB_DEADLINE_EXPIRED";
-    private static final String ACTION_JOB_DELAY_EXPIRED =
-            "android.content.jobscheduler.JOB_DELAY_EXPIRED";
 
-    /** Set an alarm for the next job expiry. */
-    private final PendingIntent mDeadlineExpiredAlarmIntent;
-    /** Set an alarm for the next job delay expiry. This*/
-    private final PendingIntent mNextDelayExpiredAlarmIntent;
+    /** Deadline alarm tag for logging purposes */
+    private final String DEADLINE_TAG = "deadline";
+    /** Delay alarm tag for logging purposes */
+    private final String DELAY_TAG = "delay";
 
     private long mNextJobExpiredElapsedMillis;
     private long mNextDelayExpiredElapsedMillis;
@@ -68,19 +61,9 @@
 
     private TimeController(StateChangedListener stateChangedListener, Context context) {
         super(stateChangedListener, context);
-        mDeadlineExpiredAlarmIntent =
-                PendingIntent.getBroadcast(mContext, 0 /* ignored */,
-                        new Intent(ACTION_JOB_EXPIRED), 0);
-        mNextDelayExpiredAlarmIntent =
-                PendingIntent.getBroadcast(mContext, 0 /* ignored */,
-                        new Intent(ACTION_JOB_DELAY_EXPIRED), 0);
+
         mNextJobExpiredElapsedMillis = Long.MAX_VALUE;
         mNextDelayExpiredElapsedMillis = Long.MAX_VALUE;
-
-        // Register BR for these intents.
-        IntentFilter intentFilter = new IntentFilter(ACTION_JOB_EXPIRED);
-        intentFilter.addAction(ACTION_JOB_DELAY_EXPIRED);
-        mContext.registerReceiver(mAlarmExpiredReceiver, intentFilter);
     }
 
     /**
@@ -224,7 +207,7 @@
     private void setDelayExpiredAlarm(long alarmTimeElapsedMillis) {
         alarmTimeElapsedMillis = maybeAdjustAlarmTime(alarmTimeElapsedMillis);
         mNextDelayExpiredElapsedMillis = alarmTimeElapsedMillis;
-        updateAlarmWithPendingIntent(mNextDelayExpiredAlarmIntent, mNextDelayExpiredElapsedMillis);
+        updateAlarmWithListener(DELAY_TAG, mNextDelayExpiredListener, mNextDelayExpiredElapsedMillis);
     }
 
     /**
@@ -235,7 +218,7 @@
     private void setDeadlineExpiredAlarm(long alarmTimeElapsedMillis) {
         alarmTimeElapsedMillis = maybeAdjustAlarmTime(alarmTimeElapsedMillis);
         mNextJobExpiredElapsedMillis = alarmTimeElapsedMillis;
-        updateAlarmWithPendingIntent(mDeadlineExpiredAlarmIntent, mNextJobExpiredElapsedMillis);
+        updateAlarmWithListener(DEADLINE_TAG, mDeadlineExpiredListener, mNextJobExpiredElapsedMillis);
     }
 
     private long maybeAdjustAlarmTime(long proposedAlarmTimeElapsedMillis) {
@@ -246,31 +229,39 @@
         return proposedAlarmTimeElapsedMillis;
     }
 
-    private void updateAlarmWithPendingIntent(PendingIntent pi, long alarmTimeElapsed) {
+    private void updateAlarmWithListener(String tag, OnAlarmListener listener,
+            long alarmTimeElapsed) {
         ensureAlarmService();
         if (alarmTimeElapsed == Long.MAX_VALUE) {
-            mAlarmService.cancel(pi);
+            mAlarmService.cancel(listener);
         } else {
             if (DEBUG) {
-                Slog.d(TAG, "Setting " + pi.getIntent().getAction() + " for: " + alarmTimeElapsed);
+                Slog.d(TAG, "Setting " + tag + " for: " + alarmTimeElapsed);
             }
-            mAlarmService.set(AlarmManager.ELAPSED_REALTIME, alarmTimeElapsed, pi);
+            mAlarmService.set(AlarmManager.ELAPSED_REALTIME, alarmTimeElapsed,
+                    tag, listener, null);
         }
     }
 
-    private final BroadcastReceiver mAlarmExpiredReceiver = new BroadcastReceiver() {
+    // Job/delay expiration alarm handling
+
+    private final OnAlarmListener mDeadlineExpiredListener = new OnAlarmListener() {
         @Override
-        public void onReceive(Context context, Intent intent) {
+        public void onAlarm() {
             if (DEBUG) {
-                Slog.d(TAG, "Just received alarm: " + intent.getAction());
+                Slog.d(TAG, "Deadline-expired alarm fired");
             }
-            // A job has just expired, so we run through the list of jobs that we have and
-            // notify our StateChangedListener.
-            if (ACTION_JOB_EXPIRED.equals(intent.getAction())) {
-                checkExpiredDeadlinesAndResetAlarm();
-            } else if (ACTION_JOB_DELAY_EXPIRED.equals(intent.getAction())) {
-                checkExpiredDelaysAndResetAlarm();
+            checkExpiredDeadlinesAndResetAlarm();
+        }
+    };
+
+    private final OnAlarmListener mNextDelayExpiredListener = new OnAlarmListener() {
+        @Override
+        public void onAlarm() {
+            if (DEBUG) {
+                Slog.d(TAG, "Delay-expired alarm fired");
             }
+            checkExpiredDelaysAndResetAlarm();
         }
     };
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 33f39bc..c83012c 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -159,8 +159,8 @@
 public class NotificationManagerService extends SystemService {
     static final String TAG = "NotificationService";
     static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
-    public static final boolean ENABLE_CHILD_NOTIFICATIONS = Build.IS_DEBUGGABLE
-            && SystemProperties.getBoolean("debug.child_notifs", false);
+    public static final boolean ENABLE_CHILD_NOTIFICATIONS
+            = SystemProperties.getBoolean("debug.child_notifs", true);
 
     static final int MAX_PACKAGE_NOTIFICATIONS = 50;
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2f8157e..3a8a988 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -9797,6 +9797,15 @@
             if (r1.system != r2.system) {
                 return r1.system ? -1 : 1;
             }
+            if (r1.activityInfo != null) {
+                return r1.activityInfo.packageName.compareTo(r2.activityInfo.packageName);
+            }
+            if (r1.serviceInfo != null) {
+                return r1.serviceInfo.packageName.compareTo(r2.serviceInfo.packageName);
+            }
+            if (r1.providerInfo != null) {
+                return r1.providerInfo.packageName.compareTo(r2.providerInfo.packageName);
+            }
             return 0;
         }
     };
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index f9ed760..b18c846 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -103,11 +103,11 @@
                     return runUninstall();
                 case "resolve-activity":
                     return runResolveActivity();
-                case "query-intent-activities":
+                case "query-activities":
                     return runQueryIntentActivities();
-                case "query-intent-services":
+                case "query-services":
                     return runQueryIntentServices();
-                case "query-intent-receivers":
+                case "query-receivers":
                     return runQueryIntentReceivers();
                 default:
                     return handleDefaultCommands(cmd);
@@ -1043,13 +1043,13 @@
         pw.println("      -s: short summary");
         pw.println("      -d: only list dangerous permissions");
         pw.println("      -u: list only the permissions users will see");
-        pw.println("  resolve-intent [--user USER_ID] INTENT");
+        pw.println("  resolve-activity [--user USER_ID] INTENT");
         pw.println("    Prints the activity that resolves to the given Intent.");
-        pw.println("  query-intent-activities [--user USER_ID] INTENT");
+        pw.println("  query-activities [--user USER_ID] INTENT");
         pw.println("    Prints all activities that can handle the given Intent.");
-        pw.println("  query-intent-services [--user USER_ID] INTENT");
+        pw.println("  query-services [--user USER_ID] INTENT");
         pw.println("    Prints all services that can handle the given Intent.");
-        pw.println("  query-intent-receivers [--user USER_ID] INTENT");
+        pw.println("  query-receivers [--user USER_ID] INTENT");
         pw.println("    Prints all broadcast receivers that can handle the given Intent.");
         pw.println();
         Intent.printIntentArgsHelp(pw , "");
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index dbbbb58..0c606fe 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2534,26 +2534,32 @@
                 win.mAttrs.height = bottom - top;
                 win.setWindowScale(win.mRequestedWidth, win.mRequestedHeight);
 
-                if (SHOW_TRANSACTIONS) {
-                    Slog.i(TAG_WM, ">>> OPEN TRANSACTION repositionChild");
-                }
+                if (win.mHasSurface) {
+                    if (SHOW_TRANSACTIONS) {
+                        Slog.i(TAG_WM, ">>> OPEN TRANSACTION repositionChild");
+                    }
 
-                SurfaceControl.openTransaction();
+                    SurfaceControl.openTransaction();
 
-                win.applyGravityAndUpdateFrame();
-                win.mWinAnimator.computeShownFrameLocked();
+                    try {
 
-                win.mWinAnimator.setSurfaceBoundariesLocked(false);
+                        win.applyGravityAndUpdateFrame();
+                        win.mWinAnimator.computeShownFrameLocked();
 
-                if (deferTransactionUntilFrame > 0) {
-                    win.mWinAnimator.mSurfaceController.deferTransactionUntil(
-                            win.mAttachedWindow.mWinAnimator.mSurfaceController.getHandle(),
-                            deferTransactionUntilFrame);
-                }
+                        win.mWinAnimator.setSurfaceBoundariesLocked(false);
 
-                SurfaceControl.closeTransaction();
-                if (SHOW_TRANSACTIONS) {
-                    Slog.i(TAG_WM, "<<< CLOSE TRANSACTION repositionChild");
+                        if (deferTransactionUntilFrame > 0) {
+                            win.mWinAnimator.mSurfaceController.deferTransactionUntil(
+                                    win.mAttachedWindow.mWinAnimator.mSurfaceController.getHandle(),
+                                    deferTransactionUntilFrame);
+                        }
+
+                    } finally {
+                        SurfaceControl.closeTransaction();
+                        if (SHOW_TRANSACTIONS) {
+                            Slog.i(TAG_WM, "<<< CLOSE TRANSACTION repositionChild");
+                        }
+                    }
                 }
 
                 outFrame = win.mCompatFrame;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 66bde7e..c540e05 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -109,8 +109,6 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
-import android.util.PrintWriterPrinter;
-import android.util.Printer;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.Xml;
@@ -1752,11 +1750,7 @@
 
         try {
             return new DeviceAdminInfo(mContext, ri);
-        } catch (XmlPullParserException e) {
-            Slog.w(LOG_TAG, "Bad device admin requested for user=" + userHandle + ": " + adminName,
-                    e);
-            return null;
-        } catch (IOException e) {
+        } catch (XmlPullParserException | IOException e) {
             Slog.w(LOG_TAG, "Bad device admin requested for user=" + userHandle + ": " + adminName,
                     e);
             return null;
@@ -1994,18 +1988,11 @@
                     XmlUtils.skipCurrentTag(parser);
                 }
             }
-        } catch (NullPointerException e) {
-            Slog.w(LOG_TAG, "failed parsing " + file + " " + e);
-        } catch (NumberFormatException e) {
-            Slog.w(LOG_TAG, "failed parsing " + file + " " + e);
-        } catch (XmlPullParserException e) {
-            Slog.w(LOG_TAG, "failed parsing " + file + " " + e);
         } catch (FileNotFoundException e) {
             // Don't be noisy, this is normal if we haven't defined any policies.
-        } catch (IOException e) {
-            Slog.w(LOG_TAG, "failed parsing " + file + " " + e);
-        } catch (IndexOutOfBoundsException e) {
-            Slog.w(LOG_TAG, "failed parsing " + file + " " + e);
+        } catch (NullPointerException | NumberFormatException | XmlPullParserException | IOException
+                | IndexOutOfBoundsException e) {
+            Slog.w(LOG_TAG, "failed parsing " + file, e);
         }
         try {
             if (stream != null) {
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index 812d9b6..2329b42 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -389,7 +389,6 @@
     }
 
     private void scheduleRenew() {
-        mRenewAlarm.cancel();
         if (mDhcpLeaseExpiry != 0) {
             long now = SystemClock.elapsedRealtime();
             long alarmTime = (now + mDhcpLeaseExpiry) / 2;
@@ -822,6 +821,11 @@
                     return NOT_HANDLED;
             }
         }
+
+        @Override
+        public void exit() {
+            mRenewAlarm.cancel();
+        }
     }
 
     class DhcpRenewingState extends PacketRetransmittingState {
diff --git a/services/tests/servicestests/src/com/android/server/BroadcastInterceptingContext.java b/services/tests/servicestests/src/com/android/server/BroadcastInterceptingContext.java
index 757f1c6..13657ab 100644
--- a/services/tests/servicestests/src/com/android/server/BroadcastInterceptingContext.java
+++ b/services/tests/servicestests/src/com/android/server/BroadcastInterceptingContext.java
@@ -21,6 +21,7 @@
 import android.content.ContextWrapper;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.UserHandle;
 
@@ -165,6 +166,11 @@
     }
 
     @Override
+    public void sendStickyBroadcastAsUser(Intent intent, UserHandle user, Bundle options) {
+        sendBroadcast(intent);
+    }
+
+    @Override
     public void removeStickyBroadcast(Intent intent) {
         // ignored
     }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 109d214..3efd0fb 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -266,8 +266,6 @@
         pw.print("  Session service="); pw.println(mInfo.getSessionService());
         pw.println("  Service info:");
         mInfo.getServiceInfo().dump(new PrintWriterPrinter(pw), "    ");
-        pw.println("  Application info:");
-        mInfo.getServiceInfo().applicationInfo.dump(new PrintWriterPrinter(pw), "    ");
         pw.print("  Recognition service="); pw.println(mInfo.getRecognitionService());
         pw.print("  Settings activity="); pw.println(mInfo.getSettingsActivity());
         pw.print("  Supports assist="); pw.println(mInfo.getSupportsAssist());
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 12b36b2..6ffc026 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -385,6 +385,11 @@
     public static final String KEY_VVM_TYPE_STRING = "vvm_type_string";
 
     /**
+     * Whether cellular data is required to access visual voicemail.
+     */
+    public static final String KEY_VVM_CELLULAR_DATA_REQUIRED_BOOLEAN = "vvm_cellular_data_required";
+
+    /**
      * The package name of the carrier's visual voicemail app to ensure that dialer visual voicemail
      * and carrier visual voicemail are not active at the same time.
      */
@@ -599,6 +604,7 @@
         sDefaults.putString(KEY_VVM_DESTINATION_NUMBER_STRING, "");
         sDefaults.putInt(KEY_VVM_PORT_NUMBER_INT, 0);
         sDefaults.putString(KEY_VVM_TYPE_STRING, "");
+        sDefaults.putBoolean(KEY_VVM_CELLULAR_DATA_REQUIRED_BOOLEAN,false);
         sDefaults.putString(KEY_CARRIER_VVM_PACKAGE_NAME_STRING, "");
         sDefaults.putBoolean(KEY_CI_ACTION_ON_SYS_UPDATE_BOOL, false);
         sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING, "");
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index 4c3b598..64d2978 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -433,6 +433,12 @@
         throw new UnsupportedOperationException();
     }
 
+    /** @hide */
+    @Override
+    public void sendStickyBroadcastAsUser(Intent intent, UserHandle user, Bundle options) {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public void sendStickyOrderedBroadcastAsUser(Intent intent,
             UserHandle user, BroadcastReceiver resultReceiver,
@@ -695,22 +701,26 @@
     }
 
     @Override
-    public Context createDeviceEncryptedContext(Context context) {
+    public Context createDeviceEncryptedStorageContext() {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@hide} */
+    @SystemApi
+    @Override
+    public Context createCredentialEncryptedStorageContext() {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public Context createCredentialEncryptedContext(Context context) {
+    public boolean isDeviceEncryptedStorage() {
         throw new UnsupportedOperationException();
     }
 
+    /** {@hide} */
+    @SystemApi
     @Override
-    public boolean isDeviceEncrypted() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public boolean isCredentialEncrypted() {
+    public boolean isCredentialEncryptedStorage() {
         throw new UnsupportedOperationException();
     }
 }
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java
index 69b2a9d..0c36063 100644
--- a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java
@@ -44,4 +44,12 @@
         }
         return anchor.getTrustedCert();
     }
+
+    public X509Certificate findByIssuerAndSignature(X509Certificate cert) {
+        java.security.cert.TrustAnchor anchor = mIndex.findByIssuerAndSignature(cert);
+        if (anchor == null) {
+            return null;
+        }
+        return anchor.getTrustedCert();
+    }
 }
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 21f47bc2..0bb88a7 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -383,6 +383,16 @@
     }
 }
 
+static void printUsesPermissionSdk23(const String8& name, int maxSdkVersion=-1) {
+    printf("uses-permission-sdk-23: ");
+
+    printf("name='%s'", ResTable::normalizeForOutput(name.string()).string());
+    if (maxSdkVersion != -1) {
+        printf(" maxSdkVersion='%d'", maxSdkVersion);
+    }
+    printf("\n");
+}
+
 static void printUsesImpliedPermission(const String8& name, const String8& reason) {
     printf("uses-implied-permission: name='%s' reason='%s'\n",
             ResTable::normalizeForOutput(name.string()).string(),
@@ -463,12 +473,20 @@
  * a pre-requisite or some other reason.
  */
 struct ImpliedFeature {
+    ImpliedFeature() : impliedBySdk23(false) {}
+    ImpliedFeature(const String8& n, bool sdk23) : name(n), impliedBySdk23(sdk23) {}
+
     /**
      * Name of the implied feature.
      */
     String8 name;
 
     /**
+     * Was this implied by a permission from SDK 23 (<uses-permission-sdk-23 />)?
+     */
+    bool impliedBySdk23;
+
+    /**
      * List of human-readable reasons for why this feature was implied.
      */
     SortedVector<String8> reasons;
@@ -497,18 +515,24 @@
 };
 
 static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures,
-        const char* name, const char* reason) {
+                              const char* name, const char* reason, bool sdk23) {
     String8 name8(name);
     ssize_t idx = impliedFeatures->indexOfKey(name8);
     if (idx < 0) {
-        idx = impliedFeatures->add(name8, ImpliedFeature());
-        impliedFeatures->editValueAt(idx).name = name8;
+        idx = impliedFeatures->add(name8, ImpliedFeature(name8, sdk23));
     }
-    impliedFeatures->editValueAt(idx).reasons.add(String8(reason));
+
+    ImpliedFeature* feature = &impliedFeatures->editValueAt(idx);
+
+    // A non-sdk 23 implied feature takes precedence.
+    if (feature->impliedBySdk23 && !sdk23) {
+        feature->impliedBySdk23 = false;
+    }
+    feature->reasons.add(String8(reason));
 }
 
-static void printFeatureGroup(const FeatureGroup& grp,
-        const KeyedVector<String8, ImpliedFeature>* impliedFeatures = NULL) {
+static void printFeatureGroupImpl(const FeatureGroup& grp,
+                                  const KeyedVector<String8, ImpliedFeature>* impliedFeatures) {
     printf("feature-group: label='%s'\n", grp.label.string());
 
     if (grp.openGLESVersion > 0) {
@@ -536,9 +560,11 @@
 
         String8 printableFeatureName(ResTable::normalizeForOutput(
                     impliedFeature.name.string()));
-        printf("  uses-feature: name='%s'\n", printableFeatureName.string());
-        printf("  uses-implied-feature: name='%s' reason='",
-                printableFeatureName.string());
+        const char* sdk23Suffix = impliedFeature.impliedBySdk23 ? "-sdk-23" : "";
+
+        printf("  uses-feature%s: name='%s'\n", sdk23Suffix, printableFeatureName.string());
+        printf("  uses-implied-feature%s: name='%s' reason='", sdk23Suffix,
+               printableFeatureName.string());
         const size_t numReasons = impliedFeature.reasons.size();
         for (size_t j = 0; j < numReasons; j++) {
             printf("%s", impliedFeature.reasons[j].string());
@@ -552,6 +578,15 @@
     }
 }
 
+static void printFeatureGroup(const FeatureGroup& grp) {
+    printFeatureGroupImpl(grp, NULL);
+}
+
+static void printDefaultFeatureGroup(const FeatureGroup& grp,
+                                     const KeyedVector<String8, ImpliedFeature>& impliedFeatures) {
+    printFeatureGroupImpl(grp, &impliedFeatures);
+}
+
 static void addParentFeatures(FeatureGroup* grp, const String8& name) {
     if (name == "android.hardware.camera.autofocus" ||
             name == "android.hardware.camera.flash") {
@@ -572,6 +607,72 @@
     }
 }
 
+static void addImpliedFeaturesForPermission(const int targetSdk, const String8& name,
+                                            KeyedVector<String8, ImpliedFeature>* impliedFeatures,
+                                            bool impliedBySdk23Permission) {
+    if (name == "android.permission.CAMERA") {
+        addImpliedFeature(impliedFeatures, "android.hardware.camera",
+                String8::format("requested %s permission", name.string())
+                .string(), impliedBySdk23Permission);
+    } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
+        addImpliedFeature(impliedFeatures, "android.hardware.location.gps",
+                String8::format("requested %s permission", name.string())
+                .string(), impliedBySdk23Permission);
+        addImpliedFeature(impliedFeatures, "android.hardware.location",
+                String8::format("requested %s permission", name.string())
+                .string(), impliedBySdk23Permission);
+    } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
+        addImpliedFeature(impliedFeatures, "android.hardware.location",
+                String8::format("requested %s permission", name.string())
+                .string(), impliedBySdk23Permission);
+    } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
+        addImpliedFeature(impliedFeatures, "android.hardware.location.network",
+                String8::format("requested %s permission", name.string())
+                .string(), impliedBySdk23Permission);
+        addImpliedFeature(impliedFeatures, "android.hardware.location",
+                String8::format("requested %s permission", name.string())
+                .string(), impliedBySdk23Permission);
+    } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
+               name == "android.permission.INSTALL_LOCATION_PROVIDER") {
+        addImpliedFeature(impliedFeatures, "android.hardware.location",
+                String8::format("requested %s permission", name.string())
+                .string(), impliedBySdk23Permission);
+    } else if (name == "android.permission.BLUETOOTH" ||
+               name == "android.permission.BLUETOOTH_ADMIN") {
+        if (targetSdk > 4) {
+            addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
+                    String8::format("requested %s permission", name.string())
+                    .string(), impliedBySdk23Permission);
+            addImpliedFeature(impliedFeatures, "android.hardware.bluetooth",
+                    "targetSdkVersion > 4", impliedBySdk23Permission);
+        }
+    } else if (name == "android.permission.RECORD_AUDIO") {
+        addImpliedFeature(impliedFeatures, "android.hardware.microphone",
+                String8::format("requested %s permission", name.string())
+                .string(), impliedBySdk23Permission);
+    } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
+               name == "android.permission.CHANGE_WIFI_STATE" ||
+               name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
+        addImpliedFeature(impliedFeatures, "android.hardware.wifi",
+                String8::format("requested %s permission", name.string())
+                .string(), impliedBySdk23Permission);
+    } else if (name == "android.permission.CALL_PHONE" ||
+               name == "android.permission.CALL_PRIVILEGED" ||
+               name == "android.permission.MODIFY_PHONE_STATE" ||
+               name == "android.permission.PROCESS_OUTGOING_CALLS" ||
+               name == "android.permission.READ_SMS" ||
+               name == "android.permission.RECEIVE_SMS" ||
+               name == "android.permission.RECEIVE_MMS" ||
+               name == "android.permission.RECEIVE_WAP_PUSH" ||
+               name == "android.permission.SEND_SMS" ||
+               name == "android.permission.WRITE_APN_SETTINGS" ||
+               name == "android.permission.WRITE_SMS") {
+        addImpliedFeature(impliedFeatures, "android.hardware.telephony",
+                String8("requested a telephony permission").string(),
+                impliedBySdk23Permission);
+    }
+}
+
 /*
  * Handle the "dump" command, to extract select data from an archive.
  */
@@ -712,7 +813,8 @@
             size_t len;
             ResXMLTree::event_code_t code;
             int depth = 0;
-            while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
+            while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
+                    code != ResXMLTree::BAD_DOCUMENT) {
                 if (code == ResXMLTree::END_TAG) {
                     depth--;
                     continue;
@@ -735,25 +837,53 @@
                     }
                     String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL);
                     printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string());
-                } else if (depth == 2 && tag == "permission") {
-                    String8 error;
-                    String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
-                    if (error != "") {
-                        fprintf(stderr, "ERROR: %s\n", error.string());
-                        goto bail;
+                } else if (depth == 2) {
+                    if (tag == "permission") {
+                        String8 error;
+                        String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
+                        if (error != "") {
+                            fprintf(stderr, "ERROR: %s\n", error.string());
+                            goto bail;
+                        }
+
+                        if (name == "") {
+                            fprintf(stderr, "ERROR: missing 'android:name' for permission\n");
+                            goto bail;
+                        }
+                        printf("permission: %s\n",
+                                ResTable::normalizeForOutput(name.string()).string());
+                    } else if (tag == "uses-permission") {
+                        String8 error;
+                        String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
+                        if (error != "") {
+                            fprintf(stderr, "ERROR: %s\n", error.string());
+                            goto bail;
+                        }
+
+                        if (name == "") {
+                            fprintf(stderr, "ERROR: missing 'android:name' for uses-permission\n");
+                            goto bail;
+                        }
+                        printUsesPermission(name,
+                                AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
+                                AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
+                    } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
+                        String8 error;
+                        String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
+                        if (error != "") {
+                            fprintf(stderr, "ERROR: %s\n", error.string());
+                            goto bail;
+                        }
+
+                        if (name == "") {
+                            fprintf(stderr, "ERROR: missing 'android:name' for "
+                                    "uses-permission-sdk-23\n");
+                            goto bail;
+                        }
+                        printUsesPermissionSdk23(
+                                name,
+                                AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
                     }
-                    printf("permission: %s\n",
-                            ResTable::normalizeForOutput(name.string()).string());
-                } else if (depth == 2 && tag == "uses-permission") {
-                    String8 error;
-                    String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
-                    if (error != "") {
-                        fprintf(stderr, "ERROR: %s\n", error.string());
-                        goto bail;
-                    }
-                    printUsesPermission(name,
-                            AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
-                            AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
                 }
             }
         } else if (strcmp("badging", option) == 0) {
@@ -893,7 +1023,8 @@
             Vector<FeatureGroup> featureGroups;
             KeyedVector<String8, ImpliedFeature> impliedFeatures;
 
-            while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
+            while ((code=tree.next()) != ResXMLTree::END_DOCUMENT &&
+                    code != ResXMLTree::BAD_DOCUMENT) {
                 if (code == ResXMLTree::END_TAG) {
                     depth--;
                     if (depth < 2) {
@@ -924,8 +1055,10 @@
                                             ResTable::normalizeForOutput(aName.string()).string());
                                 }
                                 printf(" label='%s' icon='%s'\n",
-                                        ResTable::normalizeForOutput(activityLabel.string()).string(),
-                                        ResTable::normalizeForOutput(activityIcon.string()).string());
+                                       ResTable::normalizeForOutput(activityLabel.string())
+                                                .string(),
+                                       ResTable::normalizeForOutput(activityIcon.string())
+                                                .string());
                             }
                             if (isLeanbackLauncherActivity) {
                                 printf("leanback-launchable-activity:");
@@ -934,9 +1067,12 @@
                                             ResTable::normalizeForOutput(aName.string()).string());
                                 }
                                 printf(" label='%s' icon='%s' banner='%s'\n",
-                                        ResTable::normalizeForOutput(activityLabel.string()).string(),
-                                        ResTable::normalizeForOutput(activityIcon.string()).string(),
-                                        ResTable::normalizeForOutput(activityBanner.string()).string());
+                                       ResTable::normalizeForOutput(activityLabel.string())
+                                                .string(),
+                                       ResTable::normalizeForOutput(activityIcon.string())
+                                                .string(),
+                                       ResTable::normalizeForOutput(activityBanner.string())
+                                                .string());
                             }
                         }
                         if (!hasIntentFilter) {
@@ -964,18 +1100,21 @@
                                 hasLauncher |= catLauncher;
                                 hasCameraActivity |= actCamera;
                                 hasCameraSecureActivity |= actCameraSecure;
-                                hasOtherActivities |= !actMainActivity && !actCamera && !actCameraSecure;
+                                hasOtherActivities |=
+                                        !actMainActivity && !actCamera && !actCameraSecure;
                             } else if (withinReceiver) {
                                 hasWidgetReceivers |= actWidgetReceivers;
                                 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
                                         hasBindDeviceAdminPermission);
-                                hasOtherReceivers |= (!actWidgetReceivers && !actDeviceAdminEnabled);
+                                hasOtherReceivers |=
+                                        (!actWidgetReceivers && !actDeviceAdminEnabled);
                             } else if (withinService) {
                                 hasImeService |= actImeService;
                                 hasWallpaperService |= actWallpaperService;
                                 hasAccessibilityService |= (actAccessibilityService &&
                                         hasBindAccessibilityServicePermission);
-                                hasPrintService |= (actPrintService && hasBindPrintServicePermission);
+                                hasPrintService |=
+                                        (actPrintService && hasBindPrintServicePermission);
                                 hasNotificationListenerService |= actNotificationListenerService &&
                                         hasBindNotificationListenerServicePermission;
                                 hasDreamService |= actDreamService && hasBindDreamServicePermission;
@@ -984,7 +1123,8 @@
                                         !actHostApduService && !actOffHostApduService &&
                                         !actNotificationListenerService);
                             } else if (withinProvider) {
-                                hasDocumentsProvider |= actDocumentsProvider && hasRequiredSafAttributes;
+                                hasDocumentsProvider |=
+                                        actDocumentsProvider && hasRequiredSafAttributes;
                             }
                         }
                         withinIntentFilter = false;
@@ -1125,7 +1265,8 @@
                             goto bail;
                         }
 
-                        String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR, &error);
+                        String8 banner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR,
+                                                                       &error);
                         if (error != "") {
                             fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n",
                                     error.string());
@@ -1135,7 +1276,8 @@
                                 ResTable::normalizeForOutput(label.string()).string());
                         printf("icon='%s'", ResTable::normalizeForOutput(icon.string()).string());
                         if (banner != "") {
-                            printf(" banner='%s'", ResTable::normalizeForOutput(banner.string()).string());
+                            printf(" banner='%s'",
+                                   ResTable::normalizeForOutput(banner.string()).string());
                         }
                         printf("\n");
                         if (testOnly != 0) {
@@ -1178,13 +1320,15 @@
                             }
                         }
                     } else if (tag == "uses-sdk") {
-                        int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error);
+                        int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR,
+                                                                    &error);
                         if (error != "") {
                             error = "";
                             String8 name = AaptXml::getResolvedAttribute(res, tree,
                                     MIN_SDK_VERSION_ATTR, &error);
                             if (error != "") {
-                                fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n",
+                                fprintf(stderr,
+                                        "ERROR getting 'android:minSdkVersion' attribute: %s\n",
                                         error.string());
                                 goto bail;
                             }
@@ -1205,7 +1349,8 @@
                             String8 name = AaptXml::getResolvedAttribute(res, tree,
                                     TARGET_SDK_VERSION_ATTR, &error);
                             if (error != "") {
-                                fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
+                                fprintf(stderr,
+                                        "ERROR getting 'android:targetSdkVersion' attribute: %s\n",
                                         error.string());
                                 goto bail;
                             }
@@ -1297,90 +1442,58 @@
                         }
                     } else if (tag == "uses-permission") {
                         String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
-                        if (name != "" && error == "") {
-                            if (name == "android.permission.CAMERA") {
-                                addImpliedFeature(&impliedFeatures, "android.hardware.camera",
-                                        String8::format("requested %s permission", name.string())
-                                        .string());
-                            } else if (name == "android.permission.ACCESS_FINE_LOCATION") {
-                                addImpliedFeature(&impliedFeatures, "android.hardware.location.gps",
-                                        String8::format("requested %s permission", name.string())
-                                        .string());
-                                addImpliedFeature(&impliedFeatures, "android.hardware.location",
-                                        String8::format("requested %s permission", name.string())
-                                        .string());
-                            } else if (name == "android.permission.ACCESS_MOCK_LOCATION") {
-                                addImpliedFeature(&impliedFeatures, "android.hardware.location",
-                                        String8::format("requested %s permission", name.string())
-                                        .string());
-                            } else if (name == "android.permission.ACCESS_COARSE_LOCATION") {
-                                addImpliedFeature(&impliedFeatures, "android.hardware.location.network",
-                                        String8::format("requested %s permission", name.string())
-                                        .string());
-                                addImpliedFeature(&impliedFeatures, "android.hardware.location",
-                                        String8::format("requested %s permission", name.string())
-                                        .string());
-                            } else if (name == "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" ||
-                                       name == "android.permission.INSTALL_LOCATION_PROVIDER") {
-                                addImpliedFeature(&impliedFeatures, "android.hardware.location",
-                                        String8::format("requested %s permission", name.string())
-                                        .string());
-                            } else if (name == "android.permission.BLUETOOTH" ||
-                                       name == "android.permission.BLUETOOTH_ADMIN") {
-                                if (targetSdk > 4) {
-                                    addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
-                                            String8::format("requested %s permission", name.string())
-                                            .string());
-                                    addImpliedFeature(&impliedFeatures, "android.hardware.bluetooth",
-                                            "targetSdkVersion > 4");
-                                }
-                            } else if (name == "android.permission.RECORD_AUDIO") {
-                                addImpliedFeature(&impliedFeatures, "android.hardware.microphone",
-                                        String8::format("requested %s permission", name.string())
-                                        .string());
-                            } else if (name == "android.permission.ACCESS_WIFI_STATE" ||
-                                       name == "android.permission.CHANGE_WIFI_STATE" ||
-                                       name == "android.permission.CHANGE_WIFI_MULTICAST_STATE") {
-                                addImpliedFeature(&impliedFeatures, "android.hardware.wifi",
-                                        String8::format("requested %s permission", name.string())
-                                        .string());
-                            } else if (name == "android.permission.CALL_PHONE" ||
-                                       name == "android.permission.CALL_PRIVILEGED" ||
-                                       name == "android.permission.MODIFY_PHONE_STATE" ||
-                                       name == "android.permission.PROCESS_OUTGOING_CALLS" ||
-                                       name == "android.permission.READ_SMS" ||
-                                       name == "android.permission.RECEIVE_SMS" ||
-                                       name == "android.permission.RECEIVE_MMS" ||
-                                       name == "android.permission.RECEIVE_WAP_PUSH" ||
-                                       name == "android.permission.SEND_SMS" ||
-                                       name == "android.permission.WRITE_APN_SETTINGS" ||
-                                       name == "android.permission.WRITE_SMS") {
-                                addImpliedFeature(&impliedFeatures, "android.hardware.telephony",
-                                        String8("requested a telephony permission").string());
-                            } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
-                                hasWriteExternalStoragePermission = true;
-                            } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
-                                hasReadExternalStoragePermission = true;
-                            } else if (name == "android.permission.READ_PHONE_STATE") {
-                                hasReadPhoneStatePermission = true;
-                            } else if (name == "android.permission.READ_CONTACTS") {
-                                hasReadContactsPermission = true;
-                            } else if (name == "android.permission.WRITE_CONTACTS") {
-                                hasWriteContactsPermission = true;
-                            } else if (name == "android.permission.READ_CALL_LOG") {
-                                hasReadCallLogPermission = true;
-                            } else if (name == "android.permission.WRITE_CALL_LOG") {
-                                hasWriteCallLogPermission = true;
-                            }
-
-                            printUsesPermission(name,
-                                    AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
-                                    AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
-                       } else {
+                        if (error != "") {
                             fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
                                     error.string());
                             goto bail;
                         }
+
+                        if (name == "") {
+                            fprintf(stderr, "ERROR: missing 'android:name' for uses-permission\n");
+                            goto bail;
+                        }
+
+                        addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, false);
+
+                        if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
+                            hasWriteExternalStoragePermission = true;
+                        } else if (name == "android.permission.READ_EXTERNAL_STORAGE") {
+                            hasReadExternalStoragePermission = true;
+                        } else if (name == "android.permission.READ_PHONE_STATE") {
+                            hasReadPhoneStatePermission = true;
+                        } else if (name == "android.permission.READ_CONTACTS") {
+                            hasReadContactsPermission = true;
+                        } else if (name == "android.permission.WRITE_CONTACTS") {
+                            hasWriteContactsPermission = true;
+                        } else if (name == "android.permission.READ_CALL_LOG") {
+                            hasReadCallLogPermission = true;
+                        } else if (name == "android.permission.WRITE_CALL_LOG") {
+                            hasWriteCallLogPermission = true;
+                        }
+
+                        printUsesPermission(name,
+                                AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0,
+                                AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
+
+                    } else if (tag == "uses-permission-sdk-23" || tag == "uses-permission-sdk-m") {
+                        String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
+                        if (error != "") {
+                            fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
+                                    error.string());
+                            goto bail;
+                        }
+
+                        if (name == "") {
+                            fprintf(stderr, "ERROR: missing 'android:name' for "
+                                    "uses-permission-sdk-23\n");
+                            goto bail;
+                        }
+
+                        addImpliedFeaturesForPermission(targetSdk, name, &impliedFeatures, true);
+
+                        printUsesPermissionSdk23(
+                                name, AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR));
+
                     } else if (tag == "uses-package") {
                         String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                         if (name != "" && error == "") {
@@ -1422,7 +1535,8 @@
                     } else if (tag == "package-verifier") {
                         String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                         if (name != "" && error == "") {
-                            String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR, &error);
+                            String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR,
+                                                                      &error);
                             if (publicKey != "" && error == "") {
                                 printf("package-verifier: name='%s' publicKey='%s'\n",
                                         ResTable::normalizeForOutput(name.string()).string(),
@@ -1485,12 +1599,18 @@
                             if (error == "") {
                                 if (orien == 0 || orien == 6 || orien == 8) {
                                     // Requests landscape, sensorLandscape, or reverseLandscape.
-                                    addImpliedFeature(&impliedFeatures, "android.hardware.screen.landscape",
-                                            "one or more activities have specified a landscape orientation");
+                                    addImpliedFeature(&impliedFeatures,
+                                                      "android.hardware.screen.landscape",
+                                                      "one or more activities have specified a "
+                                                      "landscape orientation",
+                                                      false);
                                 } else if (orien == 1 || orien == 7 || orien == 9) {
                                     // Requests portrait, sensorPortrait, or reversePortrait.
-                                    addImpliedFeature(&impliedFeatures, "android.hardware.screen.portrait",
-                                            "one or more activities have specified a portrait orientation");
+                                    addImpliedFeature(&impliedFeatures,
+                                                      "android.hardware.screen.portrait",
+                                                      "one or more activities have specified a "
+                                                      "portrait orientation",
+                                                      false);
                                 }
                             }
                         } else if (tag == "uses-library") {
@@ -1524,8 +1644,10 @@
                                     hasBindDeviceAdminPermission = true;
                                 }
                             } else {
-                                fprintf(stderr, "ERROR getting 'android:permission' attribute for"
-                                        " receiver '%s': %s\n", receiverName.string(), error.string());
+                                fprintf(stderr,
+                                        "ERROR getting 'android:permission' attribute for"
+                                        " receiver '%s': %s\n",
+                                        receiverName.string(), error.string());
                             }
                         } else if (tag == "service") {
                             withinService = true;
@@ -1542,20 +1664,24 @@
                             if (error == "") {
                                 if (permission == "android.permission.BIND_INPUT_METHOD") {
                                     hasBindInputMethodPermission = true;
-                                } else if (permission == "android.permission.BIND_ACCESSIBILITY_SERVICE") {
+                                } else if (permission ==
+                                        "android.permission.BIND_ACCESSIBILITY_SERVICE") {
                                     hasBindAccessibilityServicePermission = true;
-                                } else if (permission == "android.permission.BIND_PRINT_SERVICE") {
+                                } else if (permission ==
+                                        "android.permission.BIND_PRINT_SERVICE") {
                                     hasBindPrintServicePermission = true;
-                                } else if (permission == "android.permission.BIND_NFC_SERVICE") {
+                                } else if (permission ==
+                                        "android.permission.BIND_NFC_SERVICE") {
                                     hasBindNfcServicePermission = true;
-                                } else if (permission == "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
+                                } else if (permission ==
+                                        "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
                                     hasBindNotificationListenerServicePermission = true;
                                 } else if (permission == "android.permission.BIND_DREAM_SERVICE") {
                                     hasBindDreamServicePermission = true;
                                 }
                             } else {
-                                fprintf(stderr, "ERROR getting 'android:permission' attribute for"
-                                        " service '%s': %s\n", serviceName.string(), error.string());
+                                fprintf(stderr, "ERROR getting 'android:permission' attribute for "
+                                        "service '%s': %s\n", serviceName.string(), error.string());
                             }
                         } else if (tag == "provider") {
                             withinProvider = true;
@@ -1563,7 +1689,8 @@
                             bool exported = AaptXml::getResolvedIntegerAttribute(res, tree,
                                     EXPORTED_ATTR, &error);
                             if (error != "") {
-                                fprintf(stderr, "ERROR getting 'android:exported' attribute for provider:"
+                                fprintf(stderr,
+                                        "ERROR getting 'android:exported' attribute for provider:"
                                         " %s\n", error.string());
                                 goto bail;
                             }
@@ -1571,16 +1698,17 @@
                             bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute(
                                     res, tree, GRANT_URI_PERMISSIONS_ATTR, &error);
                             if (error != "") {
-                                fprintf(stderr, "ERROR getting 'android:grantUriPermissions' attribute for provider:"
-                                        " %s\n", error.string());
+                                fprintf(stderr,
+                                        "ERROR getting 'android:grantUriPermissions' attribute for "
+                                        "provider: %s\n", error.string());
                                 goto bail;
                             }
 
                             String8 permission = AaptXml::getResolvedAttribute(res, tree,
                                     PERMISSION_ATTR, &error);
                             if (error != "") {
-                                fprintf(stderr, "ERROR getting 'android:permission' attribute for provider:"
-                                        " %s\n", error.string());
+                                fprintf(stderr, "ERROR getting 'android:permission' attribute for "
+                                        "provider: %s\n", error.string());
                                 goto bail;
                             }
 
@@ -1661,8 +1789,9 @@
                     } else if (withinService && tag == "meta-data") {
                         String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                         if (error != "") {
-                            fprintf(stderr, "ERROR getting 'android:name' attribute for"
-                                    " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
+                            fprintf(stderr, "ERROR getting 'android:name' attribute for "
+                                    "meta-data tag in service '%s': %s\n", serviceName.string(),
+                                    error.string());
                             goto bail;
                         }
 
@@ -1676,8 +1805,9 @@
                             String8 xmlPath = AaptXml::getResolvedAttribute(res, tree,
                                     RESOURCE_ATTR, &error);
                             if (error != "") {
-                                fprintf(stderr, "ERROR getting 'android:resource' attribute for"
-                                        " meta-data tag in service '%s': %s\n", serviceName.string(), error.string());
+                                fprintf(stderr, "ERROR getting 'android:resource' attribute for "
+                                        "meta-data tag in service '%s': %s\n",
+                                        serviceName.string(), error.string());
                                 goto bail;
                             }
 
@@ -1731,15 +1861,19 @@
                                 actImeService = true;
                             } else if (action == "android.service.wallpaper.WallpaperService") {
                                 actWallpaperService = true;
-                            } else if (action == "android.accessibilityservice.AccessibilityService") {
+                            } else if (action ==
+                                    "android.accessibilityservice.AccessibilityService") {
                                 actAccessibilityService = true;
-                            } else if (action == "android.printservice.PrintService") {
+                            } else if (action =="android.printservice.PrintService") {
                                 actPrintService = true;
-                            } else if (action == "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
+                            } else if (action ==
+                                    "android.nfc.cardemulation.action.HOST_APDU_SERVICE") {
                                 actHostApduService = true;
-                            } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
+                            } else if (action ==
+                                    "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
                                 actOffHostApduService = true;
-                            } else if (action == "android.service.notification.NotificationListenerService") {
+                            } else if (action ==
+                                    "android.service.notification.NotificationListenerService") {
                                 actNotificationListenerService = true;
                             } else if (action == "android.service.dreams.DreamService") {
                                 actDreamService = true;
@@ -1814,12 +1948,12 @@
             }
 
             addImpliedFeature(&impliedFeatures, "android.hardware.touchscreen",
-                    "default feature for all apps");
+                    "default feature for all apps", false);
 
             const size_t numFeatureGroups = featureGroups.size();
             if (numFeatureGroups == 0) {
                 // If no <feature-group> tags were defined, apply auto-implied features.
-                printFeatureGroup(commonFeatures, &impliedFeatures);
+                printDefaultFeatureGroup(commonFeatures, impliedFeatures);
 
             } else {
                 // <feature-group> tags are defined, so we ignore implied features and
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index d8e0aac..0f83980 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -31,6 +31,8 @@
 	flatten/Archive.cpp \
 	flatten/TableFlattener.cpp \
 	flatten/XmlFlattener.cpp \
+	io/FileSystem.cpp \
+	io/ZipArchive.cpp \
 	link/AutoVersioner.cpp \
 	link/ManifestFixer.cpp \
 	link/PrivateAttributeMover.cpp \
diff --git a/tools/aapt2/NameMangler.h b/tools/aapt2/NameMangler.h
index 6d752bb..054b9ee 100644
--- a/tools/aapt2/NameMangler.h
+++ b/tools/aapt2/NameMangler.h
@@ -60,7 +60,7 @@
         };
     }
 
-    bool shouldMangle(const std::u16string& package) {
+    bool shouldMangle(const std::u16string& package) const {
         if (package.empty() || mPolicy.targetPackageName == package) {
             return false;
         }
diff --git a/tools/aapt2/Resource.cpp b/tools/aapt2/Resource.cpp
index 34dc1d5..9328b69 100644
--- a/tools/aapt2/Resource.cpp
+++ b/tools/aapt2/Resource.cpp
@@ -85,4 +85,12 @@
     return &iter->second;
 }
 
+bool operator<(const ResourceKey& a, const ResourceKey& b) {
+    return std::tie(a.name, a.config) < std::tie(b.name, b.config);
+}
+
+bool operator<(const ResourceKeyRef& a, const ResourceKeyRef& b) {
+    return std::tie(a.name, a.config) < std::tie(b.name, b.config);
+}
+
 } // namespace aapt
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index a7afbb5..c71e249 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -165,6 +165,36 @@
     std::vector<SourcedResourceName> exportedSymbols;
 };
 
+/**
+ * Useful struct used as a key to represent a unique resource in associative containers.
+ */
+struct ResourceKey {
+    ResourceName name;
+    ConfigDescription config;
+};
+
+bool operator<(const ResourceKey& a, const ResourceKey& b);
+
+/**
+ * Useful struct used as a key to represent a unique resource in associative containers.
+ * Holds a reference to the name, so that name better live longer than this key!
+ */
+struct ResourceKeyRef {
+    ResourceNameRef name;
+    ConfigDescription config;
+
+    ResourceKeyRef() = default;
+    ResourceKeyRef(const ResourceNameRef& n, const ConfigDescription& c) : name(n), config(c) {
+    }
+
+    /**
+     * Prevent taking a reference to a temporary. This is bad.
+     */
+    ResourceKeyRef(ResourceName&& n, const ConfigDescription& c) = delete;
+};
+
+bool operator<(const ResourceKeyRef& a, const ResourceKeyRef& b);
+
 //
 // ResourceId implementation.
 //
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index c2ddb5c..d4c536f 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -173,7 +173,7 @@
     ResourceName name;
     Source source;
     ResourceId id;
-    SymbolState symbolState = SymbolState::kUndefined;
+    Maybe<SymbolState> symbolState;
     std::u16string comment;
     std::unique_ptr<Value> value;
     std::list<ParsedResource> childResources;
@@ -182,9 +182,9 @@
 // Recursively adds resources to the ResourceTable.
 static bool addResourcesToTable(ResourceTable* table, const ConfigDescription& config,
                                 IDiagnostics* diag, ParsedResource* res) {
-    if (res->symbolState != SymbolState::kUndefined) {
+    if (res->symbolState) {
         Symbol symbol;
-        symbol.state = res->symbolState;
+        symbol.state = res->symbolState.value();
         symbol.source = res->source;
         symbol.comment = res->comment;
         if (!table->setSymbolState(res->name, res->id, symbol, diag)) {
@@ -325,6 +325,8 @@
             result = parseSymbol(parser, &parsedResource);
         } else if (elementName == u"public-group") {
             result = parsePublicGroup(parser, &parsedResource);
+        } else if (elementName == u"add-resource") {
+            result = parseAddResource(parser, &parsedResource);
         } else {
             // Try parsing the elementName (or type) as a resource. These shall only be
             // resources like 'layout' or 'xml' and they can only be references.
@@ -643,7 +645,7 @@
     return !error;
 }
 
-bool ResourceParser::parseSymbol(xml::XmlPullParser* parser, ParsedResource* outResource) {
+bool ResourceParser::parseSymbolImpl(xml::XmlPullParser* parser, ParsedResource* outResource) {
     const Source source = mSource.withLine(parser->getLineNumber());
 
     Maybe<StringPiece16> maybeType = xml::findNonEmptyAttribute(parser, u"type");
@@ -661,10 +663,25 @@
     }
 
     outResource->name.type = *parsedType;
-    outResource->symbolState = SymbolState::kPrivate;
     return true;
 }
 
+bool ResourceParser::parseSymbol(xml::XmlPullParser* parser, ParsedResource* outResource) {
+    if (parseSymbolImpl(parser, outResource)) {
+        outResource->symbolState = SymbolState::kPrivate;
+        return true;
+    }
+    return false;
+}
+
+bool ResourceParser::parseAddResource(xml::XmlPullParser* parser, ParsedResource* outResource) {
+    if (parseSymbolImpl(parser, outResource)) {
+        outResource->symbolState = SymbolState::kUndefined;
+        return true;
+    }
+    return false;
+}
+
 static uint32_t parseFormatType(const StringPiece16& piece) {
     if (piece == u"reference")      return android::ResTable_map::TYPE_REFERENCE;
     else if (piece == u"string")    return android::ResTable_map::TYPE_STRING;
@@ -870,7 +887,7 @@
     }
 
     return Attribute::Symbol{
-            Reference(ResourceName({}, ResourceType::kId, maybeName.value().toString())),
+            Reference(ResourceNameRef({}, ResourceType::kId, maybeName.value())),
             val.data };
 }
 
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index 1150758..04db577 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -83,7 +83,9 @@
     bool parsePrimitive(xml::XmlPullParser* parser, ParsedResource* outResource);
     bool parsePublic(xml::XmlPullParser* parser, ParsedResource* outResource);
     bool parsePublicGroup(xml::XmlPullParser* parser, ParsedResource* outResource);
+    bool parseSymbolImpl(xml::XmlPullParser* parser, ParsedResource* outResource);
     bool parseSymbol(xml::XmlPullParser* parser, ParsedResource* outResource);
+    bool parseAddResource(xml::XmlPullParser* parser, ParsedResource* outResource);
     bool parseAttr(xml::XmlPullParser* parser, ParsedResource* outResource);
     bool parseAttrImpl(xml::XmlPullParser* parser, ParsedResource* outResource, bool weak);
     Maybe<Attribute::Symbol> parseEnumOrFlagItem(xml::XmlPullParser* parser,
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 6e0812b..84f67c6 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -563,4 +563,16 @@
     ASSERT_FALSE(testParse(input));
 }
 
+TEST_F(ResourceParserTest, AddResourcesElementShouldAddEntryWithUndefinedSymbol) {
+    std::string input = R"EOF(<add-resource name="bar" type="string" />)EOF";
+    ASSERT_TRUE(testParse(input));
+
+    Maybe<ResourceTable::SearchResult> result = mTable.findResource(
+            test::parseNameOrDie(u"@string/bar"));
+    AAPT_ASSERT_TRUE(result);
+    const ResourceEntry* entry = result.value().entry;
+    ASSERT_NE(nullptr, entry);
+    EXPECT_EQ(SymbolState::kUndefined, entry->symbolStatus.state);
+}
+
 } // namespace aapt
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 73d8585..8a3d047 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -101,7 +101,7 @@
     if (iter != last && (*iter)->type == type) {
         return iter->get();
     }
-    return types.emplace(iter, new ResourceTableType{ type })->get();
+    return types.emplace(iter, new ResourceTableType(type))->get();
 }
 
 ResourceEntry* ResourceTableType::findEntry(const StringPiece16& name) {
@@ -121,7 +121,7 @@
     if (iter != last && name == (*iter)->name) {
         return iter->get();
     }
-    return entries.emplace(iter, new ResourceEntry{ name })->get();
+    return entries.emplace(iter, new ResourceEntry(name))->get();
 }
 
 /**
@@ -342,11 +342,6 @@
                                        IDiagnostics* diag) {
     assert(diag && "diagnostics can't be nullptr");
 
-    if (symbol.state == SymbolState::kUndefined) {
-        // Nothing to do.
-        return true;
-    }
-
     auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars);
     if (badCharIter != name.entry.end()) {
         diag->error(DiagMessage(symbol.source)
@@ -400,23 +395,30 @@
         return false;
     }
 
-    // Only mark the type state as public, it doesn't care about being private.
-    if (symbol.state == SymbolState::kPublic) {
-        type->symbolStatus.state = SymbolState::kPublic;
-    }
-
-    // Downgrading to a private symbol from a public one is not allowed.
-    if (entry->symbolStatus.state != SymbolState::kPublic) {
-        if (entry->symbolStatus.state != symbol.state) {
-            entry->symbolStatus = std::move(symbol);
-        }
-    }
-
     if (resId.isValid()) {
         package->id = resId.packageId();
         type->id = resId.typeId();
         entry->id = resId.entryId();
     }
+
+    // Only mark the type state as public, it doesn't care about being private.
+    if (symbol.state == SymbolState::kPublic) {
+        type->symbolStatus.state = SymbolState::kPublic;
+    }
+
+    if (symbol.state == SymbolState::kUndefined &&
+            entry->symbolStatus.state != SymbolState::kUndefined) {
+        // We can't undefine a symbol (remove its visibility). Ignore.
+        return true;
+    }
+
+    if (symbol.state == SymbolState::kPrivate &&
+            entry->symbolStatus.state == SymbolState::kPublic) {
+        // We can't downgrade public to private. Ignore.
+        return true;
+    }
+
+    entry->symbolStatus = std::move(symbol);
     return true;
 }
 
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index ffe6595..36c3e70 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 
+#include "NameMangler.h"
 #include "ResourceUtils.h"
 #include "flatten/ResourceTypeExtensions.h"
+#include "util/Files.h"
 #include "util/Util.h"
 
 #include <androidfw/ResourceTypes.h>
@@ -554,5 +556,22 @@
     return {};
 }
 
+std::string buildResourceFileName(const ResourceFile& resFile, const NameMangler* mangler) {
+    std::stringstream out;
+    out << "res/" << resFile.name.type;
+    if (resFile.config != ConfigDescription{}) {
+        out << "-" << resFile.config;
+    }
+    out << "/";
+
+    if (mangler && mangler->shouldMangle(resFile.name.package)) {
+        out << NameMangler::mangleEntry(resFile.name.package, resFile.name.entry);
+    } else {
+        out << resFile.name.entry;
+    }
+    out << file::getExtension(resFile.source.path);
+    return out.str();
+}
+
 } // namespace ResourceUtils
 } // namespace aapt
diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h
index f93a4c7..64ca971 100644
--- a/tools/aapt2/ResourceUtils.h
+++ b/tools/aapt2/ResourceUtils.h
@@ -17,6 +17,7 @@
 #ifndef AAPT_RESOURCEUTILS_H
 #define AAPT_RESOURCEUTILS_H
 
+#include "NameMangler.h"
 #include "Resource.h"
 #include "ResourceValues.h"
 #include "util/StringPiece.h"
@@ -154,6 +155,16 @@
 
 uint32_t androidTypeToAttributeTypeMask(uint16_t type);
 
+/**
+ * Returns a string path suitable for use within an APK. The path will look like:
+ *
+ * res/type[-config]/<name>.<ext>
+ *
+ * Then name may be mangled if a NameMangler is supplied (can be nullptr) and the package
+ * requires mangling.
+ */
+std::string buildResourceFileName(const ResourceFile& resFile, const NameMangler* mangler);
+
 } // namespace ResourceUtils
 } // namespace aapt
 
diff --git a/tools/aapt2/flatten/TableFlattener.cpp b/tools/aapt2/flatten/TableFlattener.cpp
index 46ee914..a2f53e1 100644
--- a/tools/aapt2/flatten/TableFlattener.cpp
+++ b/tools/aapt2/flatten/TableFlattener.cpp
@@ -583,8 +583,8 @@
                     publicEntry->state = Public_entry::kPublic;
                     break;
 
-                default:
-                    assert(false && "should not serialize any other state");
+                case SymbolState::kUndefined:
+                    publicEntry->state = Public_entry::kUndefined;
                     break;
                 }
 
diff --git a/tools/aapt2/io/File.h b/tools/aapt2/io/File.h
index 9fca398..b4d4971 100644
--- a/tools/aapt2/io/File.h
+++ b/tools/aapt2/io/File.h
@@ -52,6 +52,14 @@
     virtual const Source& getSource() const = 0;
 };
 
+class IFileCollectionIterator {
+public:
+    virtual ~IFileCollectionIterator() = default;
+
+    virtual bool hasNext() = 0;
+    virtual IFile* next() = 0;
+};
+
 /**
  * Interface for a collection of files, all of which share a common source. That source may
  * simply be the filesystem, or a ZIP archive.
@@ -60,10 +68,8 @@
 public:
     virtual ~IFileCollection() = default;
 
-    using const_iterator = std::vector<std::unique_ptr<IFile>>::const_iterator;
-
-    virtual const_iterator begin() const = 0;
-    virtual const_iterator end() const = 0;
+    virtual IFile* findFile(const StringPiece& path) = 0;
+    virtual std::unique_ptr<IFileCollectionIterator> iterator() = 0;
 };
 
 } // namespace io
diff --git a/tools/aapt2/io/FileSystem.cpp b/tools/aapt2/io/FileSystem.cpp
new file mode 100644
index 0000000..76f87ae
--- /dev/null
+++ b/tools/aapt2/io/FileSystem.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2015 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 "Source.h"
+#include "io/FileSystem.h"
+#include "util/Files.h"
+#include "util/Maybe.h"
+#include "util/StringPiece.h"
+#include "util/Util.h"
+
+#include <utils/FileMap.h>
+
+namespace aapt {
+namespace io {
+
+RegularFile::RegularFile(const Source& source) : mSource(source) {
+}
+
+std::unique_ptr<IData> RegularFile::openAsData() {
+    android::FileMap map;
+    if (Maybe<android::FileMap> map = file::mmapPath(mSource.path, nullptr)) {
+        return util::make_unique<MmappedData>(std::move(map.value()));
+    }
+    return {};
+}
+
+const Source& RegularFile::getSource() const {
+    return mSource;
+}
+
+FileCollectionIterator::FileCollectionIterator(FileCollection* collection) :
+        mCurrent(collection->mFiles.begin()), mEnd(collection->mFiles.end()) {
+}
+
+bool FileCollectionIterator::hasNext() {
+    return mCurrent != mEnd;
+}
+
+IFile* FileCollectionIterator::next() {
+    IFile* result = mCurrent->second.get();
+    ++mCurrent;
+    return result;
+}
+
+IFile* FileCollection::insertFile(const StringPiece& path) {
+    return (mFiles[path.toString()] = util::make_unique<RegularFile>(Source(path))).get();
+}
+
+IFile* FileCollection::findFile(const StringPiece& path) {
+    auto iter = mFiles.find(path.toString());
+    if (iter != mFiles.end()) {
+        return iter->second.get();
+    }
+    return nullptr;
+}
+
+std::unique_ptr<IFileCollectionIterator> FileCollection::iterator() {
+    return util::make_unique<FileCollectionIterator>(this);
+}
+
+} // namespace io
+} // namespace aapt
diff --git a/tools/aapt2/io/FileSystem.h b/tools/aapt2/io/FileSystem.h
index 5dbefcc..f0559c0 100644
--- a/tools/aapt2/io/FileSystem.h
+++ b/tools/aapt2/io/FileSystem.h
@@ -18,7 +18,8 @@
 #define AAPT_IO_FILESYSTEM_H
 
 #include "io/File.h"
-#include "util/Files.h"
+
+#include <map>
 
 namespace aapt {
 namespace io {
@@ -28,25 +29,28 @@
  */
 class RegularFile : public IFile {
 public:
-    RegularFile(const Source& source) : mSource(source) {
-    }
+    RegularFile(const Source& source);
 
-    std::unique_ptr<IData> openAsData() override {
-        android::FileMap map;
-        if (Maybe<android::FileMap> map = file::mmapPath(mSource.path, nullptr)) {
-            return util::make_unique<MmappedData>(std::move(map.value()));
-        }
-        return {};
-    }
-
-    const Source& getSource() const override {
-        return mSource;
-    }
+    std::unique_ptr<IData> openAsData() override;
+    const Source& getSource() const override;
 
 private:
     Source mSource;
 };
 
+class FileCollection;
+
+class FileCollectionIterator : public IFileCollectionIterator {
+public:
+    FileCollectionIterator(FileCollection* collection);
+
+    bool hasNext() override;
+    io::IFile* next() override;
+
+private:
+    std::map<std::string, std::unique_ptr<IFile>>::const_iterator mCurrent, mEnd;
+};
+
 /**
  * An IFileCollection representing the file system.
  */
@@ -55,21 +59,13 @@
     /**
      * Adds a file located at path. Returns the IFile representation of that file.
      */
-    IFile* insertFile(const StringPiece& path) {
-        mFiles.push_back(util::make_unique<RegularFile>(Source(path)));
-        return mFiles.back().get();
-    }
-
-    const_iterator begin() const override {
-        return mFiles.begin();
-    }
-
-    const_iterator end() const override {
-        return mFiles.end();
-    }
+    IFile* insertFile(const StringPiece& path);
+    IFile* findFile(const StringPiece& path) override;
+    std::unique_ptr<IFileCollectionIterator> iterator() override;
 
 private:
-    std::vector<std::unique_ptr<IFile>> mFiles;
+    friend class FileCollectionIterator;
+    std::map<std::string, std::unique_ptr<IFile>> mFiles;
 };
 
 } // namespace io
diff --git a/tools/aapt2/io/ZipArchive.cpp b/tools/aapt2/io/ZipArchive.cpp
new file mode 100644
index 0000000..bf0f4aa
--- /dev/null
+++ b/tools/aapt2/io/ZipArchive.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2015 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 "Source.h"
+#include "io/ZipArchive.h"
+#include "util/Util.h"
+
+#include <utils/FileMap.h>
+#include <ziparchive/zip_archive.h>
+
+namespace aapt {
+namespace io {
+
+ZipFile::ZipFile(ZipArchiveHandle handle, const ZipEntry& entry, const Source& source) :
+        mZipHandle(handle), mZipEntry(entry), mSource(source) {
+}
+
+std::unique_ptr<IData> ZipFile::openAsData() {
+    if (mZipEntry.method == kCompressStored) {
+        int fd = GetFileDescriptor(mZipHandle);
+
+        android::FileMap fileMap;
+        bool result = fileMap.create(nullptr, fd, mZipEntry.offset,
+                                     mZipEntry.uncompressed_length, true);
+        if (!result) {
+            return {};
+        }
+        return util::make_unique<MmappedData>(std::move(fileMap));
+
+    } else {
+        std::unique_ptr<uint8_t[]> data = std::unique_ptr<uint8_t[]>(
+                new uint8_t[mZipEntry.uncompressed_length]);
+        int32_t result = ExtractToMemory(mZipHandle, &mZipEntry, data.get(),
+                                         static_cast<uint32_t>(mZipEntry.uncompressed_length));
+        if (result != 0) {
+            return {};
+        }
+        return util::make_unique<MallocData>(std::move(data), mZipEntry.uncompressed_length);
+    }
+}
+
+const Source& ZipFile::getSource() const {
+    return mSource;
+}
+
+ZipFileCollectionIterator::ZipFileCollectionIterator(ZipFileCollection* collection) :
+        mCurrent(collection->mFiles.begin()), mEnd(collection->mFiles.end()) {
+}
+
+bool ZipFileCollectionIterator::hasNext() {
+    return mCurrent != mEnd;
+}
+
+IFile* ZipFileCollectionIterator::next() {
+    IFile* result = mCurrent->second.get();
+    ++mCurrent;
+    return result;
+}
+
+ZipFileCollection::ZipFileCollection() : mHandle(nullptr) {
+}
+
+std::unique_ptr<ZipFileCollection> ZipFileCollection::create(const StringPiece& path,
+                                                             std::string* outError) {
+    std::unique_ptr<ZipFileCollection> collection = std::unique_ptr<ZipFileCollection>(
+            new ZipFileCollection());
+
+    int32_t result = OpenArchive(path.data(), &collection->mHandle);
+    if (result != 0) {
+        if (outError) *outError = ErrorCodeString(result);
+        return {};
+    }
+
+    ZipString suffix(".flat");
+    void* cookie = nullptr;
+    result = StartIteration(collection->mHandle, &cookie, nullptr, &suffix);
+    if (result != 0) {
+        if (outError) *outError = ErrorCodeString(result);
+        return {};
+    }
+
+    using IterationEnder = std::unique_ptr<void, decltype(EndIteration)*>;
+    IterationEnder iterationEnder(cookie, EndIteration);
+
+    ZipString zipEntryName;
+    ZipEntry zipData;
+    while ((result = Next(cookie, &zipData, &zipEntryName)) == 0) {
+        std::string zipEntryPath = std::string(reinterpret_cast<const char*>(zipEntryName.name),
+                                               zipEntryName.name_length);
+        std::string nestedPath = path.toString() + "@" + zipEntryPath;
+        collection->mFiles[zipEntryPath] = util::make_unique<ZipFile>(collection->mHandle,
+                                                                      zipData,
+                                                                      Source(nestedPath));
+    }
+
+    if (result != -1) {
+        if (outError) *outError = ErrorCodeString(result);
+        return {};
+    }
+    return collection;
+}
+
+IFile* ZipFileCollection::findFile(const StringPiece& path) {
+    auto iter = mFiles.find(path.toString());
+    if (iter != mFiles.end()) {
+        return iter->second.get();
+    }
+    return nullptr;
+}
+
+std::unique_ptr<IFileCollectionIterator> ZipFileCollection::iterator() {
+    return util::make_unique<ZipFileCollectionIterator>(this);
+}
+
+ZipFileCollection::~ZipFileCollection() {
+    if (mHandle) {
+        CloseArchive(mHandle);
+    }
+}
+
+} // namespace io
+} // namespace aapt
diff --git a/tools/aapt2/io/ZipArchive.h b/tools/aapt2/io/ZipArchive.h
index 98afc49..5ad119d 100644
--- a/tools/aapt2/io/ZipArchive.h
+++ b/tools/aapt2/io/ZipArchive.h
@@ -20,7 +20,7 @@
 #include "io/File.h"
 #include "util/StringPiece.h"
 
-#include <utils/FileMap.h>
+#include <map>
 #include <ziparchive/zip_archive.h>
 
 namespace aapt {
@@ -32,37 +32,10 @@
  */
 class ZipFile : public IFile {
 public:
-    ZipFile(ZipArchiveHandle handle, const ZipEntry& entry, const Source& source) :
-            mZipHandle(handle), mZipEntry(entry), mSource(source) {
-    }
+    ZipFile(ZipArchiveHandle handle, const ZipEntry& entry, const Source& source);
 
-    std::unique_ptr<IData> openAsData() override {
-        if (mZipEntry.method == kCompressStored) {
-            int fd = GetFileDescriptor(mZipHandle);
-
-            android::FileMap fileMap;
-            bool result = fileMap.create(nullptr, fd, mZipEntry.offset,
-                                         mZipEntry.uncompressed_length, true);
-            if (!result) {
-                return {};
-            }
-            return util::make_unique<MmappedData>(std::move(fileMap));
-
-        } else {
-            std::unique_ptr<uint8_t[]> data = std::unique_ptr<uint8_t[]>(
-                    new uint8_t[mZipEntry.uncompressed_length]);
-            int32_t result = ExtractToMemory(mZipHandle, &mZipEntry, data.get(),
-                                             static_cast<uint32_t>(mZipEntry.uncompressed_length));
-            if (result != 0) {
-                return {};
-            }
-            return util::make_unique<MallocData>(std::move(data), mZipEntry.uncompressed_length);
-        }
-    }
-
-    const Source& getSource() const override {
-        return mSource;
-    }
+    std::unique_ptr<IData> openAsData() override;
+    const Source& getSource() const override;
 
 private:
     ZipArchiveHandle mZipHandle;
@@ -70,71 +43,38 @@
     Source mSource;
 };
 
+class ZipFileCollection;
+
+class ZipFileCollectionIterator : public IFileCollectionIterator {
+public:
+    ZipFileCollectionIterator(ZipFileCollection* collection);
+
+    bool hasNext() override;
+    io::IFile* next() override;
+
+private:
+    std::map<std::string, std::unique_ptr<IFile>>::const_iterator mCurrent, mEnd;
+};
+
 /**
  * An IFileCollection that represents a ZIP archive and the entries within it.
  */
 class ZipFileCollection : public IFileCollection {
 public:
     static std::unique_ptr<ZipFileCollection> create(const StringPiece& path,
-                                                     std::string* outError) {
-        std::unique_ptr<ZipFileCollection> collection = std::unique_ptr<ZipFileCollection>(
-                new ZipFileCollection());
+                                                     std::string* outError);
 
-        int32_t result = OpenArchive(path.data(), &collection->mHandle);
-        if (result != 0) {
-            if (outError) *outError = ErrorCodeString(result);
-            return {};
-        }
+    io::IFile* findFile(const StringPiece& path) override;
+    std::unique_ptr<IFileCollectionIterator> iterator() override;
 
-        ZipString suffix(".flat");
-        void* cookie = nullptr;
-        result = StartIteration(collection->mHandle, &cookie, nullptr, &suffix);
-        if (result != 0) {
-            if (outError) *outError = ErrorCodeString(result);
-            return {};
-        }
-
-        using IterationEnder = std::unique_ptr<void, decltype(EndIteration)*>;
-        IterationEnder iterationEnder(cookie, EndIteration);
-
-        ZipString zipEntryName;
-        ZipEntry zipData;
-        while ((result = Next(cookie, &zipData, &zipEntryName)) == 0) {
-            std::string nestedPath = path.toString();
-            nestedPath += "@" + std::string(reinterpret_cast<const char*>(zipEntryName.name),
-                                            zipEntryName.name_length);
-            collection->mFiles.push_back(util::make_unique<ZipFile>(collection->mHandle,
-                                                                    zipData,
-                                                                    Source(nestedPath)));
-        }
-
-        if (result != -1) {
-            if (outError) *outError = ErrorCodeString(result);
-            return {};
-        }
-        return collection;
-    }
-
-    const_iterator begin() const override {
-        return mFiles.begin();
-    }
-
-    const_iterator end() const override {
-        return mFiles.end();
-    }
-
-    ~ZipFileCollection() override {
-        if (mHandle) {
-            CloseArchive(mHandle);
-        }
-    }
+    ~ZipFileCollection() override;
 
 private:
-    ZipFileCollection() : mHandle(nullptr) {
-    }
+    friend class ZipFileCollectionIterator;
+    ZipFileCollection();
 
     ZipArchiveHandle mHandle;
-    std::vector<std::unique_ptr<IFile>> mFiles;
+    std::map<std::string, std::unique_ptr<IFile>> mFiles;
 };
 
 } // namespace io
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index 33d9272..652e52f 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -57,6 +57,7 @@
     bool staticLib = false;
     bool verbose = false;
     bool outputToDirectory = false;
+    bool autoAddOverlay = false;
     Maybe<std::u16string> privateSymbols;
     Maybe<std::u16string> minSdkVersionDefault;
     Maybe<std::u16string> targetSdkVersionDefault;
@@ -104,23 +105,6 @@
         mCollections.push_back(std::move(fileCollection));
     }
 
-    std::string buildResourceFileName(const ResourceFile& resFile) {
-        std::stringstream out;
-        out << "res/" << resFile.name.type;
-        if (resFile.config != ConfigDescription{}) {
-            out << "-" << resFile.config;
-        }
-        out << "/";
-
-        if (mContext.getNameMangler()->shouldMangle(resFile.name.package)) {
-            out << NameMangler::mangleEntry(resFile.name.package, resFile.name.entry);
-        } else {
-            out << resFile.name.entry;
-        }
-        out << file::getExtension(resFile.source.path);
-        return out.str();
-    }
-
     /**
      * Creates a SymbolTable that loads symbols from the various APKs and caches the
      * results for faster lookup.
@@ -425,45 +409,28 @@
             return false;
         }
 
-        if (!mTableMerger->merge(file->getSource(), table.get(), override)) {
-            return false;
+        bool result = false;
+        if (override) {
+            result = mTableMerger->mergeOverlay(file->getSource(), table.get());
+        } else {
+            result = mTableMerger->merge(file->getSource(), table.get());
         }
-        return true;
+        return result;
     }
 
-    bool mergeCompiledFile(io::IFile* file, std::unique_ptr<ResourceFile> fileDesc, bool override) {
-        // Apply the package name used for this compilation phase if none was specified.
-        if (fileDesc->name.package.empty()) {
-            fileDesc->name.package = mContext.getCompilationPackage().toString();
+    bool mergeCompiledFile(io::IFile* file, std::unique_ptr<ResourceFile> fileDesc, bool overlay) {
+        if (mOptions.verbose) {
+            mContext.getDiagnostics()->note(DiagMessage() << "adding " << file->getSource());
         }
 
-        // Mangle the name if necessary.
-        ResourceNameRef resName = fileDesc->name;
-        Maybe<ResourceName> mangledName = mContext.getNameMangler()->mangleName(fileDesc->name);
-        if (mangledName) {
-            resName = mangledName.value();
-        }
-
-        // If we are overriding resources, we supply a custom resolver function.
-        std::function<int(Value*,Value*)> resolver;
-        if (override) {
-            resolver = [](Value* a, Value* b) -> int {
-                int result = ResourceTable::resolveValueCollision(a, b);
-                if (result == 0) {
-                    // Always accept the new value if it would normally conflict (override).
-                    result = 1;
-                }
-                return result;
-            };
+        bool result = false;
+        if (overlay) {
+            result = mTableMerger->mergeFileOverlay(*fileDesc, file);
         } else {
-            // Otherwise use the default resolution.
-            resolver = ResourceTable::resolveValueCollision;
+            result = mTableMerger->mergeFile(*fileDesc, file);
         }
 
-        // Add this file to the table.
-        if (!mFinalTable.addFileReference(resName, fileDesc->config, fileDesc->source,
-                                          util::utf8ToUtf16(buildResourceFileName(*fileDesc)),
-                                          resolver, mContext.getDiagnostics())) {
+        if (!result) {
             return false;
         }
 
@@ -489,10 +456,6 @@
                 return false;
             }
         }
-
-        // Now add this file for later processing. Once the table is assigned IDs, we can compile
-        // this file.
-        mFilesToProcess.insert(FileToProcess{ std::move(fileDesc), file });
         return true;
     }
 
@@ -509,8 +472,8 @@
         }
 
         bool error = false;
-        for (const std::unique_ptr<io::IFile>& file : *collection) {
-            if (!processFile(file.get(), override)) {
+        for (auto iter = collection->iterator(); iter->hasNext(); ) {
+            if (!processFile(iter->next(), override)) {
                 error = true;
             }
         }
@@ -588,7 +551,9 @@
             return 1;
         }
 
-        mTableMerger = util::make_unique<TableMerger>(&mContext, &mFinalTable);
+        TableMergerOptions tableMergerOptions;
+        tableMergerOptions.autoAddOverlay = mOptions.autoAddOverlay;
+        mTableMerger = util::make_unique<TableMerger>(&mContext, &mFinalTable, tableMergerOptions);
 
         if (mOptions.verbose) {
             mContext.getDiagnostics()->note(
@@ -698,31 +663,47 @@
             return 1;
         }
 
-        for (const FileToProcess& file : mFilesToProcess) {
-            const StringPiece path = file.file->getSource().path;
+        for (auto& mergeEntry : mTableMerger->getFilesToMerge()) {
+            const ResourceKeyRef& key = mergeEntry.first;
+            const FileToMerge& fileToMerge = mergeEntry.second;
 
-            if (file.fileExport->name.type != ResourceType::kRaw &&
-                    util::stringEndsWith<char>(path, ".xml.flat")) {
+            const StringPiece path = fileToMerge.file->getSource().path;
+
+            if (key.name.type != ResourceType::kRaw &&
+                    (util::stringEndsWith<char>(path, ".xml.flat") ||
+                    util::stringEndsWith<char>(path, ".xml"))) {
                 if (mOptions.verbose) {
                     mContext.getDiagnostics()->note(DiagMessage() << "linking " << path);
                 }
 
-                std::unique_ptr<io::IData> data = file.file->openAsData();
+                io::IFile* file = fileToMerge.file;
+                std::unique_ptr<io::IData> data = file->openAsData();
                 if (!data) {
-                    mContext.getDiagnostics()->error(DiagMessage(file.file->getSource())
+                    mContext.getDiagnostics()->error(DiagMessage(file->getSource())
                                                      << "failed to open file");
                     return 1;
                 }
 
-                std::unique_ptr<xml::XmlResource> xmlRes = loadBinaryXmlSkipFileExport(
-                        file.file->getSource(), data->data(), data->size(),
-                        mContext.getDiagnostics());
+                std::unique_ptr<xml::XmlResource> xmlRes;
+                if (util::stringEndsWith<char>(path, ".flat")) {
+                    xmlRes = loadBinaryXmlSkipFileExport(file->getSource(),
+                                                         data->data(), data->size(),
+                                                         mContext.getDiagnostics());
+                } else {
+                    xmlRes = xml::inflate(data->data(), data->size(), mContext.getDiagnostics(),
+                                          file->getSource());
+                }
+
                 if (!xmlRes) {
                     return 1;
                 }
 
-                // Move the file description over.
-                xmlRes->file = std::move(*file.fileExport);
+                // Create the file description header.
+                xmlRes->file = ResourceFile{
+                        key.name.toResourceName(),
+                        key.config,
+                        fileToMerge.originalSource,
+                };
 
                 XmlReferenceLinker xmlLinker;
                 if (xmlLinker.consume(&mContext, xmlRes.get())) {
@@ -736,7 +717,7 @@
                         maxSdkLevel = std::max<size_t>(xmlRes->file.config.sdkVersion, 1u);
                     }
 
-                    if (!flattenXml(xmlRes.get(), buildResourceFileName(xmlRes->file), maxSdkLevel,
+                    if (!flattenXml(xmlRes.get(), fileToMerge.dstPath, maxSdkLevel,
                                     archiveWriter.get())) {
                         error = true;
                     }
@@ -750,19 +731,23 @@
                                                                     xmlRes->file.config,
                                                                     sdkLevel)) {
                                 xmlRes->file.config.sdkVersion = sdkLevel;
+
+                                std::string genResourcePath = ResourceUtils::buildResourceFileName(
+                                        xmlRes->file, mContext.getNameMangler());
+
                                 bool added = mFinalTable.addFileReference(
                                         xmlRes->file.name,
                                         xmlRes->file.config,
                                         xmlRes->file.source,
-                                        util::utf8ToUtf16(buildResourceFileName(xmlRes->file)),
+                                        util::utf8ToUtf16(genResourcePath),
                                         mContext.getDiagnostics());
                                 if (!added) {
                                     error = true;
                                     continue;
                                 }
 
-                                if (!flattenXml(xmlRes.get(), buildResourceFileName(xmlRes->file),
-                                                sdkLevel, archiveWriter.get())) {
+                                if (!flattenXml(xmlRes.get(), genResourcePath, sdkLevel,
+                                                archiveWriter.get())) {
                                     error = true;
                                 }
                             }
@@ -777,7 +762,7 @@
                     mContext.getDiagnostics()->note(DiagMessage() << "copying " << path);
                 }
 
-                if (!copyFileToArchive(file.file, buildResourceFileName(*file.fileExport), 0,
+                if (!copyFileToArchive(fileToMerge.file, fileToMerge.dstPath, 0,
                                        archiveWriter.get())) {
                     error = true;
                 }
@@ -845,15 +830,7 @@
 
         if (mOptions.verbose) {
             Debug::printTable(&mFinalTable);
-            for (; !mTableMerger->getFileMergeQueue()->empty();
-                    mTableMerger->getFileMergeQueue()->pop()) {
-                const FileToMerge& f = mTableMerger->getFileMergeQueue()->front();
-                mContext.getDiagnostics()->note(
-                        DiagMessage() << f.srcPath << " -> " << f.dstPath << " from (0x"
-                                      << std::hex << (uintptr_t) f.srcTable << std::dec);
-            }
         }
-
         return 0;
     }
 
@@ -861,24 +838,15 @@
     LinkOptions mOptions;
     LinkContext mContext;
     ResourceTable mFinalTable;
+
+    ResourceTable mLocalFileTable;
     std::unique_ptr<TableMerger> mTableMerger;
 
+    // A pointer to the FileCollection representing the filesystem (not archives).
     io::FileCollection* mFileCollection;
+
+    // A vector of IFileCollections. This is mainly here to keep ownership of the collections.
     std::vector<std::unique_ptr<io::IFileCollection>> mCollections;
-
-    struct FileToProcess {
-        std::unique_ptr<ResourceFile> fileExport;
-        io::IFile* file;
-    };
-
-    struct FileToProcessComparator {
-        bool operator()(const FileToProcess& a, const FileToProcess& b) {
-            return std::tie(a.fileExport->name, a.fileExport->config) <
-                    std::tie(b.fileExport->name, b.fileExport->config);
-        }
-    };
-
-    std::set<FileToProcess, FileToProcessComparator> mFilesToProcess;
 };
 
 int link(const std::vector<StringPiece>& args) {
@@ -915,6 +883,8 @@
                           "package name", &privateSymbolsPackage)
             .optionalFlagList("--extra-packages", "Generate the same R.java but with different "
                               "package names", &extraJavaPackages)
+            .optionalSwitch("--auto-add-overlay", "Allows the addition of new resources in "
+                            "overlays without <add-resource> tags", &options.autoAddOverlay)
             .optionalSwitch("-v", "Enables verbose logging", &options.verbose);
 
     if (!flags.parse("aapt2 link", args, &std::cerr)) {
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index a06a1bf..27a23bd 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -15,6 +15,7 @@
  */
 
 #include "ResourceTable.h"
+#include "ResourceUtils.h"
 #include "ResourceValues.h"
 #include "ValueVisitor.h"
 
@@ -26,8 +27,9 @@
 
 namespace aapt {
 
-TableMerger::TableMerger(IAaptContext* context, ResourceTable* outTable) :
-        mContext(context), mMasterTable(outTable) {
+TableMerger::TableMerger(IAaptContext* context, ResourceTable* outTable,
+                         const TableMergerOptions& options) :
+        mContext(context), mMasterTable(outTable), mOptions(options) {
     // Create the desired package that all tables will be merged into.
     mMasterPackage = mMasterTable->createPackage(
             mContext->getCompilationPackage(), mContext->getPackageId());
@@ -37,7 +39,8 @@
 /**
  * This will merge packages with the same package name (or no package name).
  */
-bool TableMerger::merge(const Source& src, ResourceTable* table, bool overrideExisting) {
+bool TableMerger::mergeImpl(const Source& src, ResourceTable* table,
+                            bool overlay, bool allowNew) {
     const uint8_t desiredPackageId = mContext->getPackageId();
 
     bool error = false;
@@ -55,19 +58,26 @@
             // mangled, then looked up at resolution time.
             // Also, when linking, we convert references with no package name to use
             // the compilation package name.
-            if (!doMerge(src, table, package.get(), false, overrideExisting)) {
-                error = true;
-            }
+            error |= !doMerge(src, table, package.get(),
+                              false /* mangle */, overlay, allowNew, {});
         }
     }
     return !error;
 }
 
+bool TableMerger::merge(const Source& src, ResourceTable* table) {
+    return mergeImpl(src, table, false /* overlay */, true /* allow new */);
+}
+
+bool TableMerger::mergeOverlay(const Source& src, ResourceTable* table) {
+    return mergeImpl(src, table, true /* overlay */, mOptions.autoAddOverlay);
+}
+
 /**
  * This will merge and mangle resources from a static library.
  */
 bool TableMerger::mergeAndMangle(const Source& src, const StringPiece16& packageName,
-                                 ResourceTable* table) {
+                                 ResourceTable* table, io::IFileCollection* collection) {
     bool error = false;
     for (auto& package : table->packages) {
         // Warn of packages with an unrelated ID.
@@ -79,16 +89,35 @@
 
         bool mangle = packageName != mContext->getCompilationPackage();
         mMergedPackages.insert(package->name);
-        if (!doMerge(src, table, package.get(), mangle, false)) {
-            error = true;
-        }
+
+        auto callback = [&](const ResourceNameRef& name, const ConfigDescription& config,
+                            FileReference* newFile, FileReference* oldFile) -> bool {
+            // The old file's path points inside the APK, so we can use it as is.
+            io::IFile* f = collection->findFile(util::utf16ToUtf8(*oldFile->path));
+            if (!f) {
+                mContext->getDiagnostics()->error(DiagMessage(src) << "file '" << *oldFile->path
+                                                  << "' not found");
+                return false;
+            }
+
+            mFilesToMerge[ResourceKeyRef{ name, config }] = FileToMerge{
+                    f, oldFile->getSource(), util::utf16ToUtf8(*newFile->path) };
+            return true;
+        };
+
+        error |= !doMerge(src, table, package.get(),
+                          mangle, false /* overlay */, true /* allow new */, callback);
     }
     return !error;
 }
 
-bool TableMerger::doMerge(const Source& src, ResourceTable* srcTable,
-                          ResourceTablePackage* srcPackage, const bool manglePackage,
-                          const bool overrideExisting) {
+bool TableMerger::doMerge(const Source& src,
+                          ResourceTable* srcTable,
+                          ResourceTablePackage* srcPackage,
+                          const bool manglePackage,
+                          const bool overlay,
+                          const bool allowNewResources,
+                          FileMergeCallback callback) {
     bool error = false;
 
     for (auto& srcType : srcPackage->types) {
@@ -112,10 +141,33 @@
         for (auto& srcEntry : srcType->entries) {
             ResourceEntry* dstEntry;
             if (manglePackage) {
-                dstEntry = dstType->findOrCreateEntry(NameMangler::mangleEntry(
-                        srcPackage->name, srcEntry->name));
+                std::u16string mangledName = NameMangler::mangleEntry(srcPackage->name,
+                                                                      srcEntry->name);
+                if (allowNewResources) {
+                    dstEntry = dstType->findOrCreateEntry(mangledName);
+                } else {
+                    dstEntry = dstType->findEntry(mangledName);
+                }
             } else {
-                dstEntry = dstType->findOrCreateEntry(srcEntry->name);
+                if (allowNewResources) {
+                    dstEntry = dstType->findOrCreateEntry(srcEntry->name);
+                } else {
+                    dstEntry = dstType->findEntry(srcEntry->name);
+                }
+            }
+
+            if (!dstEntry) {
+                mContext->getDiagnostics()->error(DiagMessage(src)
+                                                  << "resource "
+                                                  << ResourceNameRef(srcPackage->name,
+                                                                     srcType->type,
+                                                                     srcEntry->name)
+                                                  << " does not override an existing resource");
+                mContext->getDiagnostics()->note(DiagMessage(src)
+                                                 << "define an <add-resource> tag or use "
+                                                    "--auto-add-overlay");
+                error = true;
+                continue;
             }
 
             if (srcEntry->symbolStatus.state != SymbolState::kUndefined) {
@@ -143,6 +195,8 @@
                 }
             }
 
+            ResourceNameRef resName(mMasterPackage->name, dstType->type, dstEntry->name);
+
             for (ResourceConfigValue& srcValue : srcEntry->values) {
                 auto iter = std::lower_bound(dstEntry->values.begin(), dstEntry->values.end(),
                                              srcValue.config, cmp::lessThanConfig);
@@ -150,7 +204,7 @@
                 if (iter != dstEntry->values.end() && iter->config == srcValue.config) {
                     const int collisionResult = ResourceTable::resolveValueCollision(
                             iter->value.get(), srcValue.value.get());
-                    if (collisionResult == 0 && !overrideExisting) {
+                    if (collisionResult == 0 && !overlay) {
                         // Error!
                         ResourceNameRef resourceName(srcPackage->name,
                                                      srcType->type,
@@ -175,10 +229,26 @@
                     iter = dstEntry->values.insert(iter, ResourceConfigValue{ srcValue.config });
                 }
 
-                if (manglePackage) {
-                    iter->value = cloneAndMangle(srcTable, srcPackage->name, srcValue.value.get());
+                if (FileReference* f = valueCast<FileReference>(srcValue.value.get())) {
+                    std::unique_ptr<FileReference> newFileRef;
+                    if (manglePackage) {
+                        newFileRef = cloneAndMangleFile(srcPackage->name, *f);
+                    } else {
+                        newFileRef = std::unique_ptr<FileReference>(f->clone(
+                                &mMasterTable->stringPool));
+                    }
+
+                    if (callback) {
+                        if (!callback(resName, iter->config, newFileRef.get(), f)) {
+                            error = true;
+                            continue;
+                        }
+                    }
+                    iter->value = std::move(newFileRef);
+
                 } else {
-                    iter->value = clone(srcValue.value.get());
+                    iter->value = std::unique_ptr<Value>(srcValue.value->clone(
+                            &mMasterTable->stringPool));
                 }
             }
         }
@@ -186,28 +256,52 @@
     return !error;
 }
 
-std::unique_ptr<Value> TableMerger::cloneAndMangle(ResourceTable* table,
-                                                   const std::u16string& package,
-                                                   Value* value) {
-    if (FileReference* f = valueCast<FileReference>(value)) {
-        // Mangle the path.
-        StringPiece16 prefix, entry, suffix;
-        if (util::extractResFilePathParts(*f->path, &prefix, &entry, &suffix)) {
-            std::u16string mangledEntry = NameMangler::mangleEntry(package, entry.toString());
-            std::u16string newPath = prefix.toString() + mangledEntry + suffix.toString();
-            mFilesToMerge.push(FileToMerge{ table, *f->path, newPath });
-            std::unique_ptr<FileReference> fileRef = util::make_unique<FileReference>(
-                    mMasterTable->stringPool.makeRef(newPath));
-            fileRef->setComment(f->getComment());
-            fileRef->setSource(f->getSource());
-            return std::move(fileRef);
-        }
+std::unique_ptr<FileReference> TableMerger::cloneAndMangleFile(const std::u16string& package,
+                                                               const FileReference& fileRef) {
+
+    StringPiece16 prefix, entry, suffix;
+    if (util::extractResFilePathParts(*fileRef.path, &prefix, &entry, &suffix)) {
+        std::u16string mangledEntry = NameMangler::mangleEntry(package, entry.toString());
+        std::u16string newPath = prefix.toString() + mangledEntry + suffix.toString();
+        std::unique_ptr<FileReference> newFileRef = util::make_unique<FileReference>(
+                mMasterTable->stringPool.makeRef(newPath));
+        newFileRef->setComment(fileRef.getComment());
+        newFileRef->setSource(fileRef.getSource());
+        return newFileRef;
     }
-    return clone(value);
+    return std::unique_ptr<FileReference>(fileRef.clone(&mMasterTable->stringPool));
 }
 
-std::unique_ptr<Value> TableMerger::clone(Value* value) {
-    return std::unique_ptr<Value>(value->clone(&mMasterTable->stringPool));
+bool TableMerger::mergeFileImpl(const ResourceFile& fileDesc, io::IFile* file, bool overlay) {
+    ResourceTable table;
+    std::u16string path = util::utf8ToUtf16(ResourceUtils::buildResourceFileName(fileDesc,
+                                                                                 nullptr));
+    std::unique_ptr<FileReference> fileRef = util::make_unique<FileReference>(
+            table.stringPool.makeRef(path));
+    fileRef->setSource(fileDesc.source);
+
+    ResourceTablePackage* pkg = table.createPackage(fileDesc.name.package, 0x0);
+    pkg->findOrCreateType(fileDesc.name.type)
+            ->findOrCreateEntry(fileDesc.name.entry)
+            ->values.push_back(ResourceConfigValue{ fileDesc.config, std::move(fileRef) });
+
+    auto callback = [&](const ResourceNameRef& name, const ConfigDescription& config,
+                       FileReference* newFile, FileReference* oldFile) -> bool {
+        mFilesToMerge[ResourceKeyRef{ name, config }] = FileToMerge{
+                file, oldFile->getSource(), util::utf16ToUtf8(*newFile->path) };
+        return true;
+    };
+
+    return doMerge(file->getSource(), &table, pkg,
+                   false /* mangle */, overlay /* overlay */, true /* allow new */, callback);
+}
+
+bool TableMerger::mergeFile(const ResourceFile& fileDesc, io::IFile* file) {
+    return mergeFileImpl(fileDesc, file, false /* overlay */);
+}
+
+bool TableMerger::mergeFileOverlay(const ResourceFile& fileDesc, io::IFile* file) {
+    return mergeFileImpl(fileDesc, file, true /* overlay */);
 }
 
 } // namespace aapt
diff --git a/tools/aapt2/link/TableMerger.h b/tools/aapt2/link/TableMerger.h
index a2c9dbf..e1be5d5 100644
--- a/tools/aapt2/link/TableMerger.h
+++ b/tools/aapt2/link/TableMerger.h
@@ -17,21 +17,40 @@
 #ifndef AAPT_TABLEMERGER_H
 #define AAPT_TABLEMERGER_H
 
+#include "Resource.h"
 #include "ResourceTable.h"
 #include "ResourceValues.h"
+#include "io/File.h"
+#include "process/IResourceTableConsumer.h"
 #include "util/Util.h"
 
-#include "process/IResourceTableConsumer.h"
-
-#include <queue>
-#include <set>
+#include <functional>
+#include <map>
 
 namespace aapt {
 
 struct FileToMerge {
-    ResourceTable* srcTable;
-    std::u16string srcPath;
-    std::u16string dstPath;
+    /**
+     * The compiled file from which to read the data.
+     */
+    io::IFile* file;
+
+    /**
+     * Where the original, uncompiled file came from.
+     */
+    Source originalSource;
+
+    /**
+     * The destination path within the APK/archive.
+     */
+    std::string dstPath;
+};
+
+struct TableMergerOptions {
+    /**
+     * If true, resources in overlays can be added without previously having existed.
+     */
+    bool autoAddOverlay = false;
 };
 
 /**
@@ -50,40 +69,74 @@
  */
 class TableMerger {
 public:
-    TableMerger(IAaptContext* context, ResourceTable* outTable);
+    /**
+     * Note: The outTable ResourceTable must live longer than this TableMerger. References
+     * are made to this ResourceTable for efficiency reasons.
+     */
+    TableMerger(IAaptContext* context, ResourceTable* outTable, const TableMergerOptions& options);
 
-    inline std::queue<FileToMerge>* getFileMergeQueue() {
-        return &mFilesToMerge;
+    const std::map<ResourceKeyRef, FileToMerge>& getFilesToMerge() {
+        return mFilesToMerge;
     }
 
-    inline const std::set<std::u16string>& getMergedPackages() const {
+    const std::set<std::u16string>& getMergedPackages() const {
         return mMergedPackages;
     }
 
     /**
      * Merges resources from the same or empty package. This is for local sources.
      */
-    bool merge(const Source& src, ResourceTable* table, bool overrideExisting);
+    bool merge(const Source& src, ResourceTable* table);
+
+    /**
+     * Merges resources from an overlay ResourceTable.
+     */
+    bool mergeOverlay(const Source& src, ResourceTable* table);
 
     /**
      * Merges resources from the given package, mangling the name. This is for static libraries.
+     * An io::IFileCollection is needed in order to find the referenced Files and process them.
      */
-    bool mergeAndMangle(const Source& src, const StringPiece16& package, ResourceTable* table);
+    bool mergeAndMangle(const Source& src, const StringPiece16& package, ResourceTable* table,
+                        io::IFileCollection* collection);
+
+    /**
+     * Merges a compiled file that belongs to this same or empty package. This is for local sources.
+     */
+    bool mergeFile(const ResourceFile& fileDesc, io::IFile* file);
+
+    /**
+     * Merges a compiled file from an overlay, overriding an existing definition.
+     */
+    bool mergeFileOverlay(const ResourceFile& fileDesc, io::IFile* file);
 
 private:
+    using FileMergeCallback = std::function<bool(const ResourceNameRef&,
+                                                 const ConfigDescription& config,
+                                                 FileReference*,
+                                                 FileReference*)>;
+
     IAaptContext* mContext;
     ResourceTable* mMasterTable;
+    TableMergerOptions mOptions;
     ResourceTablePackage* mMasterPackage;
 
     std::set<std::u16string> mMergedPackages;
-    std::queue<FileToMerge> mFilesToMerge;
+    std::map<ResourceKeyRef, FileToMerge> mFilesToMerge;
+
+    bool mergeFileImpl(const ResourceFile& fileDesc, io::IFile* file, bool overlay);
+
+    bool mergeImpl(const Source& src, ResourceTable* srcTable,
+                   bool overlay, bool allowNew);
 
     bool doMerge(const Source& src, ResourceTable* srcTable, ResourceTablePackage* srcPackage,
-                 const bool manglePackage, const bool overrideExisting);
+                 const bool manglePackage,
+                 const bool overlay,
+                 const bool allowNewResources,
+                 FileMergeCallback callback);
 
-    std::unique_ptr<Value> cloneAndMangle(ResourceTable* table, const std::u16string& package,
-                                          Value* value);
-    std::unique_ptr<Value> clone(Value* value);
+    std::unique_ptr<FileReference> cloneAndMangleFile(const std::u16string& package,
+                                                      const FileReference& value);
 };
 
 } // namespace aapt
diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp
index b7ffba7..fa4afd3 100644
--- a/tools/aapt2/link/TableMerger_test.cpp
+++ b/tools/aapt2/link/TableMerger_test.cpp
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
+#include "io/FileSystem.h"
 #include "link/TableMerger.h"
-
 #include "test/Builders.h"
 #include "test/Context.h"
 
@@ -57,10 +57,11 @@
             .build();
 
     ResourceTable finalTable;
-    TableMerger merger(mContext.get(), &finalTable);
+    TableMerger merger(mContext.get(), &finalTable, TableMergerOptions{});
+    io::FileCollection collection;
 
-    ASSERT_TRUE(merger.merge({}, tableA.get(), false));
-    ASSERT_TRUE(merger.mergeAndMangle({}, u"com.app.b", tableB.get()));
+    ASSERT_TRUE(merger.merge({}, tableA.get()));
+    ASSERT_TRUE(merger.mergeAndMangle({}, u"com.app.b", tableB.get(), &collection));
 
     EXPECT_TRUE(merger.getMergedPackages().count(u"com.app.b") != 0);
 
@@ -76,6 +77,60 @@
     AAPT_EXPECT_TRUE(finalTable.findResource(test::parseNameOrDie(u"@com.app.a:id/com.app.b$foo")));
 }
 
+TEST_F(TableMergerTest, MergeFile) {
+    ResourceTable finalTable;
+    TableMergerOptions options;
+    options.autoAddOverlay = false;
+    TableMerger merger(mContext.get(), &finalTable, options);
+
+    ResourceFile fileDesc;
+    fileDesc.config = test::parseConfigOrDie("hdpi-v4");
+    fileDesc.name = test::parseNameOrDie(u"@layout/main");
+    fileDesc.source = Source("res/layout-hdpi/main.xml");
+    test::TestFile testFile("path/to/res/layout-hdpi/main.xml.flat");
+
+    ASSERT_TRUE(merger.mergeFile(fileDesc, &testFile));
+
+    FileReference* file = test::getValueForConfig<FileReference>(&finalTable,
+                                                                 u"@com.app.a:layout/main",
+                                                                 test::parseConfigOrDie("hdpi-v4"));
+    ASSERT_NE(nullptr, file);
+    EXPECT_EQ(std::u16string(u"res/layout-hdpi-v4/main.xml"), *file->path);
+
+    ResourceName name = test::parseNameOrDie(u"@com.app.a:layout/main");
+    ResourceKeyRef key = { name, test::parseConfigOrDie("hdpi-v4") };
+
+    auto iter = merger.getFilesToMerge().find(key);
+    ASSERT_NE(merger.getFilesToMerge().end(), iter);
+
+    const FileToMerge& actualFileToMerge = iter->second;
+    EXPECT_EQ(&testFile, actualFileToMerge.file);
+    EXPECT_EQ(std::string("res/layout-hdpi-v4/main.xml"), actualFileToMerge.dstPath);
+}
+
+TEST_F(TableMergerTest, MergeFileOverlay) {
+    ResourceTable finalTable;
+    TableMergerOptions tableMergerOptions;
+    tableMergerOptions.autoAddOverlay = false;
+    TableMerger merger(mContext.get(), &finalTable, tableMergerOptions);
+
+    ResourceFile fileDesc;
+    fileDesc.name = test::parseNameOrDie(u"@xml/foo");
+    test::TestFile fileA("path/to/fileA.xml.flat");
+    test::TestFile fileB("path/to/fileB.xml.flat");
+
+    ASSERT_TRUE(merger.mergeFile(fileDesc, &fileA));
+    ASSERT_TRUE(merger.mergeFileOverlay(fileDesc, &fileB));
+
+    ResourceName name = test::parseNameOrDie(u"@com.app.a:xml/foo");
+    ResourceKeyRef key = { name, ConfigDescription{} };
+    auto iter = merger.getFilesToMerge().find(key);
+    ASSERT_NE(merger.getFilesToMerge().end(), iter);
+
+    const FileToMerge& actualFileToMerge = iter->second;
+    EXPECT_EQ(&fileB, actualFileToMerge.file);
+}
+
 TEST_F(TableMergerTest, MergeFileReferences) {
     std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder()
             .setPackageId(u"com.app.a", 0x7f)
@@ -87,10 +142,12 @@
             .build();
 
     ResourceTable finalTable;
-    TableMerger merger(mContext.get(), &finalTable);
+    TableMerger merger(mContext.get(), &finalTable, TableMergerOptions{});
+    io::FileCollection collection;
+    collection.insertFile("res/xml/file.xml");
 
-    ASSERT_TRUE(merger.merge({}, tableA.get(), false));
-    ASSERT_TRUE(merger.mergeAndMangle({}, u"com.app.b", tableB.get()));
+    ASSERT_TRUE(merger.merge({}, tableA.get()));
+    ASSERT_TRUE(merger.mergeAndMangle({}, u"com.app.b", tableB.get(), &collection));
 
     FileReference* f = test::getValue<FileReference>(&finalTable, u"@com.app.a:xml/file");
     ASSERT_NE(f, nullptr);
@@ -100,13 +157,90 @@
     ASSERT_NE(f, nullptr);
     EXPECT_EQ(std::u16string(u"res/xml/com.app.b$file.xml"), *f->path);
 
-    std::queue<FileToMerge>* filesToMerge = merger.getFileMergeQueue();
-    ASSERT_FALSE(filesToMerge->empty());
+    ResourceName name = test::parseNameOrDie(u"@com.app.a:xml/com.app.b$file");
+    ResourceKeyRef key = { name, ConfigDescription{} };
+    auto iter = merger.getFilesToMerge().find(key);
+    ASSERT_NE(merger.getFilesToMerge().end(), iter);
 
-    FileToMerge& fileToMerge = filesToMerge->front();
-    EXPECT_EQ(fileToMerge.srcTable, tableB.get());
-    EXPECT_EQ(fileToMerge.srcPath, u"res/xml/file.xml");
-    EXPECT_EQ(fileToMerge.dstPath, u"res/xml/com.app.b$file.xml");
+    const FileToMerge& actualFileToMerge = iter->second;
+    EXPECT_EQ(Source("res/xml/file.xml"), actualFileToMerge.file->getSource());
+    EXPECT_EQ(std::string("res/xml/com.app.b$file.xml"), actualFileToMerge.dstPath);
+}
+
+TEST_F(TableMergerTest, OverrideResourceWithOverlay) {
+    std::unique_ptr<ResourceTable> base = test::ResourceTableBuilder()
+            .setPackageId(u"", 0x00)
+            .addValue(u"@bool/foo", ResourceUtils::tryParseBool(u"true"))
+            .build();
+    std::unique_ptr<ResourceTable> overlay = test::ResourceTableBuilder()
+            .setPackageId(u"", 0x00)
+            .addValue(u"@bool/foo", ResourceUtils::tryParseBool(u"false"))
+            .build();
+
+    ResourceTable finalTable;
+    TableMergerOptions tableMergerOptions;
+    tableMergerOptions.autoAddOverlay = false;
+    TableMerger merger(mContext.get(), &finalTable, tableMergerOptions);
+
+    ASSERT_TRUE(merger.merge({}, base.get()));
+    ASSERT_TRUE(merger.mergeOverlay({}, overlay.get()));
+
+    BinaryPrimitive* foo = test::getValue<BinaryPrimitive>(&finalTable, u"@com.app.a:bool/foo");
+    ASSERT_NE(nullptr, foo);
+    EXPECT_EQ(0x0u, foo->value.data);
+}
+
+TEST_F(TableMergerTest, MergeAddResourceFromOverlay) {
+    std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder()
+            .setPackageId(u"", 0x7f)
+            .setSymbolState(u"@bool/foo", {}, SymbolState::kUndefined)
+            .build();
+    std::unique_ptr<ResourceTable> tableB = test::ResourceTableBuilder()
+            .setPackageId(u"", 0x7f)
+            .addValue(u"@bool/foo", ResourceUtils::tryParseBool(u"true"))
+            .build();
+
+    ResourceTable finalTable;
+    TableMerger merger(mContext.get(), &finalTable, TableMergerOptions{});
+
+    ASSERT_TRUE(merger.merge({}, tableA.get()));
+    ASSERT_TRUE(merger.mergeOverlay({}, tableB.get()));
+}
+
+TEST_F(TableMergerTest, MergeAddResourceFromOverlayWithAutoAddOverlay) {
+    std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder()
+            .setPackageId(u"", 0x7f)
+            .build();
+    std::unique_ptr<ResourceTable> tableB = test::ResourceTableBuilder()
+            .setPackageId(u"", 0x7f)
+            .addValue(u"@bool/foo", ResourceUtils::tryParseBool(u"true"))
+            .build();
+
+    ResourceTable finalTable;
+    TableMergerOptions options;
+    options.autoAddOverlay = true;
+    TableMerger merger(mContext.get(), &finalTable, options);
+
+    ASSERT_TRUE(merger.merge({}, tableA.get()));
+    ASSERT_TRUE(merger.mergeOverlay({}, tableB.get()));
+}
+
+TEST_F(TableMergerTest, FailToMergeNewResourceWithoutAutoAddOverlay) {
+    std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder()
+            .setPackageId(u"", 0x7f)
+            .build();
+    std::unique_ptr<ResourceTable> tableB = test::ResourceTableBuilder()
+            .setPackageId(u"", 0x7f)
+            .addValue(u"@bool/foo", ResourceUtils::tryParseBool(u"true"))
+            .build();
+
+    ResourceTable finalTable;
+    TableMergerOptions options;
+    options.autoAddOverlay = false;
+    TableMerger merger(mContext.get(), &finalTable, options);
+
+    ASSERT_TRUE(merger.merge({}, tableA.get()));
+    ASSERT_FALSE(merger.mergeOverlay({}, tableB.get()));
 }
 
 } // namespace aapt
diff --git a/tools/aapt2/test/Common.h b/tools/aapt2/test/Common.h
index 6fdaebb..51e2dd4 100644
--- a/tools/aapt2/test/Common.h
+++ b/tools/aapt2/test/Common.h
@@ -22,7 +22,7 @@
 #include "ResourceTable.h"
 #include "ResourceUtils.h"
 #include "ValueVisitor.h"
-
+#include "io/File.h"
 #include "process/IResourceTableConsumer.h"
 #include "util/StringPiece.h"
 
@@ -87,6 +87,22 @@
     return getValueForConfig<T>(table, resName, {});
 }
 
+class TestFile : public io::IFile {
+private:
+    Source mSource;
+
+public:
+    TestFile(const StringPiece& path) : mSource(path) {}
+
+    std::unique_ptr<io::IData> openAsData() override {
+        return {};
+    }
+
+    const Source& getSource() const override {
+        return mSource;
+    }
+};
+
 } // namespace test
 } // namespace aapt
 
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.cpp b/tools/aapt2/unflatten/BinaryResourceParser.cpp
index 46b5205..21e476f 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.cpp
+++ b/tools/aapt2/unflatten/BinaryResourceParser.cpp
@@ -447,6 +447,10 @@
         case Public_entry::kPublic:
             symbol.state = SymbolState::kPublic;
             break;
+
+        case Public_entry::kUndefined:
+            symbol.state = SymbolState::kUndefined;
+            break;
         }
 
         if (!mTable->setSymbolStateAllowMangled(name, resId, symbol, mContext->getDiagnostics())) {
diff --git a/tools/aapt2/util/Maybe.h b/tools/aapt2/util/Maybe.h
index aa409ea..10a2803 100644
--- a/tools/aapt2/util/Maybe.h
+++ b/tools/aapt2/util/Maybe.h
@@ -285,6 +285,8 @@
 -> decltype(std::declval<T> == std::declval<U>) {
     if (a && b) {
         return a.value() == b.value();
+    } else if (!a && !b) {
+        return true;
     }
     return false;
 }
diff --git a/tools/aapt2/util/Maybe_test.cpp b/tools/aapt2/util/Maybe_test.cpp
index 9cca40e..5d42dc3 100644
--- a/tools/aapt2/util/Maybe_test.cpp
+++ b/tools/aapt2/util/Maybe_test.cpp
@@ -124,9 +124,12 @@
     Maybe<int> b = 1;
     Maybe<int> c;
 
+    Maybe<int> emptyA, emptyB;
+
     EXPECT_EQ(a, b);
     EXPECT_EQ(b, a);
     EXPECT_NE(a, c);
+    EXPECT_EQ(emptyA, emptyB);
 }
 
 } // namespace aapt
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index bd5335e..63411b0 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -1634,6 +1634,11 @@
     }
 
     @Override
+    public void sendStickyBroadcastAsUser(Intent intent, UserHandle user, Bundle options) {
+        // pass
+    }
+
+    @Override
     public void sendStickyOrderedBroadcastAsUser(Intent intent,
             UserHandle user, BroadcastReceiver resultReceiver,
             Handler scheduler, int initialCode, String initialData,
@@ -1804,24 +1809,24 @@
     }
 
     @Override
-    public Context createDeviceEncryptedContext(Context context) {
+    public Context createDeviceEncryptedStorageContext() {
         // pass
         return null;
     }
 
     @Override
-    public Context createCredentialEncryptedContext(Context context) {
+    public Context createCredentialEncryptedStorageContext() {
         // pass
         return null;
     }
 
     @Override
-    public boolean isDeviceEncrypted() {
+    public boolean isDeviceEncryptedStorage() {
         return false;
     }
 
     @Override
-    public boolean isCredentialEncrypted() {
+    public boolean isCredentialEncryptedStorage() {
         return false;
     }
 }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java
index 26f9000..d797eec 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java
@@ -42,10 +42,6 @@
 import java.util.Collections;
 import java.util.List;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
 /**
  * Action to render a given Drawable provided through {@link DrawableParams#getDrawable()}.
  *
@@ -72,8 +68,12 @@
         BridgeContext context = getContext();
         drawableResource = context.getRenderResources().resolveResValue(drawableResource);
 
-        if (drawableResource == null ||
-                drawableResource.getResourceType() != ResourceType.DRAWABLE) {
+        if (drawableResource == null) {
+            return Status.ERROR_NOT_A_DRAWABLE.createResult();
+        }
+
+        ResourceType resourceType = drawableResource.getResourceType();
+        if (resourceType != ResourceType.DRAWABLE && resourceType != ResourceType.MIPMAP) {
             return Status.ERROR_NOT_A_DRAWABLE.createResult();
         }