Merge "Implicitly expose activities"
diff --git a/Android.mk b/Android.mk
index 36a35a1..88be12f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -38,7 +38,9 @@
 include $(CLEAR_VARS)
 
 # FRAMEWORKS_BASE_SUBDIRS comes from build/core/pathmap.mk
-LOCAL_SRC_FILES := $(call find-other-java-files,$(FRAMEWORKS_BASE_SUBDIRS))
+LOCAL_SRC_FILES := \
+        $(call find-other-java-files,$(FRAMEWORKS_BASE_SUBDIRS)) \
+        $(call all-proto-files-under, core/proto)
 
 # EventLogTags files.
 LOCAL_SRC_FILES += \
@@ -145,6 +147,7 @@
 	core/java/android/content/ISyncStatusObserver.aidl \
 	core/java/android/content/pm/ILauncherApps.aidl \
 	core/java/android/content/pm/IOnAppsChangedListener.aidl \
+	core/java/android/content/pm/IOnPermissionsChangeListener.aidl \
 	core/java/android/content/pm/IOtaDexopt.aidl \
 	core/java/android/content/pm/IPackageDataObserver.aidl \
 	core/java/android/content/pm/IPackageDeleteObserver.aidl \
@@ -157,7 +160,7 @@
 	core/java/android/content/pm/IPackageManager.aidl \
 	core/java/android/content/pm/IPackageMoveObserver.aidl \
 	core/java/android/content/pm/IPackageStatsObserver.aidl \
-	core/java/android/content/pm/IOnPermissionsChangeListener.aidl \
+	core/java/android/content/pm/IPinItemRequest.aidl \
 	core/java/android/content/pm/IShortcutService.aidl \
 	core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl \
 	core/java/android/database/IContentObserver.aidl \
@@ -233,6 +236,9 @@
 	core/java/android/os/IDeviceIdentifiersPolicyService.aidl \
 	core/java/android/os/IDeviceIdleController.aidl \
 	core/java/android/os/IHardwarePropertiesManager.aidl \
+	core/java/android/os/IIncidentManager.aidl \
+	core/java/android/os/IIncidentReportCompletedListener.aidl \
+	core/java/android/os/IIncidentReportStatusListener.aidl \
 	core/java/android/os/IMaintenanceActivityListener.aidl \
 	core/java/android/os/IMessenger.aidl \
 	core/java/android/os/INetworkActivityListener.aidl \
@@ -527,6 +533,10 @@
     android.hardware.thermal@1.0-java-constants         \
     android.hardware.health@1.0-java-constants          \
 
+LOCAL_PROTOC_OPTIMIZE_TYPE := stream
+LOCAL_PROTOC_FLAGS := \
+    -Iexternal/protobuf/src
+
 LOCAL_MODULE := framework
 
 LOCAL_JACK_FLAGS := --multi-dex native
@@ -1384,6 +1394,35 @@
 
 include $(BUILD_JAVA_LIBRARY)
 
+# ====  c++ proto host library  ==============================
+include $(CLEAR_VARS)
+LOCAL_MODULE := libplatformprotos
+LOCAL_PROTOC_OPTIMIZE_TYPE := full
+LOCAL_PROTOC_FLAGS := \
+    --include_source_info \
+    -Iexternal/protobuf/src
+LOCAL_SRC_FILES := \
+    $(call all-proto-files-under, core/proto) \
+    $(call all-proto-files-under, libs/incident/proto)
+LOCAL_C_INCLUDES := \
+    $(call generated-sources-dir-for,STATIC_LIBRARIES,libplatformprotos,)/proto
+LOCAL_EXPORT_C_INCLUDES := \
+    $(call generated-sources-dir-for,STATIC_LIBRARIES,libplatformprotos,)/proto
+include $(BUILD_HOST_SHARED_LIBRARY)
+
+
+# ====  java proto host library  ==============================
+include $(CLEAR_VARS)
+LOCAL_MODULE := platformprotos
+LOCAL_PROTOC_OPTIMIZE_TYPE := full
+LOCAL_PROTOC_FLAGS := \
+    -Iexternal/protobuf/src
+LOCAL_SOURCE_FILES_ALL_GENERATED := true
+LOCAL_SRC_FILES := \
+    $(call all-proto-files-under, core/proto) \
+    $(call all-proto-files-under, libs/incident/proto)
+include $(BUILD_HOST_JAVA_LIBRARY)
+
 
 # Include subdirectory makefiles
 # ============================================================
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index c28db57..75de4a7 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,5 +1,5 @@
 [Hook Scripts]
-checkstyle_hook = ../../development/tools/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
+checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
                   -fw core/java/android/animation/
                       core/java/android/hardware/usb/
                       core/java/android/print/
diff --git a/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java b/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java
index c7da48c..6159da4 100644
--- a/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java
+++ b/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java
@@ -121,7 +121,7 @@
     @Parameterized.Parameters(name = "Factory:{0},depth:{1}")
     public static Iterable<Object[]> params() {
         List<Object[]> params = new ArrayList<>();
-        for (int depth : new int[] {1, 6, 10}) {
+        for (int depth : new int[] { 6 }) {
             for (SubTreeFactory subTreeFactory : sSubTreeFactories) {
                 params.add(new Object[]{ subTreeFactory, depth });
             }
@@ -198,6 +198,21 @@
         });
     }
 
+    @Test
+    public void testRecordAfterAdd() throws Throwable {
+        testParentWithChild((state, width, height, parent, child) -> {
+            while (state.keepRunning()) {
+                state.pauseTiming();
+                parent.removeAllViews();
+                updateAndValidateDisplayList(parent); // Note, done to be safe, likely not needed
+                parent.addView(child);
+                layout(width, height, child);
+                state.resumeTiming();
+
+                updateAndValidateDisplayList(parent);
+            }
+        });
+    }
 
     private void testVisibility(int fromVisibility, int toVisibility) throws Throwable {
         testParentWithChild((state, width, height, parent, child) -> {
diff --git a/api/current.txt b/api/current.txt
index 66390e8..89573ac 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3657,6 +3657,7 @@
     method public void setIntent(android.content.Intent);
     method public final void setMediaController(android.media.session.MediaController);
     method public void setOverlayWithDecorCaptionEnabled(boolean);
+    method public void setPictureInPictureActions(java.util.List<android.app.RemoteAction>);
     method public void setPictureInPictureAspectRatio(float);
     method public final deprecated void setProgress(int);
     method public final deprecated void setProgressBarIndeterminate(boolean);
@@ -3737,6 +3738,7 @@
     method public int getLauncherLargeIconDensity();
     method public int getLauncherLargeIconSize();
     method public int getLockTaskModeState();
+    method public static int getMaxNumPictureInPictureActions();
     method public int getMemoryClass();
     method public void getMemoryInfo(android.app.ActivityManager.MemoryInfo);
     method public static void getMyMemoryState(android.app.ActivityManager.RunningAppProcessInfo);
@@ -5547,6 +5549,22 @@
     field public static final int STYLE_SPINNER = 0; // 0x0
   }
 
+  public final class RemoteAction implements android.os.Parcelable {
+    ctor public RemoteAction(android.graphics.drawable.Icon, java.lang.CharSequence, java.lang.CharSequence, android.app.RemoteAction.OnActionListener);
+    method public android.app.RemoteAction clone();
+    method public int describeContents();
+    method public void dump(java.lang.String, java.io.PrintWriter);
+    method public java.lang.CharSequence getContentDescription();
+    method public android.graphics.drawable.Icon getIcon();
+    method public java.lang.CharSequence getTitle();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.RemoteAction> CREATOR;
+  }
+
+  public static abstract interface RemoteAction.OnActionListener {
+    method public abstract void onAction(android.app.RemoteAction);
+  }
+
   public final class RemoteInput implements android.os.Parcelable {
     method public static void addResultsToIntent(android.app.RemoteInput[], android.content.Intent, android.os.Bundle);
     method public int describeContents();
@@ -6206,6 +6224,9 @@
     field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION";
     field public static final deprecated java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME";
     field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM = "android.app.extra.PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM";
+    field public static final java.lang.String EXTRA_PROVISIONING_DISCLAIMERS = "android.app.extra.PROVISIONING_DISCLAIMERS";
+    field public static final java.lang.String EXTRA_PROVISIONING_DISCLAIMER_CONTENT = "android.app.extra.PROVISIONING_DISCLAIMER_CONTENT";
+    field public static final java.lang.String EXTRA_PROVISIONING_DISCLAIMER_HEADER = "android.app.extra.PROVISIONING_DISCLAIMER_HEADER";
     field public static final java.lang.String EXTRA_PROVISIONING_EMAIL_ADDRESS = "android.app.extra.PROVISIONING_EMAIL_ADDRESS";
     field public static final java.lang.String EXTRA_PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION = "android.app.extra.PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION";
     field public static final java.lang.String EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED = "android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED";
@@ -9695,6 +9716,7 @@
 
   public class LauncherApps {
     method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
+    method public android.content.pm.LauncherApps.PinItemRequest getPinItemRequest(android.content.Intent);
     method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(android.content.pm.ShortcutInfo, int);
     method public android.graphics.drawable.Drawable getShortcutIconDrawable(android.content.pm.ShortcutInfo, int);
     method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
@@ -9710,6 +9732,8 @@
     method public void startShortcut(java.lang.String, java.lang.String, android.graphics.Rect, android.os.Bundle, android.os.UserHandle);
     method public void startShortcut(android.content.pm.ShortcutInfo, android.graphics.Rect, android.os.Bundle);
     method public void unregisterCallback(android.content.pm.LauncherApps.Callback);
+    field public static final java.lang.String ACTION_CONFIRM_PIN_ITEM = "android.content.pm.action.CONFIRM_PIN_ITEM";
+    field public static final java.lang.String EXTRA_PIN_ITEM_REQUEST = "android.content.pm.extra.PIN_ITEM_REQUEST";
   }
 
   public static abstract class LauncherApps.Callback {
@@ -9724,6 +9748,21 @@
     method public void onShortcutsChanged(java.lang.String, java.util.List<android.content.pm.ShortcutInfo>, android.os.UserHandle);
   }
 
+  public static final class LauncherApps.PinItemRequest implements android.os.Parcelable {
+    method public boolean accept(android.os.Bundle);
+    method public boolean accept();
+    method public int describeContents();
+    method public int getRequestType();
+    method public android.content.pm.ShortcutInfo getShortcutInfo();
+    method public boolean isValid();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.content.pm.LauncherApps.PinItemRequest> CREATOR;
+    field public static final int REQUEST_TYPE_SHORTCUT = 1; // 0x1
+  }
+
+  public static abstract class LauncherApps.PinItemRequest.RequestType implements java.lang.annotation.Annotation {
+  }
+
   public static class LauncherApps.ShortcutQuery {
     ctor public LauncherApps.ShortcutQuery();
     method public android.content.pm.LauncherApps.ShortcutQuery setActivity(android.content.ComponentName);
@@ -10288,9 +10327,11 @@
     method public int getMaxShortcutCountPerActivity();
     method public java.util.List<android.content.pm.ShortcutInfo> getPinnedShortcuts();
     method public boolean isRateLimitingActive();
+    method public boolean isRequestPinShortcutSupported();
     method public void removeAllDynamicShortcuts();
     method public void removeDynamicShortcuts(java.util.List<java.lang.String>);
     method public void reportShortcutUsed(java.lang.String);
+    method public boolean requestPinShortcut(android.content.pm.ShortcutInfo, android.content.IntentSender);
     method public boolean setDynamicShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
     method public boolean updateShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
   }
@@ -11728,6 +11769,8 @@
     method public static android.graphics.Bitmap createBitmap(android.graphics.Bitmap, int, int, int, int, android.graphics.Matrix, boolean);
     method public static android.graphics.Bitmap createBitmap(int, int, android.graphics.Bitmap.Config);
     method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int, int, android.graphics.Bitmap.Config);
+    method public static android.graphics.Bitmap createBitmap(int, int, android.graphics.Bitmap.Config, boolean);
+    method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int, int, android.graphics.Bitmap.Config, boolean);
     method public static android.graphics.Bitmap createBitmap(int[], int, int, int, int, android.graphics.Bitmap.Config);
     method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int[], int, int, int, int, android.graphics.Bitmap.Config);
     method public static android.graphics.Bitmap createBitmap(int[], int, int, android.graphics.Bitmap.Config);
@@ -11792,6 +11835,7 @@
     enum_constant public static final deprecated android.graphics.Bitmap.Config ARGB_4444;
     enum_constant public static final android.graphics.Bitmap.Config ARGB_8888;
     enum_constant public static final android.graphics.Bitmap.Config HARDWARE;
+    enum_constant public static final android.graphics.Bitmap.Config RGBA_F16;
     enum_constant public static final android.graphics.Bitmap.Config RGB_565;
   }
 
@@ -12151,6 +12195,7 @@
     method public android.graphics.Bitmap render();
     method public android.graphics.ColorSpace.Renderer showWhitePoint(boolean);
     method public android.graphics.ColorSpace.Renderer size(int);
+    method public android.graphics.ColorSpace.Renderer uniformChromaticityScale(boolean);
   }
 
   public static class ColorSpace.Rgb extends android.graphics.ColorSpace {
@@ -12686,7 +12731,9 @@
     field public static final deprecated int RGBA_4444 = 7; // 0x7
     field public static final deprecated int RGBA_5551 = 6; // 0x6
     field public static final int RGBA_8888 = 1; // 0x1
+    field public static final int RGBA_F16 = 22; // 0x16
     field public static final int RGBX_8888 = 2; // 0x2
+    field public static final int RGBX_F16 = 23; // 0x17
     field public static final deprecated int RGB_332 = 11; // 0xb
     field public static final int RGB_565 = 4; // 0x4
     field public static final int RGB_888 = 3; // 0x3
@@ -15242,6 +15289,10 @@
     field public static final int CONTROL = 1; // 0x1
     field public static final int CR = 2; // 0x2
     field public static final int EXTEND = 3; // 0x3
+    field public static final int E_BASE = 13; // 0xd
+    field public static final int E_BASE_GAZ = 14; // 0xe
+    field public static final int E_MODIFIER = 15; // 0xf
+    field public static final int GLUE_AFTER_ZWJ = 16; // 0x10
     field public static final int L = 4; // 0x4
     field public static final int LF = 5; // 0x5
     field public static final int LV = 6; // 0x6
@@ -15252,6 +15303,7 @@
     field public static final int SPACING_MARK = 10; // 0xa
     field public static final int T = 8; // 0x8
     field public static final int V = 9; // 0x9
+    field public static final int ZWJ = 17; // 0x11
   }
 
   public static abstract interface UCharacter.HangulSyllableType {
@@ -15264,6 +15316,9 @@
   }
 
   public static abstract interface UCharacter.JoiningGroup {
+    field public static final int AFRICAN_FEH = 86; // 0x56
+    field public static final int AFRICAN_NOON = 87; // 0x57
+    field public static final int AFRICAN_QAF = 88; // 0x58
     field public static final int AIN = 1; // 0x1
     field public static final int ALAPH = 2; // 0x2
     field public static final int ALEF = 3; // 0x3
@@ -15377,6 +15432,8 @@
     field public static final int CONDITIONAL_JAPANESE_STARTER = 37; // 0x25
     field public static final int CONTINGENT_BREAK = 7; // 0x7
     field public static final int EXCLAMATION = 11; // 0xb
+    field public static final int E_BASE = 40; // 0x28
+    field public static final int E_MODIFIER = 41; // 0x29
     field public static final int GLUE = 12; // 0xc
     field public static final int H2 = 31; // 0x1f
     field public static final int H3 = 32; // 0x20
@@ -15403,6 +15460,7 @@
     field public static final int SURROGATE = 25; // 0x19
     field public static final int UNKNOWN = 0; // 0x0
     field public static final int WORD_JOINER = 30; // 0x1e
+    field public static final int ZWJ = 42; // 0x2a
     field public static final int ZWSPACE = 28; // 0x1c
   }
 
@@ -15436,6 +15494,8 @@
     method public int getID();
     method public static android.icu.lang.UCharacter.UnicodeBlock getInstance(int);
     method public static android.icu.lang.UCharacter.UnicodeBlock of(int);
+    field public static final android.icu.lang.UCharacter.UnicodeBlock ADLAM;
+    field public static final int ADLAM_ID = 263; // 0x107
     field public static final android.icu.lang.UCharacter.UnicodeBlock AEGEAN_NUMBERS;
     field public static final int AEGEAN_NUMBERS_ID = 119; // 0x77
     field public static final android.icu.lang.UCharacter.UnicodeBlock AHOM;
@@ -15484,6 +15544,8 @@
     field public static final int BATAK_ID = 199; // 0xc7
     field public static final android.icu.lang.UCharacter.UnicodeBlock BENGALI;
     field public static final int BENGALI_ID = 16; // 0x10
+    field public static final android.icu.lang.UCharacter.UnicodeBlock BHAIKSUKI;
+    field public static final int BHAIKSUKI_ID = 264; // 0x108
     field public static final android.icu.lang.UCharacter.UnicodeBlock BLOCK_ELEMENTS;
     field public static final int BLOCK_ELEMENTS_ID = 53; // 0x35
     field public static final android.icu.lang.UCharacter.UnicodeBlock BOPOMOFO;
@@ -15573,6 +15635,8 @@
     field public static final int CYRILLIC_EXTENDED_A_ID = 158; // 0x9e
     field public static final android.icu.lang.UCharacter.UnicodeBlock CYRILLIC_EXTENDED_B;
     field public static final int CYRILLIC_EXTENDED_B_ID = 160; // 0xa0
+    field public static final android.icu.lang.UCharacter.UnicodeBlock CYRILLIC_EXTENDED_C;
+    field public static final int CYRILLIC_EXTENDED_C_ID = 265; // 0x109
     field public static final int CYRILLIC_ID = 9; // 0x9
     field public static final android.icu.lang.UCharacter.UnicodeBlock CYRILLIC_SUPPLEMENT;
     field public static final android.icu.lang.UCharacter.UnicodeBlock CYRILLIC_SUPPLEMENTARY;
@@ -15626,6 +15690,8 @@
     field public static final int GEORGIAN_SUPPLEMENT_ID = 135; // 0x87
     field public static final android.icu.lang.UCharacter.UnicodeBlock GLAGOLITIC;
     field public static final int GLAGOLITIC_ID = 136; // 0x88
+    field public static final android.icu.lang.UCharacter.UnicodeBlock GLAGOLITIC_SUPPLEMENT;
+    field public static final int GLAGOLITIC_SUPPLEMENT_ID = 266; // 0x10a
     field public static final android.icu.lang.UCharacter.UnicodeBlock GOTHIC;
     field public static final int GOTHIC_ID = 89; // 0x59
     field public static final android.icu.lang.UCharacter.UnicodeBlock GRANTHA;
@@ -15664,6 +15730,8 @@
     field public static final int HIRAGANA_ID = 62; // 0x3e
     field public static final android.icu.lang.UCharacter.UnicodeBlock IDEOGRAPHIC_DESCRIPTION_CHARACTERS;
     field public static final int IDEOGRAPHIC_DESCRIPTION_CHARACTERS_ID = 60; // 0x3c
+    field public static final android.icu.lang.UCharacter.UnicodeBlock IDEOGRAPHIC_SYMBOLS_AND_PUNCTUATION;
+    field public static final int IDEOGRAPHIC_SYMBOLS_AND_PUNCTUATION_ID = 267; // 0x10b
     field public static final android.icu.lang.UCharacter.UnicodeBlock IMPERIAL_ARAMAIC;
     field public static final int IMPERIAL_ARAMAIC_ID = 186; // 0xba
     field public static final android.icu.lang.UCharacter.UnicodeBlock INSCRIPTIONAL_PAHLAVI;
@@ -15748,6 +15816,8 @@
     field public static final int MANDAIC_ID = 198; // 0xc6
     field public static final android.icu.lang.UCharacter.UnicodeBlock MANICHAEAN;
     field public static final int MANICHAEAN_ID = 234; // 0xea
+    field public static final android.icu.lang.UCharacter.UnicodeBlock MARCHEN;
+    field public static final int MARCHEN_ID = 268; // 0x10c
     field public static final android.icu.lang.UCharacter.UnicodeBlock MATHEMATICAL_ALPHANUMERIC_SYMBOLS;
     field public static final int MATHEMATICAL_ALPHANUMERIC_SYMBOLS_ID = 93; // 0x5d
     field public static final android.icu.lang.UCharacter.UnicodeBlock MATHEMATICAL_OPERATORS;
@@ -15782,6 +15852,8 @@
     field public static final int MODI_ID = 236; // 0xec
     field public static final android.icu.lang.UCharacter.UnicodeBlock MONGOLIAN;
     field public static final int MONGOLIAN_ID = 37; // 0x25
+    field public static final android.icu.lang.UCharacter.UnicodeBlock MONGOLIAN_SUPPLEMENT;
+    field public static final int MONGOLIAN_SUPPLEMENT_ID = 269; // 0x10d
     field public static final android.icu.lang.UCharacter.UnicodeBlock MRO;
     field public static final int MRO_ID = 237; // 0xed
     field public static final android.icu.lang.UCharacter.UnicodeBlock MULTANI;
@@ -15796,6 +15868,8 @@
     field public static final int MYANMAR_ID = 28; // 0x1c
     field public static final android.icu.lang.UCharacter.UnicodeBlock NABATAEAN;
     field public static final int NABATAEAN_ID = 239; // 0xef
+    field public static final android.icu.lang.UCharacter.UnicodeBlock NEWA;
+    field public static final int NEWA_ID = 270; // 0x10e
     field public static final android.icu.lang.UCharacter.UnicodeBlock NEW_TAI_LUE;
     field public static final int NEW_TAI_LUE_ID = 139; // 0x8b
     field public static final android.icu.lang.UCharacter.UnicodeBlock NKO;
@@ -15827,6 +15901,8 @@
     field public static final int ORIYA_ID = 19; // 0x13
     field public static final android.icu.lang.UCharacter.UnicodeBlock ORNAMENTAL_DINGBATS;
     field public static final int ORNAMENTAL_DINGBATS_ID = 242; // 0xf2
+    field public static final android.icu.lang.UCharacter.UnicodeBlock OSAGE;
+    field public static final int OSAGE_ID = 271; // 0x10f
     field public static final android.icu.lang.UCharacter.UnicodeBlock OSMANYA;
     field public static final int OSMANYA_ID = 122; // 0x7a
     field public static final android.icu.lang.UCharacter.UnicodeBlock PAHAWH_HMONG;
@@ -15929,6 +16005,10 @@
     field public static final int TAKRI_ID = 220; // 0xdc
     field public static final android.icu.lang.UCharacter.UnicodeBlock TAMIL;
     field public static final int TAMIL_ID = 20; // 0x14
+    field public static final android.icu.lang.UCharacter.UnicodeBlock TANGUT;
+    field public static final android.icu.lang.UCharacter.UnicodeBlock TANGUT_COMPONENTS;
+    field public static final int TANGUT_COMPONENTS_ID = 273; // 0x111
+    field public static final int TANGUT_ID = 272; // 0x110
     field public static final android.icu.lang.UCharacter.UnicodeBlock TELUGU;
     field public static final int TELUGU_ID = 21; // 0x15
     field public static final android.icu.lang.UCharacter.UnicodeBlock THAANA;
@@ -15975,7 +16055,11 @@
     field public static final int DOUBLE_QUOTE = 16; // 0x10
     field public static final int EXTEND = 9; // 0x9
     field public static final int EXTENDNUMLET = 7; // 0x7
+    field public static final int E_BASE = 17; // 0x11
+    field public static final int E_BASE_GAZ = 18; // 0x12
+    field public static final int E_MODIFIER = 19; // 0x13
     field public static final int FORMAT = 2; // 0x2
+    field public static final int GLUE_AFTER_ZWJ = 20; // 0x14
     field public static final int HEBREW_LETTER = 14; // 0xe
     field public static final int KATAKANA = 3; // 0x3
     field public static final int LF = 10; // 0xa
@@ -15987,6 +16071,7 @@
     field public static final int OTHER = 0; // 0x0
     field public static final int REGIONAL_INDICATOR = 13; // 0xd
     field public static final int SINGLE_QUOTE = 15; // 0xf
+    field public static final int ZWJ = 21; // 0x15
   }
 
   public final class UCharacterCategory implements android.icu.lang.UCharacterEnums.ECharacterCategory {
@@ -16205,6 +16290,7 @@
     method public static final boolean hasScript(int, int);
     method public static final boolean isCased(int);
     method public static final boolean isRightToLeft(int);
+    field public static final int ADLAM = 167; // 0xa7
     field public static final int AFAKA = 147; // 0x93
     field public static final int AHOM = 161; // 0xa1
     field public static final int ANATOLIAN_HIEROGLYPHS = 156; // 0x9c
@@ -16216,6 +16302,7 @@
     field public static final int BASSA_VAH = 134; // 0x86
     field public static final int BATAK = 63; // 0x3f
     field public static final int BENGALI = 4; // 0x4
+    field public static final int BHAIKSUKI = 168; // 0xa8
     field public static final int BLISSYMBOLS = 64; // 0x40
     field public static final int BOOK_PAHLAVI = 124; // 0x7c
     field public static final int BOPOMOFO = 5; // 0x5
@@ -16254,6 +16341,7 @@
     field public static final int HAN = 17; // 0x11
     field public static final int HANGUL = 18; // 0x12
     field public static final int HANUNOO = 43; // 0x2b
+    field public static final int HAN_WITH_BOPOMOFO = 172; // 0xac
     field public static final int HARAPPAN_INDUS = 77; // 0x4d
     field public static final int HATRAN = 162; // 0xa2
     field public static final int HEBREW = 19; // 0x13
@@ -16264,6 +16352,7 @@
     field public static final int INSCRIPTIONAL_PAHLAVI = 122; // 0x7a
     field public static final int INSCRIPTIONAL_PARTHIAN = 125; // 0x7d
     field public static final int INVALID_CODE = -1; // 0xffffffff
+    field public static final int JAMO = 173; // 0xad
     field public static final int JAPANESE = 105; // 0x69
     field public static final int JAVANESE = 78; // 0x4e
     field public static final int JURCHEN = 148; // 0x94
@@ -16297,6 +16386,7 @@
     field public static final int MANDAEAN = 84; // 0x54
     field public static final int MANDAIC = 84; // 0x54
     field public static final int MANICHAEAN = 121; // 0x79
+    field public static final int MARCHEN = 169; // 0xa9
     field public static final int MATHEMATICAL_NOTATION = 128; // 0x80
     field public static final int MAYAN_HIEROGLYPHS = 85; // 0x55
     field public static final int MEITEI_MAYEK = 115; // 0x73
@@ -16313,6 +16403,7 @@
     field public static final int MYANMAR = 28; // 0x1c
     field public static final int NABATAEAN = 143; // 0x8f
     field public static final int NAKHI_GEBA = 132; // 0x84
+    field public static final int NEWA = 170; // 0xaa
     field public static final int NEW_TAI_LUE = 59; // 0x3b
     field public static final int NKO = 87; // 0x57
     field public static final int NUSHU = 150; // 0x96
@@ -16327,6 +16418,7 @@
     field public static final int OL_CHIKI = 109; // 0x6d
     field public static final int ORIYA = 31; // 0x1f
     field public static final int ORKHON = 88; // 0x58
+    field public static final int OSAGE = 171; // 0xab
     field public static final int OSMANYA = 50; // 0x32
     field public static final int PAHAWH_HMONG = 75; // 0x4b
     field public static final int PALMYRENE = 144; // 0x90
@@ -16352,6 +16444,7 @@
     field public static final int SUNDANESE = 113; // 0x71
     field public static final int SYLOTI_NAGRI = 58; // 0x3a
     field public static final int SYMBOLS = 129; // 0x81
+    field public static final int SYMBOLS_EMOJI = 174; // 0xae
     field public static final int SYRIAC = 34; // 0x22
     field public static final int TAGALOG = 42; // 0x2a
     field public static final int TAGBANWA = 45; // 0x2d
@@ -16878,6 +16971,8 @@
     method public static final android.icu.text.DateFormat.BooleanAttribute[] values();
     enum_constant public static final android.icu.text.DateFormat.BooleanAttribute PARSE_ALLOW_NUMERIC;
     enum_constant public static final android.icu.text.DateFormat.BooleanAttribute PARSE_ALLOW_WHITESPACE;
+    enum_constant public static final android.icu.text.DateFormat.BooleanAttribute PARSE_MULTIPLE_PATTERNS_FOR_MATCH;
+    enum_constant public static final android.icu.text.DateFormat.BooleanAttribute PARSE_PARTIAL_LITERAL_MATCH;
   }
 
   public static class DateFormat.Field extends java.text.Format.Field {
@@ -17562,6 +17657,7 @@
     field public static final int PERCENTSTYLE = 2; // 0x2
     field public static final int PLURALCURRENCYSTYLE = 6; // 0x6
     field public static final int SCIENTIFICSTYLE = 3; // 0x3
+    field public static final int STANDARDCURRENCYSTYLE = 9; // 0x9
   }
 
   public static class NumberFormat.Field extends java.text.Format.Field {
@@ -18356,7 +18452,7 @@
     field public static final int AM_PM = 9; // 0x9
     field public static final int APRIL = 3; // 0x3
     field public static final int AUGUST = 7; // 0x7
-    field protected static final int BASE_FIELD_COUNT = 23; // 0x17
+    field protected static final deprecated int BASE_FIELD_COUNT = 23; // 0x17
     field public static final int DATE = 5; // 0x5
     field public static final int DAY_OF_MONTH = 5; // 0x5
     field public static final int DAY_OF_WEEK = 7; // 0x7
@@ -18384,7 +18480,7 @@
     field public static final int MARCH = 2; // 0x2
     field protected static final int MAXIMUM = 3; // 0x3
     field protected static final java.util.Date MAX_DATE;
-    field protected static final int MAX_FIELD_COUNT = 32; // 0x20
+    field protected static final deprecated int MAX_FIELD_COUNT = 32; // 0x20
     field protected static final int MAX_JULIAN = 2130706432; // 0x7f000000
     field protected static final long MAX_MILLIS = 183882168921600000L; // 0x28d47dbbf19b000L
     field public static final int MAY = 4; // 0x4
@@ -18704,6 +18800,7 @@
     field public static final android.icu.util.MeasureUnit CELSIUS;
     field public static final android.icu.util.MeasureUnit CENTILITER;
     field public static final android.icu.util.MeasureUnit CENTIMETER;
+    field public static final android.icu.util.MeasureUnit CENTURY;
     field public static final android.icu.util.MeasureUnit CUBIC_CENTIMETER;
     field public static final android.icu.util.MeasureUnit CUBIC_FOOT;
     field public static final android.icu.util.MeasureUnit CUBIC_INCH;
@@ -18712,6 +18809,7 @@
     field public static final android.icu.util.MeasureUnit CUBIC_MILE;
     field public static final android.icu.util.MeasureUnit CUBIC_YARD;
     field public static final android.icu.util.MeasureUnit CUP;
+    field public static final android.icu.util.MeasureUnit CUP_METRIC;
     field public static final android.icu.util.TimeUnit DAY;
     field public static final android.icu.util.MeasureUnit DECILITER;
     field public static final android.icu.util.MeasureUnit DECIMETER;
@@ -18723,6 +18821,7 @@
     field public static final android.icu.util.MeasureUnit FOOT;
     field public static final android.icu.util.MeasureUnit FURLONG;
     field public static final android.icu.util.MeasureUnit GALLON;
+    field public static final android.icu.util.MeasureUnit GENERIC_TEMPERATURE;
     field public static final android.icu.util.MeasureUnit GIGABIT;
     field public static final android.icu.util.MeasureUnit GIGABYTE;
     field public static final android.icu.util.MeasureUnit GIGAHERTZ;
@@ -18750,8 +18849,10 @@
     field public static final android.icu.util.MeasureUnit KILOMETER_PER_HOUR;
     field public static final android.icu.util.MeasureUnit KILOWATT;
     field public static final android.icu.util.MeasureUnit KILOWATT_HOUR;
+    field public static final android.icu.util.MeasureUnit KNOT;
     field public static final android.icu.util.MeasureUnit LIGHT_YEAR;
     field public static final android.icu.util.MeasureUnit LITER;
+    field public static final android.icu.util.MeasureUnit LITER_PER_100KILOMETERS;
     field public static final android.icu.util.MeasureUnit LITER_PER_KILOMETER;
     field public static final android.icu.util.MeasureUnit LUX;
     field public static final android.icu.util.MeasureUnit MEGABIT;
@@ -18769,6 +18870,7 @@
     field public static final android.icu.util.MeasureUnit MILE;
     field public static final android.icu.util.MeasureUnit MILE_PER_GALLON;
     field public static final android.icu.util.MeasureUnit MILE_PER_HOUR;
+    field public static final android.icu.util.MeasureUnit MILE_SCANDINAVIAN;
     field public static final android.icu.util.MeasureUnit MILLIAMPERE;
     field public static final android.icu.util.MeasureUnit MILLIBAR;
     field public static final android.icu.util.MeasureUnit MILLIGRAM;
@@ -18788,10 +18890,12 @@
     field public static final android.icu.util.MeasureUnit PARSEC;
     field public static final android.icu.util.MeasureUnit PICOMETER;
     field public static final android.icu.util.MeasureUnit PINT;
+    field public static final android.icu.util.MeasureUnit PINT_METRIC;
     field public static final android.icu.util.MeasureUnit POUND;
     field public static final android.icu.util.MeasureUnit POUND_PER_SQUARE_INCH;
     field public static final android.icu.util.MeasureUnit QUART;
     field public static final android.icu.util.MeasureUnit RADIAN;
+    field public static final android.icu.util.MeasureUnit REVOLUTION_ANGLE;
     field public static final android.icu.util.TimeUnit SECOND;
     field public static final android.icu.util.MeasureUnit SQUARE_CENTIMETER;
     field public static final android.icu.util.MeasureUnit SQUARE_FOOT;
@@ -19103,6 +19207,7 @@
     field public static final android.icu.util.VersionInfo UNICODE_6_3;
     field public static final android.icu.util.VersionInfo UNICODE_7_0;
     field public static final android.icu.util.VersionInfo UNICODE_8_0;
+    field public static final android.icu.util.VersionInfo UNICODE_9_0;
   }
 
 }
@@ -22609,6 +22714,7 @@
     method public android.content.ComponentName getServiceComponent();
     method public android.media.session.MediaSession.Token getSessionToken();
     method public boolean isConnected();
+    method public void search(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SearchCallback);
     method public void subscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback);
     method public void subscribe(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SubscriptionCallback);
     method public void unsubscribe(java.lang.String);
@@ -22644,6 +22750,12 @@
     field public static final int FLAG_PLAYABLE = 2; // 0x2
   }
 
+  public static abstract class MediaBrowser.SearchCallback {
+    ctor public MediaBrowser.SearchCallback();
+    method public void onError(java.lang.String, android.os.Bundle);
+    method public void onSearchResult(java.lang.String, android.os.Bundle, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
+  }
+
   public static abstract class MediaBrowser.SubscriptionCallback {
     ctor public MediaBrowser.SubscriptionCallback();
     method public void onChildrenLoaded(java.lang.String, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
@@ -29714,6 +29826,7 @@
     method public deprecated void setUserRestrictions(android.os.Bundle, android.os.UserHandle);
     method public static boolean supportsMultipleUsers();
     field public static final java.lang.String ALLOW_PARENT_PROFILE_APP_LINKING = "allow_parent_profile_app_linking";
+    field public static final java.lang.String DISALLOW_ADD_MANAGED_PROFILE = "no_add_managed_profile";
     field public static final java.lang.String DISALLOW_ADD_USER = "no_add_user";
     field public static final java.lang.String DISALLOW_ADJUST_VOLUME = "no_adjust_volume";
     field public static final java.lang.String DISALLOW_APPS_CONTROL = "no_control_apps";
@@ -29738,6 +29851,7 @@
     field public static final java.lang.String DISALLOW_NETWORK_RESET = "no_network_reset";
     field public static final java.lang.String DISALLOW_OUTGOING_BEAM = "no_outgoing_beam";
     field public static final java.lang.String DISALLOW_OUTGOING_CALLS = "no_outgoing_calls";
+    field public static final java.lang.String DISALLOW_REMOVE_MANAGED_PROFILE = "no_remove_managed_profile";
     field public static final java.lang.String DISALLOW_REMOVE_USER = "no_remove_user";
     field public static final java.lang.String DISALLOW_SAFE_BOOT = "no_safe_boot";
     field public static final java.lang.String DISALLOW_SET_USER_ICON = "no_set_user_icon";
@@ -35075,6 +35189,7 @@
     method public abstract void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>);
     method public void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>, android.os.Bundle);
     method public void onLoadItem(java.lang.String, android.service.media.MediaBrowserService.Result<android.media.browse.MediaBrowser.MediaItem>);
+    method public void onSearch(java.lang.String, android.os.Bundle, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>);
     method public void setSessionToken(android.media.session.MediaSession.Token);
     field public static final java.lang.String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
   }
@@ -37368,6 +37483,7 @@
     field public static final java.lang.String KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL = "show_onscreen_dial_button_bool";
     field public static final java.lang.String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
     field public static final java.lang.String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
+    field public static final java.lang.String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = "support_3gpp_call_forwarding_while_roaming_bool";
     field public static final java.lang.String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
     field public static final java.lang.String KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL = "support_pause_ims_video_calls_bool";
     field public static final java.lang.String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool";
@@ -37905,6 +38021,7 @@
     method public java.lang.String getSimOperatorName();
     method public java.lang.String getSimSerialNumber();
     method public int getSimState();
+    method public int getSimState(int);
     method public java.lang.String getSubscriberId();
     method public java.lang.String getVoiceMailAlphaTag();
     method public java.lang.String getVoiceMailNumber();
@@ -37989,7 +38106,11 @@
     field public static final int PHONE_TYPE_NONE = 0; // 0x0
     field public static final int PHONE_TYPE_SIP = 3; // 0x3
     field public static final int SIM_STATE_ABSENT = 1; // 0x1
+    field public static final int SIM_STATE_CARD_IO_ERROR = 8; // 0x8
+    field public static final int SIM_STATE_CARD_RESTRICTED = 9; // 0x9
     field public static final int SIM_STATE_NETWORK_LOCKED = 4; // 0x4
+    field public static final int SIM_STATE_NOT_READY = 6; // 0x6
+    field public static final int SIM_STATE_PERM_DISABLED = 7; // 0x7
     field public static final int SIM_STATE_PIN_REQUIRED = 2; // 0x2
     field public static final int SIM_STATE_PUK_REQUIRED = 3; // 0x3
     field public static final int SIM_STATE_READY = 5; // 0x5
@@ -38754,7 +38875,7 @@
     method public java.lang.CharSequence subSequence(int, int);
   }
 
-  public class AndroidCharacter {
+  public deprecated class AndroidCharacter {
     ctor public AndroidCharacter();
     method public static void getDirectionalities(char[], byte[], int);
     method public static int getEastAsianWidth(char);
@@ -41617,6 +41738,7 @@
     method public android.view.View findNearestTouchable(android.view.ViewGroup, int, int, int, int[]);
     method public final android.view.View findNextFocus(android.view.ViewGroup, android.view.View, int);
     method public android.view.View findNextFocusFromRect(android.view.ViewGroup, android.graphics.Rect, int);
+    method public android.view.View findNextKeyboardNavigationCluster(android.view.ViewGroup, android.view.View, int);
     method public static android.view.FocusFinder getInstance();
   }
 
@@ -42909,6 +43031,7 @@
     method public void addChildrenForAccessibility(java.util.ArrayList<android.view.View>);
     method public void addFocusables(java.util.ArrayList<android.view.View>, int);
     method public void addFocusables(java.util.ArrayList<android.view.View>, int, int);
+    method public void addKeyboardNavigationClusters(java.util.Collection<android.view.View>, int);
     method public void addOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener);
     method public void addOnLayoutChangeListener(android.view.View.OnLayoutChangeListener);
     method public void addTouchables(java.util.ArrayList<android.view.View>);
@@ -43196,6 +43319,7 @@
     method public boolean isVerticalFadingEdgeEnabled();
     method public boolean isVerticalScrollBarEnabled();
     method public void jumpDrawablesToCurrentState();
+    method public android.view.View keyboardNavigationClusterSearch(int);
     method public void layout(int, int, int, int);
     method public final void measure(int, int);
     method protected static int[] mergeDrawableStates(int[], int[]);
@@ -43843,6 +43967,7 @@
     method protected deprecated boolean isChildrenDrawnWithCacheEnabled();
     method public boolean isMotionEventSplittingEnabled();
     method public boolean isTransitionGroup();
+    method public android.view.View keyboardNavigationClusterSearch(android.view.View, int);
     method public final void layout(int, int, int, int);
     method protected void measureChild(android.view.View, int, int);
     method protected void measureChildWithMargins(android.view.View, int, int, int, int);
@@ -44005,6 +44130,7 @@
     method public abstract boolean isLayoutRequested();
     method public abstract boolean isTextAlignmentResolved();
     method public abstract boolean isTextDirectionResolved();
+    method public abstract android.view.View keyboardNavigationClusterSearch(android.view.View, int);
     method public abstract void notifySubtreeAccessibilityStateChanged(android.view.View, android.view.View, int);
     method public abstract boolean onNestedFling(android.view.View, float, float, boolean);
     method public abstract boolean onNestedPreFling(android.view.View, float, float);
@@ -61301,31 +61427,31 @@
     ctor public CopyOnWriteArrayList();
     ctor public CopyOnWriteArrayList(java.util.Collection<? extends E>);
     ctor public CopyOnWriteArrayList(E[]);
-    method public synchronized boolean add(E);
-    method public synchronized void add(int, E);
-    method public synchronized boolean addAll(java.util.Collection<? extends E>);
-    method public synchronized boolean addAll(int, java.util.Collection<? extends E>);
-    method public synchronized int addAllAbsent(java.util.Collection<? extends E>);
-    method public synchronized boolean addIfAbsent(E);
-    method public synchronized void clear();
+    method public boolean add(E);
+    method public void add(int, E);
+    method public boolean addAll(java.util.Collection<? extends E>);
+    method public boolean addAll(int, java.util.Collection<? extends E>);
+    method public int addAllAbsent(java.util.Collection<? extends E>);
+    method public boolean addIfAbsent(E);
+    method public void clear();
     method public java.lang.Object clone();
     method public boolean contains(java.lang.Object);
     method public boolean containsAll(java.util.Collection<?>);
     method public void forEach(java.util.function.Consumer<? super E>);
     method public E get(int);
-    method public int indexOf(E, int);
     method public int indexOf(java.lang.Object);
+    method public int indexOf(E, int);
     method public boolean isEmpty();
     method public java.util.Iterator<E> iterator();
-    method public int lastIndexOf(E, int);
     method public int lastIndexOf(java.lang.Object);
-    method public java.util.ListIterator<E> listIterator(int);
+    method public int lastIndexOf(E, int);
     method public java.util.ListIterator<E> listIterator();
-    method public synchronized E remove(int);
-    method public synchronized boolean remove(java.lang.Object);
-    method public synchronized boolean removeAll(java.util.Collection<?>);
-    method public synchronized boolean retainAll(java.util.Collection<?>);
-    method public synchronized E set(int, E);
+    method public java.util.ListIterator<E> listIterator(int);
+    method public E remove(int);
+    method public boolean remove(java.lang.Object);
+    method public boolean removeAll(java.util.Collection<?>);
+    method public boolean retainAll(java.util.Collection<?>);
+    method public E set(int, E);
     method public int size();
     method public java.util.List<E> subList(int, int);
     method public java.lang.Object[] toArray();
diff --git a/api/system-current.txt b/api/system-current.txt
index 6d45b6c..870070b 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3776,6 +3776,7 @@
     method public void setIntent(android.content.Intent);
     method public final void setMediaController(android.media.session.MediaController);
     method public void setOverlayWithDecorCaptionEnabled(boolean);
+    method public void setPictureInPictureActions(java.util.List<android.app.RemoteAction>);
     method public void setPictureInPictureAspectRatio(float);
     method public final deprecated void setProgress(int);
     method public final deprecated void setProgressBarIndeterminate(boolean);
@@ -3862,6 +3863,7 @@
     method public int getLauncherLargeIconDensity();
     method public int getLauncherLargeIconSize();
     method public int getLockTaskModeState();
+    method public static int getMaxNumPictureInPictureActions();
     method public int getMemoryClass();
     method public void getMemoryInfo(android.app.ActivityManager.MemoryInfo);
     method public static void getMyMemoryState(android.app.ActivityManager.RunningAppProcessInfo);
@@ -5145,6 +5147,7 @@
     method public java.lang.String getChannel();
     method public java.lang.String getGroup();
     method public android.graphics.drawable.Icon getLargeIcon();
+    method public static java.lang.Class<? extends android.app.Notification.Style> getNotificationStyleClass(java.lang.String);
     method public android.graphics.drawable.Icon getSmallIcon();
     method public java.lang.String getSortKey();
     method public void writeToParcel(android.os.Parcel, int);
@@ -5714,6 +5717,22 @@
     field public static final int STYLE_SPINNER = 0; // 0x0
   }
 
+  public final class RemoteAction implements android.os.Parcelable {
+    ctor public RemoteAction(android.graphics.drawable.Icon, java.lang.CharSequence, java.lang.CharSequence, android.app.RemoteAction.OnActionListener);
+    method public android.app.RemoteAction clone();
+    method public int describeContents();
+    method public void dump(java.lang.String, java.io.PrintWriter);
+    method public java.lang.CharSequence getContentDescription();
+    method public android.graphics.drawable.Icon getIcon();
+    method public java.lang.CharSequence getTitle();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.RemoteAction> CREATOR;
+  }
+
+  public static abstract interface RemoteAction.OnActionListener {
+    method public abstract void onAction(android.app.RemoteAction);
+  }
+
   public final class RemoteInput implements android.os.Parcelable {
     method public static void addResultsToIntent(android.app.RemoteInput[], android.content.Intent, android.os.Bundle);
     method public int describeContents();
@@ -6398,6 +6417,9 @@
     field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION";
     field public static final deprecated java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME";
     field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM = "android.app.extra.PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM";
+    field public static final java.lang.String EXTRA_PROVISIONING_DISCLAIMERS = "android.app.extra.PROVISIONING_DISCLAIMERS";
+    field public static final java.lang.String EXTRA_PROVISIONING_DISCLAIMER_CONTENT = "android.app.extra.PROVISIONING_DISCLAIMER_CONTENT";
+    field public static final java.lang.String EXTRA_PROVISIONING_DISCLAIMER_HEADER = "android.app.extra.PROVISIONING_DISCLAIMER_HEADER";
     field public static final java.lang.String EXTRA_PROVISIONING_EMAIL_ADDRESS = "android.app.extra.PROVISIONING_EMAIL_ADDRESS";
     field public static final java.lang.String EXTRA_PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION = "android.app.extra.PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION";
     field public static final java.lang.String EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED = "android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED";
@@ -9096,6 +9118,7 @@
     field public static final java.lang.String ACTION_EDIT = "android.intent.action.EDIT";
     field public static final java.lang.String ACTION_EXTERNAL_APPLICATIONS_AVAILABLE = "android.intent.action.EXTERNAL_APPLICATIONS_AVAILABLE";
     field public static final java.lang.String ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE = "android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE";
+    field public static final java.lang.String ACTION_FACTORY_RESET = "android.intent.action.FACTORY_RESET";
     field public static final java.lang.String ACTION_FACTORY_TEST = "android.intent.action.FACTORY_TEST";
     field public static final java.lang.String ACTION_GET_CONTENT = "android.intent.action.GET_CONTENT";
     field public static final java.lang.String ACTION_GET_RESTRICTION_ENTRIES = "android.intent.action.GET_RESTRICTION_ENTRIES";
@@ -9259,6 +9282,7 @@
     field public static final java.lang.String EXTRA_DONT_KILL_APP = "android.intent.extra.DONT_KILL_APP";
     field public static final java.lang.String EXTRA_EMAIL = "android.intent.extra.EMAIL";
     field public static final java.lang.String EXTRA_EXCLUDE_COMPONENTS = "android.intent.extra.EXCLUDE_COMPONENTS";
+    field public static final java.lang.String EXTRA_FORCE_FACTORY_RESET = "android.intent.extra.FORCE_FACTORY_RESET";
     field public static final java.lang.String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
     field public static final java.lang.String EXTRA_INDEX = "android.intent.extra.INDEX";
     field public static final java.lang.String EXTRA_INITIAL_INTENTS = "android.intent.extra.INITIAL_INTENTS";
@@ -10098,6 +10122,7 @@
 
   public class LauncherApps {
     method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
+    method public android.content.pm.LauncherApps.PinItemRequest getPinItemRequest(android.content.Intent);
     method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(android.content.pm.ShortcutInfo, int);
     method public android.graphics.drawable.Drawable getShortcutIconDrawable(android.content.pm.ShortcutInfo, int);
     method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
@@ -10113,6 +10138,8 @@
     method public void startShortcut(java.lang.String, java.lang.String, android.graphics.Rect, android.os.Bundle, android.os.UserHandle);
     method public void startShortcut(android.content.pm.ShortcutInfo, android.graphics.Rect, android.os.Bundle);
     method public void unregisterCallback(android.content.pm.LauncherApps.Callback);
+    field public static final java.lang.String ACTION_CONFIRM_PIN_ITEM = "android.content.pm.action.CONFIRM_PIN_ITEM";
+    field public static final java.lang.String EXTRA_PIN_ITEM_REQUEST = "android.content.pm.extra.PIN_ITEM_REQUEST";
   }
 
   public static abstract class LauncherApps.Callback {
@@ -10127,6 +10154,21 @@
     method public void onShortcutsChanged(java.lang.String, java.util.List<android.content.pm.ShortcutInfo>, android.os.UserHandle);
   }
 
+  public static final class LauncherApps.PinItemRequest implements android.os.Parcelable {
+    method public boolean accept(android.os.Bundle);
+    method public boolean accept();
+    method public int describeContents();
+    method public int getRequestType();
+    method public android.content.pm.ShortcutInfo getShortcutInfo();
+    method public boolean isValid();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.content.pm.LauncherApps.PinItemRequest> CREATOR;
+    field public static final int REQUEST_TYPE_SHORTCUT = 1; // 0x1
+  }
+
+  public static abstract class LauncherApps.PinItemRequest.RequestType implements java.lang.annotation.Annotation {
+  }
+
   public static class LauncherApps.ShortcutQuery {
     ctor public LauncherApps.ShortcutQuery();
     method public android.content.pm.LauncherApps.ShortcutQuery setActivity(android.content.ComponentName);
@@ -10764,9 +10806,11 @@
     method public int getMaxShortcutCountPerActivity();
     method public java.util.List<android.content.pm.ShortcutInfo> getPinnedShortcuts();
     method public boolean isRateLimitingActive();
+    method public boolean isRequestPinShortcutSupported();
     method public void removeAllDynamicShortcuts();
     method public void removeDynamicShortcuts(java.util.List<java.lang.String>);
     method public void reportShortcutUsed(java.lang.String);
+    method public boolean requestPinShortcut(android.content.pm.ShortcutInfo, android.content.IntentSender);
     method public boolean setDynamicShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
     method public boolean updateShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
   }
@@ -12218,6 +12262,8 @@
     method public static android.graphics.Bitmap createBitmap(android.graphics.Bitmap, int, int, int, int, android.graphics.Matrix, boolean);
     method public static android.graphics.Bitmap createBitmap(int, int, android.graphics.Bitmap.Config);
     method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int, int, android.graphics.Bitmap.Config);
+    method public static android.graphics.Bitmap createBitmap(int, int, android.graphics.Bitmap.Config, boolean);
+    method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int, int, android.graphics.Bitmap.Config, boolean);
     method public static android.graphics.Bitmap createBitmap(int[], int, int, int, int, android.graphics.Bitmap.Config);
     method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int[], int, int, int, int, android.graphics.Bitmap.Config);
     method public static android.graphics.Bitmap createBitmap(int[], int, int, android.graphics.Bitmap.Config);
@@ -12282,6 +12328,7 @@
     enum_constant public static final deprecated android.graphics.Bitmap.Config ARGB_4444;
     enum_constant public static final android.graphics.Bitmap.Config ARGB_8888;
     enum_constant public static final android.graphics.Bitmap.Config HARDWARE;
+    enum_constant public static final android.graphics.Bitmap.Config RGBA_F16;
     enum_constant public static final android.graphics.Bitmap.Config RGB_565;
   }
 
@@ -12641,6 +12688,7 @@
     method public android.graphics.Bitmap render();
     method public android.graphics.ColorSpace.Renderer showWhitePoint(boolean);
     method public android.graphics.ColorSpace.Renderer size(int);
+    method public android.graphics.ColorSpace.Renderer uniformChromaticityScale(boolean);
   }
 
   public static class ColorSpace.Rgb extends android.graphics.ColorSpace {
@@ -13176,7 +13224,9 @@
     field public static final deprecated int RGBA_4444 = 7; // 0x7
     field public static final deprecated int RGBA_5551 = 6; // 0x6
     field public static final int RGBA_8888 = 1; // 0x1
+    field public static final int RGBA_F16 = 22; // 0x16
     field public static final int RGBX_8888 = 2; // 0x2
+    field public static final int RGBX_F16 = 23; // 0x17
     field public static final deprecated int RGB_332 = 11; // 0xb
     field public static final int RGB_565 = 4; // 0x4
     field public static final int RGB_888 = 3; // 0x3
@@ -15773,7 +15823,6 @@
   }
 
   public final class GeofenceHardware {
-    ctor public GeofenceHardware(android.hardware.location.IGeofenceHardware);
     method public boolean addGeofence(int, int, android.hardware.location.GeofenceHardwareRequest, android.hardware.location.GeofenceHardwareCallback);
     method public int[] getMonitoringTypes();
     method public int getStatusOfMonitoringType(int);
@@ -15848,46 +15897,6 @@
     method public void setUnknownTimer(int);
   }
 
-  public final class GeofenceHardwareRequestParcelable implements android.os.Parcelable {
-    ctor public GeofenceHardwareRequestParcelable(int, android.hardware.location.GeofenceHardwareRequest);
-    method public int describeContents();
-    method public int getId();
-    method public int getLastTransition();
-    method public double getLatitude();
-    method public double getLongitude();
-    method public int getMonitorTransitions();
-    method public int getNotificationResponsiveness();
-    method public double getRadius();
-    method public int getUnknownTimer();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.hardware.location.GeofenceHardwareRequestParcelable> CREATOR;
-  }
-
-  public abstract interface IGeofenceHardware implements android.os.IInterface {
-    method public abstract boolean addCircularFence(int, android.hardware.location.GeofenceHardwareRequestParcelable, android.hardware.location.IGeofenceHardwareCallback) throws android.os.RemoteException;
-    method public abstract int[] getMonitoringTypes() throws android.os.RemoteException;
-    method public abstract int getStatusOfMonitoringType(int) throws android.os.RemoteException;
-    method public abstract boolean pauseGeofence(int, int) throws android.os.RemoteException;
-    method public abstract boolean registerForMonitorStateChangeCallback(int, android.hardware.location.IGeofenceHardwareMonitorCallback) throws android.os.RemoteException;
-    method public abstract boolean removeGeofence(int, int) throws android.os.RemoteException;
-    method public abstract boolean resumeGeofence(int, int, int) throws android.os.RemoteException;
-    method public abstract void setFusedGeofenceHardware(android.location.IFusedGeofenceHardware) throws android.os.RemoteException;
-    method public abstract void setGpsGeofenceHardware(android.location.IGpsGeofenceHardware) throws android.os.RemoteException;
-    method public abstract boolean unregisterForMonitorStateChangeCallback(int, android.hardware.location.IGeofenceHardwareMonitorCallback) throws android.os.RemoteException;
-  }
-
-  public abstract interface IGeofenceHardwareCallback implements android.os.IInterface {
-    method public abstract void onGeofenceAdd(int, int) throws android.os.RemoteException;
-    method public abstract void onGeofencePause(int, int) throws android.os.RemoteException;
-    method public abstract void onGeofenceRemove(int, int) throws android.os.RemoteException;
-    method public abstract void onGeofenceResume(int, int) throws android.os.RemoteException;
-    method public abstract void onGeofenceTransition(int, int, android.location.Location, long, int) throws android.os.RemoteException;
-  }
-
-  public abstract interface IGeofenceHardwareMonitorCallback implements android.os.IInterface {
-    method public abstract void onMonitoringSystemChange(android.hardware.location.GeofenceHardwareMonitorEvent) throws android.os.RemoteException;
-  }
-
   public class MemoryRegion implements android.os.Parcelable {
     ctor public MemoryRegion(android.os.Parcel);
     method public int describeContents();
@@ -16495,6 +16504,10 @@
     field public static final int CONTROL = 1; // 0x1
     field public static final int CR = 2; // 0x2
     field public static final int EXTEND = 3; // 0x3
+    field public static final int E_BASE = 13; // 0xd
+    field public static final int E_BASE_GAZ = 14; // 0xe
+    field public static final int E_MODIFIER = 15; // 0xf
+    field public static final int GLUE_AFTER_ZWJ = 16; // 0x10
     field public static final int L = 4; // 0x4
     field public static final int LF = 5; // 0x5
     field public static final int LV = 6; // 0x6
@@ -16505,6 +16518,7 @@
     field public static final int SPACING_MARK = 10; // 0xa
     field public static final int T = 8; // 0x8
     field public static final int V = 9; // 0x9
+    field public static final int ZWJ = 17; // 0x11
   }
 
   public static abstract interface UCharacter.HangulSyllableType {
@@ -16517,6 +16531,9 @@
   }
 
   public static abstract interface UCharacter.JoiningGroup {
+    field public static final int AFRICAN_FEH = 86; // 0x56
+    field public static final int AFRICAN_NOON = 87; // 0x57
+    field public static final int AFRICAN_QAF = 88; // 0x58
     field public static final int AIN = 1; // 0x1
     field public static final int ALAPH = 2; // 0x2
     field public static final int ALEF = 3; // 0x3
@@ -16630,6 +16647,8 @@
     field public static final int CONDITIONAL_JAPANESE_STARTER = 37; // 0x25
     field public static final int CONTINGENT_BREAK = 7; // 0x7
     field public static final int EXCLAMATION = 11; // 0xb
+    field public static final int E_BASE = 40; // 0x28
+    field public static final int E_MODIFIER = 41; // 0x29
     field public static final int GLUE = 12; // 0xc
     field public static final int H2 = 31; // 0x1f
     field public static final int H3 = 32; // 0x20
@@ -16656,6 +16675,7 @@
     field public static final int SURROGATE = 25; // 0x19
     field public static final int UNKNOWN = 0; // 0x0
     field public static final int WORD_JOINER = 30; // 0x1e
+    field public static final int ZWJ = 42; // 0x2a
     field public static final int ZWSPACE = 28; // 0x1c
   }
 
@@ -16689,6 +16709,8 @@
     method public int getID();
     method public static android.icu.lang.UCharacter.UnicodeBlock getInstance(int);
     method public static android.icu.lang.UCharacter.UnicodeBlock of(int);
+    field public static final android.icu.lang.UCharacter.UnicodeBlock ADLAM;
+    field public static final int ADLAM_ID = 263; // 0x107
     field public static final android.icu.lang.UCharacter.UnicodeBlock AEGEAN_NUMBERS;
     field public static final int AEGEAN_NUMBERS_ID = 119; // 0x77
     field public static final android.icu.lang.UCharacter.UnicodeBlock AHOM;
@@ -16737,6 +16759,8 @@
     field public static final int BATAK_ID = 199; // 0xc7
     field public static final android.icu.lang.UCharacter.UnicodeBlock BENGALI;
     field public static final int BENGALI_ID = 16; // 0x10
+    field public static final android.icu.lang.UCharacter.UnicodeBlock BHAIKSUKI;
+    field public static final int BHAIKSUKI_ID = 264; // 0x108
     field public static final android.icu.lang.UCharacter.UnicodeBlock BLOCK_ELEMENTS;
     field public static final int BLOCK_ELEMENTS_ID = 53; // 0x35
     field public static final android.icu.lang.UCharacter.UnicodeBlock BOPOMOFO;
@@ -16826,6 +16850,8 @@
     field public static final int CYRILLIC_EXTENDED_A_ID = 158; // 0x9e
     field public static final android.icu.lang.UCharacter.UnicodeBlock CYRILLIC_EXTENDED_B;
     field public static final int CYRILLIC_EXTENDED_B_ID = 160; // 0xa0
+    field public static final android.icu.lang.UCharacter.UnicodeBlock CYRILLIC_EXTENDED_C;
+    field public static final int CYRILLIC_EXTENDED_C_ID = 265; // 0x109
     field public static final int CYRILLIC_ID = 9; // 0x9
     field public static final android.icu.lang.UCharacter.UnicodeBlock CYRILLIC_SUPPLEMENT;
     field public static final android.icu.lang.UCharacter.UnicodeBlock CYRILLIC_SUPPLEMENTARY;
@@ -16879,6 +16905,8 @@
     field public static final int GEORGIAN_SUPPLEMENT_ID = 135; // 0x87
     field public static final android.icu.lang.UCharacter.UnicodeBlock GLAGOLITIC;
     field public static final int GLAGOLITIC_ID = 136; // 0x88
+    field public static final android.icu.lang.UCharacter.UnicodeBlock GLAGOLITIC_SUPPLEMENT;
+    field public static final int GLAGOLITIC_SUPPLEMENT_ID = 266; // 0x10a
     field public static final android.icu.lang.UCharacter.UnicodeBlock GOTHIC;
     field public static final int GOTHIC_ID = 89; // 0x59
     field public static final android.icu.lang.UCharacter.UnicodeBlock GRANTHA;
@@ -16917,6 +16945,8 @@
     field public static final int HIRAGANA_ID = 62; // 0x3e
     field public static final android.icu.lang.UCharacter.UnicodeBlock IDEOGRAPHIC_DESCRIPTION_CHARACTERS;
     field public static final int IDEOGRAPHIC_DESCRIPTION_CHARACTERS_ID = 60; // 0x3c
+    field public static final android.icu.lang.UCharacter.UnicodeBlock IDEOGRAPHIC_SYMBOLS_AND_PUNCTUATION;
+    field public static final int IDEOGRAPHIC_SYMBOLS_AND_PUNCTUATION_ID = 267; // 0x10b
     field public static final android.icu.lang.UCharacter.UnicodeBlock IMPERIAL_ARAMAIC;
     field public static final int IMPERIAL_ARAMAIC_ID = 186; // 0xba
     field public static final android.icu.lang.UCharacter.UnicodeBlock INSCRIPTIONAL_PAHLAVI;
@@ -17001,6 +17031,8 @@
     field public static final int MANDAIC_ID = 198; // 0xc6
     field public static final android.icu.lang.UCharacter.UnicodeBlock MANICHAEAN;
     field public static final int MANICHAEAN_ID = 234; // 0xea
+    field public static final android.icu.lang.UCharacter.UnicodeBlock MARCHEN;
+    field public static final int MARCHEN_ID = 268; // 0x10c
     field public static final android.icu.lang.UCharacter.UnicodeBlock MATHEMATICAL_ALPHANUMERIC_SYMBOLS;
     field public static final int MATHEMATICAL_ALPHANUMERIC_SYMBOLS_ID = 93; // 0x5d
     field public static final android.icu.lang.UCharacter.UnicodeBlock MATHEMATICAL_OPERATORS;
@@ -17035,6 +17067,8 @@
     field public static final int MODI_ID = 236; // 0xec
     field public static final android.icu.lang.UCharacter.UnicodeBlock MONGOLIAN;
     field public static final int MONGOLIAN_ID = 37; // 0x25
+    field public static final android.icu.lang.UCharacter.UnicodeBlock MONGOLIAN_SUPPLEMENT;
+    field public static final int MONGOLIAN_SUPPLEMENT_ID = 269; // 0x10d
     field public static final android.icu.lang.UCharacter.UnicodeBlock MRO;
     field public static final int MRO_ID = 237; // 0xed
     field public static final android.icu.lang.UCharacter.UnicodeBlock MULTANI;
@@ -17049,6 +17083,8 @@
     field public static final int MYANMAR_ID = 28; // 0x1c
     field public static final android.icu.lang.UCharacter.UnicodeBlock NABATAEAN;
     field public static final int NABATAEAN_ID = 239; // 0xef
+    field public static final android.icu.lang.UCharacter.UnicodeBlock NEWA;
+    field public static final int NEWA_ID = 270; // 0x10e
     field public static final android.icu.lang.UCharacter.UnicodeBlock NEW_TAI_LUE;
     field public static final int NEW_TAI_LUE_ID = 139; // 0x8b
     field public static final android.icu.lang.UCharacter.UnicodeBlock NKO;
@@ -17080,6 +17116,8 @@
     field public static final int ORIYA_ID = 19; // 0x13
     field public static final android.icu.lang.UCharacter.UnicodeBlock ORNAMENTAL_DINGBATS;
     field public static final int ORNAMENTAL_DINGBATS_ID = 242; // 0xf2
+    field public static final android.icu.lang.UCharacter.UnicodeBlock OSAGE;
+    field public static final int OSAGE_ID = 271; // 0x10f
     field public static final android.icu.lang.UCharacter.UnicodeBlock OSMANYA;
     field public static final int OSMANYA_ID = 122; // 0x7a
     field public static final android.icu.lang.UCharacter.UnicodeBlock PAHAWH_HMONG;
@@ -17182,6 +17220,10 @@
     field public static final int TAKRI_ID = 220; // 0xdc
     field public static final android.icu.lang.UCharacter.UnicodeBlock TAMIL;
     field public static final int TAMIL_ID = 20; // 0x14
+    field public static final android.icu.lang.UCharacter.UnicodeBlock TANGUT;
+    field public static final android.icu.lang.UCharacter.UnicodeBlock TANGUT_COMPONENTS;
+    field public static final int TANGUT_COMPONENTS_ID = 273; // 0x111
+    field public static final int TANGUT_ID = 272; // 0x110
     field public static final android.icu.lang.UCharacter.UnicodeBlock TELUGU;
     field public static final int TELUGU_ID = 21; // 0x15
     field public static final android.icu.lang.UCharacter.UnicodeBlock THAANA;
@@ -17228,7 +17270,11 @@
     field public static final int DOUBLE_QUOTE = 16; // 0x10
     field public static final int EXTEND = 9; // 0x9
     field public static final int EXTENDNUMLET = 7; // 0x7
+    field public static final int E_BASE = 17; // 0x11
+    field public static final int E_BASE_GAZ = 18; // 0x12
+    field public static final int E_MODIFIER = 19; // 0x13
     field public static final int FORMAT = 2; // 0x2
+    field public static final int GLUE_AFTER_ZWJ = 20; // 0x14
     field public static final int HEBREW_LETTER = 14; // 0xe
     field public static final int KATAKANA = 3; // 0x3
     field public static final int LF = 10; // 0xa
@@ -17240,6 +17286,7 @@
     field public static final int OTHER = 0; // 0x0
     field public static final int REGIONAL_INDICATOR = 13; // 0xd
     field public static final int SINGLE_QUOTE = 15; // 0xf
+    field public static final int ZWJ = 21; // 0x15
   }
 
   public final class UCharacterCategory implements android.icu.lang.UCharacterEnums.ECharacterCategory {
@@ -17458,6 +17505,7 @@
     method public static final boolean hasScript(int, int);
     method public static final boolean isCased(int);
     method public static final boolean isRightToLeft(int);
+    field public static final int ADLAM = 167; // 0xa7
     field public static final int AFAKA = 147; // 0x93
     field public static final int AHOM = 161; // 0xa1
     field public static final int ANATOLIAN_HIEROGLYPHS = 156; // 0x9c
@@ -17469,6 +17517,7 @@
     field public static final int BASSA_VAH = 134; // 0x86
     field public static final int BATAK = 63; // 0x3f
     field public static final int BENGALI = 4; // 0x4
+    field public static final int BHAIKSUKI = 168; // 0xa8
     field public static final int BLISSYMBOLS = 64; // 0x40
     field public static final int BOOK_PAHLAVI = 124; // 0x7c
     field public static final int BOPOMOFO = 5; // 0x5
@@ -17507,6 +17556,7 @@
     field public static final int HAN = 17; // 0x11
     field public static final int HANGUL = 18; // 0x12
     field public static final int HANUNOO = 43; // 0x2b
+    field public static final int HAN_WITH_BOPOMOFO = 172; // 0xac
     field public static final int HARAPPAN_INDUS = 77; // 0x4d
     field public static final int HATRAN = 162; // 0xa2
     field public static final int HEBREW = 19; // 0x13
@@ -17517,6 +17567,7 @@
     field public static final int INSCRIPTIONAL_PAHLAVI = 122; // 0x7a
     field public static final int INSCRIPTIONAL_PARTHIAN = 125; // 0x7d
     field public static final int INVALID_CODE = -1; // 0xffffffff
+    field public static final int JAMO = 173; // 0xad
     field public static final int JAPANESE = 105; // 0x69
     field public static final int JAVANESE = 78; // 0x4e
     field public static final int JURCHEN = 148; // 0x94
@@ -17550,6 +17601,7 @@
     field public static final int MANDAEAN = 84; // 0x54
     field public static final int MANDAIC = 84; // 0x54
     field public static final int MANICHAEAN = 121; // 0x79
+    field public static final int MARCHEN = 169; // 0xa9
     field public static final int MATHEMATICAL_NOTATION = 128; // 0x80
     field public static final int MAYAN_HIEROGLYPHS = 85; // 0x55
     field public static final int MEITEI_MAYEK = 115; // 0x73
@@ -17566,6 +17618,7 @@
     field public static final int MYANMAR = 28; // 0x1c
     field public static final int NABATAEAN = 143; // 0x8f
     field public static final int NAKHI_GEBA = 132; // 0x84
+    field public static final int NEWA = 170; // 0xaa
     field public static final int NEW_TAI_LUE = 59; // 0x3b
     field public static final int NKO = 87; // 0x57
     field public static final int NUSHU = 150; // 0x96
@@ -17580,6 +17633,7 @@
     field public static final int OL_CHIKI = 109; // 0x6d
     field public static final int ORIYA = 31; // 0x1f
     field public static final int ORKHON = 88; // 0x58
+    field public static final int OSAGE = 171; // 0xab
     field public static final int OSMANYA = 50; // 0x32
     field public static final int PAHAWH_HMONG = 75; // 0x4b
     field public static final int PALMYRENE = 144; // 0x90
@@ -17605,6 +17659,7 @@
     field public static final int SUNDANESE = 113; // 0x71
     field public static final int SYLOTI_NAGRI = 58; // 0x3a
     field public static final int SYMBOLS = 129; // 0x81
+    field public static final int SYMBOLS_EMOJI = 174; // 0xae
     field public static final int SYRIAC = 34; // 0x22
     field public static final int TAGALOG = 42; // 0x2a
     field public static final int TAGBANWA = 45; // 0x2d
@@ -18131,6 +18186,8 @@
     method public static final android.icu.text.DateFormat.BooleanAttribute[] values();
     enum_constant public static final android.icu.text.DateFormat.BooleanAttribute PARSE_ALLOW_NUMERIC;
     enum_constant public static final android.icu.text.DateFormat.BooleanAttribute PARSE_ALLOW_WHITESPACE;
+    enum_constant public static final android.icu.text.DateFormat.BooleanAttribute PARSE_MULTIPLE_PATTERNS_FOR_MATCH;
+    enum_constant public static final android.icu.text.DateFormat.BooleanAttribute PARSE_PARTIAL_LITERAL_MATCH;
   }
 
   public static class DateFormat.Field extends java.text.Format.Field {
@@ -18815,6 +18872,7 @@
     field public static final int PERCENTSTYLE = 2; // 0x2
     field public static final int PLURALCURRENCYSTYLE = 6; // 0x6
     field public static final int SCIENTIFICSTYLE = 3; // 0x3
+    field public static final int STANDARDCURRENCYSTYLE = 9; // 0x9
   }
 
   public static class NumberFormat.Field extends java.text.Format.Field {
@@ -19609,7 +19667,7 @@
     field public static final int AM_PM = 9; // 0x9
     field public static final int APRIL = 3; // 0x3
     field public static final int AUGUST = 7; // 0x7
-    field protected static final int BASE_FIELD_COUNT = 23; // 0x17
+    field protected static final deprecated int BASE_FIELD_COUNT = 23; // 0x17
     field public static final int DATE = 5; // 0x5
     field public static final int DAY_OF_MONTH = 5; // 0x5
     field public static final int DAY_OF_WEEK = 7; // 0x7
@@ -19637,7 +19695,7 @@
     field public static final int MARCH = 2; // 0x2
     field protected static final int MAXIMUM = 3; // 0x3
     field protected static final java.util.Date MAX_DATE;
-    field protected static final int MAX_FIELD_COUNT = 32; // 0x20
+    field protected static final deprecated int MAX_FIELD_COUNT = 32; // 0x20
     field protected static final int MAX_JULIAN = 2130706432; // 0x7f000000
     field protected static final long MAX_MILLIS = 183882168921600000L; // 0x28d47dbbf19b000L
     field public static final int MAY = 4; // 0x4
@@ -19957,6 +20015,7 @@
     field public static final android.icu.util.MeasureUnit CELSIUS;
     field public static final android.icu.util.MeasureUnit CENTILITER;
     field public static final android.icu.util.MeasureUnit CENTIMETER;
+    field public static final android.icu.util.MeasureUnit CENTURY;
     field public static final android.icu.util.MeasureUnit CUBIC_CENTIMETER;
     field public static final android.icu.util.MeasureUnit CUBIC_FOOT;
     field public static final android.icu.util.MeasureUnit CUBIC_INCH;
@@ -19965,6 +20024,7 @@
     field public static final android.icu.util.MeasureUnit CUBIC_MILE;
     field public static final android.icu.util.MeasureUnit CUBIC_YARD;
     field public static final android.icu.util.MeasureUnit CUP;
+    field public static final android.icu.util.MeasureUnit CUP_METRIC;
     field public static final android.icu.util.TimeUnit DAY;
     field public static final android.icu.util.MeasureUnit DECILITER;
     field public static final android.icu.util.MeasureUnit DECIMETER;
@@ -19976,6 +20036,7 @@
     field public static final android.icu.util.MeasureUnit FOOT;
     field public static final android.icu.util.MeasureUnit FURLONG;
     field public static final android.icu.util.MeasureUnit GALLON;
+    field public static final android.icu.util.MeasureUnit GENERIC_TEMPERATURE;
     field public static final android.icu.util.MeasureUnit GIGABIT;
     field public static final android.icu.util.MeasureUnit GIGABYTE;
     field public static final android.icu.util.MeasureUnit GIGAHERTZ;
@@ -20003,8 +20064,10 @@
     field public static final android.icu.util.MeasureUnit KILOMETER_PER_HOUR;
     field public static final android.icu.util.MeasureUnit KILOWATT;
     field public static final android.icu.util.MeasureUnit KILOWATT_HOUR;
+    field public static final android.icu.util.MeasureUnit KNOT;
     field public static final android.icu.util.MeasureUnit LIGHT_YEAR;
     field public static final android.icu.util.MeasureUnit LITER;
+    field public static final android.icu.util.MeasureUnit LITER_PER_100KILOMETERS;
     field public static final android.icu.util.MeasureUnit LITER_PER_KILOMETER;
     field public static final android.icu.util.MeasureUnit LUX;
     field public static final android.icu.util.MeasureUnit MEGABIT;
@@ -20022,6 +20085,7 @@
     field public static final android.icu.util.MeasureUnit MILE;
     field public static final android.icu.util.MeasureUnit MILE_PER_GALLON;
     field public static final android.icu.util.MeasureUnit MILE_PER_HOUR;
+    field public static final android.icu.util.MeasureUnit MILE_SCANDINAVIAN;
     field public static final android.icu.util.MeasureUnit MILLIAMPERE;
     field public static final android.icu.util.MeasureUnit MILLIBAR;
     field public static final android.icu.util.MeasureUnit MILLIGRAM;
@@ -20041,10 +20105,12 @@
     field public static final android.icu.util.MeasureUnit PARSEC;
     field public static final android.icu.util.MeasureUnit PICOMETER;
     field public static final android.icu.util.MeasureUnit PINT;
+    field public static final android.icu.util.MeasureUnit PINT_METRIC;
     field public static final android.icu.util.MeasureUnit POUND;
     field public static final android.icu.util.MeasureUnit POUND_PER_SQUARE_INCH;
     field public static final android.icu.util.MeasureUnit QUART;
     field public static final android.icu.util.MeasureUnit RADIAN;
+    field public static final android.icu.util.MeasureUnit REVOLUTION_ANGLE;
     field public static final android.icu.util.TimeUnit SECOND;
     field public static final android.icu.util.MeasureUnit SQUARE_CENTIMETER;
     field public static final android.icu.util.MeasureUnit SQUARE_FOOT;
@@ -20356,6 +20422,7 @@
     field public static final android.icu.util.VersionInfo UNICODE_6_3;
     field public static final android.icu.util.VersionInfo UNICODE_7_0;
     field public static final android.icu.util.VersionInfo UNICODE_8_0;
+    field public static final android.icu.util.VersionInfo UNICODE_9_0;
   }
 
 }
@@ -21136,23 +21203,6 @@
     method public abstract void onNmeaReceived(long, java.lang.String);
   }
 
-  public abstract interface IFusedGeofenceHardware implements android.os.IInterface {
-    method public abstract void addGeofences(android.hardware.location.GeofenceHardwareRequestParcelable[]) throws android.os.RemoteException;
-    method public abstract boolean isSupported() throws android.os.RemoteException;
-    method public abstract void modifyGeofenceOptions(int, int, int, int, int, int) throws android.os.RemoteException;
-    method public abstract void pauseMonitoringGeofence(int) throws android.os.RemoteException;
-    method public abstract void removeGeofences(int[]) throws android.os.RemoteException;
-    method public abstract void resumeMonitoringGeofence(int, int) throws android.os.RemoteException;
-  }
-
-  public abstract interface IGpsGeofenceHardware implements android.os.IInterface {
-    method public abstract boolean addCircularHardwareGeofence(int, double, double, double, int, int, int, int) throws android.os.RemoteException;
-    method public abstract boolean isHardwareGeofenceSupported() throws android.os.RemoteException;
-    method public abstract boolean pauseHardwareGeofence(int) throws android.os.RemoteException;
-    method public abstract boolean removeHardwareGeofence(int) throws android.os.RemoteException;
-    method public abstract boolean resumeHardwareGeofence(int, int) throws android.os.RemoteException;
-  }
-
   public class Location implements android.os.Parcelable {
     ctor public Location(java.lang.String);
     ctor public Location(android.location.Location);
@@ -24255,6 +24305,7 @@
     method public android.content.ComponentName getServiceComponent();
     method public android.media.session.MediaSession.Token getSessionToken();
     method public boolean isConnected();
+    method public void search(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SearchCallback);
     method public void subscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback);
     method public void subscribe(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SubscriptionCallback);
     method public void unsubscribe(java.lang.String);
@@ -24290,6 +24341,12 @@
     field public static final int FLAG_PLAYABLE = 2; // 0x2
   }
 
+  public static abstract class MediaBrowser.SearchCallback {
+    ctor public MediaBrowser.SearchCallback();
+    method public void onError(java.lang.String, android.os.Bundle);
+    method public void onSearchResult(java.lang.String, android.os.Bundle, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
+  }
+
   public static abstract class MediaBrowser.SubscriptionCallback {
     ctor public MediaBrowser.SubscriptionCallback();
     method public void onChildrenLoaded(java.lang.String, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
@@ -31683,6 +31740,27 @@
     method public abstract android.os.IBinder asBinder();
   }
 
+  public class IncidentManager {
+    method public void reportIncident(android.os.IncidentReportArgs);
+    method public void reportIncident(java.lang.String, byte[]);
+  }
+
+  public final class IncidentReportArgs implements android.os.Parcelable {
+    ctor public IncidentReportArgs();
+    ctor public IncidentReportArgs(android.os.Parcel);
+    method public void addHeader(byte[]);
+    method public void addSection(int);
+    method public boolean containsSection(int);
+    method public int describeContents();
+    method public boolean isAll();
+    method public static android.os.IncidentReportArgs parseSetting(java.lang.String) throws java.lang.IllegalArgumentException;
+    method public void readFromParcel(android.os.Parcel);
+    method public int sectionCount();
+    method public void setAll(boolean);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.os.IncidentReportArgs> CREATOR;
+  }
+
   public final class LocaleList implements android.os.Parcelable {
     ctor public LocaleList(java.util.Locale...);
     method public int describeContents();
@@ -32370,6 +32448,7 @@
     method public deprecated void setUserRestrictions(android.os.Bundle, android.os.UserHandle);
     method public static boolean supportsMultipleUsers();
     field public static final java.lang.String ALLOW_PARENT_PROFILE_APP_LINKING = "allow_parent_profile_app_linking";
+    field public static final java.lang.String DISALLOW_ADD_MANAGED_PROFILE = "no_add_managed_profile";
     field public static final java.lang.String DISALLOW_ADD_USER = "no_add_user";
     field public static final java.lang.String DISALLOW_ADJUST_VOLUME = "no_adjust_volume";
     field public static final java.lang.String DISALLOW_APPS_CONTROL = "no_control_apps";
@@ -32395,6 +32474,7 @@
     field public static final java.lang.String DISALLOW_OEM_UNLOCK = "no_oem_unlock";
     field public static final java.lang.String DISALLOW_OUTGOING_BEAM = "no_outgoing_beam";
     field public static final java.lang.String DISALLOW_OUTGOING_CALLS = "no_outgoing_calls";
+    field public static final java.lang.String DISALLOW_REMOVE_MANAGED_PROFILE = "no_remove_managed_profile";
     field public static final java.lang.String DISALLOW_REMOVE_USER = "no_remove_user";
     field public static final java.lang.String DISALLOW_SAFE_BOOT = "no_safe_boot";
     field public static final java.lang.String DISALLOW_SET_USER_ICON = "no_set_user_icon";
@@ -37917,6 +37997,7 @@
     method public abstract void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>);
     method public void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>, android.os.Bundle);
     method public void onLoadItem(java.lang.String, android.service.media.MediaBrowserService.Result<android.media.browse.MediaBrowser.MediaItem>);
+    method public void onSearch(java.lang.String, android.os.Bundle, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>);
     method public void setSessionToken(android.media.session.MediaSession.Token);
     field public static final java.lang.String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
   }
@@ -40483,6 +40564,7 @@
     field public static final java.lang.String KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL = "show_onscreen_dial_button_bool";
     field public static final java.lang.String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
     field public static final java.lang.String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
+    field public static final java.lang.String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = "support_3gpp_call_forwarding_while_roaming_bool";
     field public static final java.lang.String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
     field public static final java.lang.String KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL = "support_pause_ims_video_calls_bool";
     field public static final java.lang.String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool";
@@ -41058,6 +41140,7 @@
     method public java.lang.String getSimOperatorName();
     method public java.lang.String getSimSerialNumber();
     method public int getSimState();
+    method public int getSimState(int);
     method public java.lang.String getSubscriberId();
     method public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms();
     method public java.lang.String getVoiceMailAlphaTag();
@@ -41173,7 +41256,11 @@
     field public static final int SIM_ACTIVATION_RESULT_IN_PROGRESS = 2; // 0x2
     field public static final int SIM_ACTIVATION_RESULT_NOT_SUPPORTED = 1; // 0x1
     field public static final int SIM_STATE_ABSENT = 1; // 0x1
+    field public static final int SIM_STATE_CARD_IO_ERROR = 8; // 0x8
+    field public static final int SIM_STATE_CARD_RESTRICTED = 9; // 0x9
     field public static final int SIM_STATE_NETWORK_LOCKED = 4; // 0x4
+    field public static final int SIM_STATE_NOT_READY = 6; // 0x6
+    field public static final int SIM_STATE_PERM_DISABLED = 7; // 0x7
     field public static final int SIM_STATE_PIN_REQUIRED = 2; // 0x2
     field public static final int SIM_STATE_PUK_REQUIRED = 3; // 0x3
     field public static final int SIM_STATE_READY = 5; // 0x5
@@ -41951,7 +42038,7 @@
     method public java.lang.CharSequence subSequence(int, int);
   }
 
-  public class AndroidCharacter {
+  public deprecated class AndroidCharacter {
     ctor public AndroidCharacter();
     method public static void getDirectionalities(char[], byte[], int);
     method public static int getEastAsianWidth(char);
@@ -44814,6 +44901,7 @@
     method public android.view.View findNearestTouchable(android.view.ViewGroup, int, int, int, int[]);
     method public final android.view.View findNextFocus(android.view.ViewGroup, android.view.View, int);
     method public android.view.View findNextFocusFromRect(android.view.ViewGroup, android.graphics.Rect, int);
+    method public android.view.View findNextKeyboardNavigationCluster(android.view.ViewGroup, android.view.View, int);
     method public static android.view.FocusFinder getInstance();
   }
 
@@ -46106,6 +46194,7 @@
     method public void addChildrenForAccessibility(java.util.ArrayList<android.view.View>);
     method public void addFocusables(java.util.ArrayList<android.view.View>, int);
     method public void addFocusables(java.util.ArrayList<android.view.View>, int, int);
+    method public void addKeyboardNavigationClusters(java.util.Collection<android.view.View>, int);
     method public void addOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener);
     method public void addOnLayoutChangeListener(android.view.View.OnLayoutChangeListener);
     method public void addTouchables(java.util.ArrayList<android.view.View>);
@@ -46393,6 +46482,7 @@
     method public boolean isVerticalFadingEdgeEnabled();
     method public boolean isVerticalScrollBarEnabled();
     method public void jumpDrawablesToCurrentState();
+    method public android.view.View keyboardNavigationClusterSearch(int);
     method public void layout(int, int, int, int);
     method public final void measure(int, int);
     method protected static int[] mergeDrawableStates(int[], int[]);
@@ -47040,6 +47130,7 @@
     method protected deprecated boolean isChildrenDrawnWithCacheEnabled();
     method public boolean isMotionEventSplittingEnabled();
     method public boolean isTransitionGroup();
+    method public android.view.View keyboardNavigationClusterSearch(android.view.View, int);
     method public final void layout(int, int, int, int);
     method protected void measureChild(android.view.View, int, int);
     method protected void measureChildWithMargins(android.view.View, int, int, int, int);
@@ -47202,6 +47293,7 @@
     method public abstract boolean isLayoutRequested();
     method public abstract boolean isTextAlignmentResolved();
     method public abstract boolean isTextDirectionResolved();
+    method public abstract android.view.View keyboardNavigationClusterSearch(android.view.View, int);
     method public abstract void notifySubtreeAccessibilityStateChanged(android.view.View, android.view.View, int);
     method public abstract boolean onNestedFling(android.view.View, float, float, boolean);
     method public abstract boolean onNestedPreFling(android.view.View, float, float);
@@ -64858,31 +64950,31 @@
     ctor public CopyOnWriteArrayList();
     ctor public CopyOnWriteArrayList(java.util.Collection<? extends E>);
     ctor public CopyOnWriteArrayList(E[]);
-    method public synchronized boolean add(E);
-    method public synchronized void add(int, E);
-    method public synchronized boolean addAll(java.util.Collection<? extends E>);
-    method public synchronized boolean addAll(int, java.util.Collection<? extends E>);
-    method public synchronized int addAllAbsent(java.util.Collection<? extends E>);
-    method public synchronized boolean addIfAbsent(E);
-    method public synchronized void clear();
+    method public boolean add(E);
+    method public void add(int, E);
+    method public boolean addAll(java.util.Collection<? extends E>);
+    method public boolean addAll(int, java.util.Collection<? extends E>);
+    method public int addAllAbsent(java.util.Collection<? extends E>);
+    method public boolean addIfAbsent(E);
+    method public void clear();
     method public java.lang.Object clone();
     method public boolean contains(java.lang.Object);
     method public boolean containsAll(java.util.Collection<?>);
     method public void forEach(java.util.function.Consumer<? super E>);
     method public E get(int);
-    method public int indexOf(E, int);
     method public int indexOf(java.lang.Object);
+    method public int indexOf(E, int);
     method public boolean isEmpty();
     method public java.util.Iterator<E> iterator();
-    method public int lastIndexOf(E, int);
     method public int lastIndexOf(java.lang.Object);
-    method public java.util.ListIterator<E> listIterator(int);
+    method public int lastIndexOf(E, int);
     method public java.util.ListIterator<E> listIterator();
-    method public synchronized E remove(int);
-    method public synchronized boolean remove(java.lang.Object);
-    method public synchronized boolean removeAll(java.util.Collection<?>);
-    method public synchronized boolean retainAll(java.util.Collection<?>);
-    method public synchronized E set(int, E);
+    method public java.util.ListIterator<E> listIterator(int);
+    method public E remove(int);
+    method public boolean remove(java.lang.Object);
+    method public boolean removeAll(java.util.Collection<?>);
+    method public boolean retainAll(java.util.Collection<?>);
+    method public E set(int, E);
     method public int size();
     method public java.util.List<E> subList(int, int);
     method public java.lang.Object[] toArray();
diff --git a/api/test-current.txt b/api/test-current.txt
index 239863a..10554ff 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3659,6 +3659,7 @@
     method public void setIntent(android.content.Intent);
     method public final void setMediaController(android.media.session.MediaController);
     method public void setOverlayWithDecorCaptionEnabled(boolean);
+    method public void setPictureInPictureActions(java.util.List<android.app.RemoteAction>);
     method public void setPictureInPictureAspectRatio(float);
     method public final deprecated void setProgress(int);
     method public final deprecated void setProgressBarIndeterminate(boolean);
@@ -3740,6 +3741,7 @@
     method public int getLauncherLargeIconDensity();
     method public int getLauncherLargeIconSize();
     method public int getLockTaskModeState();
+    method public static int getMaxNumPictureInPictureActions();
     method public int getMemoryClass();
     method public void getMemoryInfo(android.app.ActivityManager.MemoryInfo);
     method public static void getMyMemoryState(android.app.ActivityManager.RunningAppProcessInfo);
@@ -5558,6 +5560,22 @@
     field public static final int STYLE_SPINNER = 0; // 0x0
   }
 
+  public final class RemoteAction implements android.os.Parcelable {
+    ctor public RemoteAction(android.graphics.drawable.Icon, java.lang.CharSequence, java.lang.CharSequence, android.app.RemoteAction.OnActionListener);
+    method public android.app.RemoteAction clone();
+    method public int describeContents();
+    method public void dump(java.lang.String, java.io.PrintWriter);
+    method public java.lang.CharSequence getContentDescription();
+    method public android.graphics.drawable.Icon getIcon();
+    method public java.lang.CharSequence getTitle();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.RemoteAction> CREATOR;
+  }
+
+  public static abstract interface RemoteAction.OnActionListener {
+    method public abstract void onAction(android.app.RemoteAction);
+  }
+
   public final class RemoteInput implements android.os.Parcelable {
     method public static void addResultsToIntent(android.app.RemoteInput[], android.content.Intent, android.os.Bundle);
     method public int describeContents();
@@ -6228,6 +6246,9 @@
     field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION";
     field public static final deprecated java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME";
     field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM = "android.app.extra.PROVISIONING_DEVICE_ADMIN_SIGNATURE_CHECKSUM";
+    field public static final java.lang.String EXTRA_PROVISIONING_DISCLAIMERS = "android.app.extra.PROVISIONING_DISCLAIMERS";
+    field public static final java.lang.String EXTRA_PROVISIONING_DISCLAIMER_CONTENT = "android.app.extra.PROVISIONING_DISCLAIMER_CONTENT";
+    field public static final java.lang.String EXTRA_PROVISIONING_DISCLAIMER_HEADER = "android.app.extra.PROVISIONING_DISCLAIMER_HEADER";
     field public static final java.lang.String EXTRA_PROVISIONING_EMAIL_ADDRESS = "android.app.extra.PROVISIONING_EMAIL_ADDRESS";
     field public static final java.lang.String EXTRA_PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION = "android.app.extra.PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION";
     field public static final java.lang.String EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED = "android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED";
@@ -9723,6 +9744,7 @@
   public class LauncherApps {
     ctor public LauncherApps(android.content.Context);
     method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
+    method public android.content.pm.LauncherApps.PinItemRequest getPinItemRequest(android.content.Intent);
     method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(android.content.pm.ShortcutInfo, int);
     method public android.graphics.drawable.Drawable getShortcutIconDrawable(android.content.pm.ShortcutInfo, int);
     method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
@@ -9738,6 +9760,8 @@
     method public void startShortcut(java.lang.String, java.lang.String, android.graphics.Rect, android.os.Bundle, android.os.UserHandle);
     method public void startShortcut(android.content.pm.ShortcutInfo, android.graphics.Rect, android.os.Bundle);
     method public void unregisterCallback(android.content.pm.LauncherApps.Callback);
+    field public static final java.lang.String ACTION_CONFIRM_PIN_ITEM = "android.content.pm.action.CONFIRM_PIN_ITEM";
+    field public static final java.lang.String EXTRA_PIN_ITEM_REQUEST = "android.content.pm.extra.PIN_ITEM_REQUEST";
   }
 
   public static abstract class LauncherApps.Callback {
@@ -9752,6 +9776,21 @@
     method public void onShortcutsChanged(java.lang.String, java.util.List<android.content.pm.ShortcutInfo>, android.os.UserHandle);
   }
 
+  public static final class LauncherApps.PinItemRequest implements android.os.Parcelable {
+    method public boolean accept(android.os.Bundle);
+    method public boolean accept();
+    method public int describeContents();
+    method public int getRequestType();
+    method public android.content.pm.ShortcutInfo getShortcutInfo();
+    method public boolean isValid();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.content.pm.LauncherApps.PinItemRequest> CREATOR;
+    field public static final int REQUEST_TYPE_SHORTCUT = 1; // 0x1
+  }
+
+  public static abstract class LauncherApps.PinItemRequest.RequestType implements java.lang.annotation.Annotation {
+  }
+
   public static class LauncherApps.ShortcutQuery {
     ctor public LauncherApps.ShortcutQuery();
     method public android.content.pm.LauncherApps.ShortcutQuery setActivity(android.content.ComponentName);
@@ -10319,9 +10358,11 @@
     method public int getMaxShortcutCountPerActivity();
     method public java.util.List<android.content.pm.ShortcutInfo> getPinnedShortcuts();
     method public boolean isRateLimitingActive();
+    method public boolean isRequestPinShortcutSupported();
     method public void removeAllDynamicShortcuts();
     method public void removeDynamicShortcuts(java.util.List<java.lang.String>);
     method public void reportShortcutUsed(java.lang.String);
+    method public boolean requestPinShortcut(android.content.pm.ShortcutInfo, android.content.IntentSender);
     method public boolean setDynamicShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
     method public boolean updateShortcuts(java.util.List<android.content.pm.ShortcutInfo>);
   }
@@ -11759,6 +11800,8 @@
     method public static android.graphics.Bitmap createBitmap(android.graphics.Bitmap, int, int, int, int, android.graphics.Matrix, boolean);
     method public static android.graphics.Bitmap createBitmap(int, int, android.graphics.Bitmap.Config);
     method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int, int, android.graphics.Bitmap.Config);
+    method public static android.graphics.Bitmap createBitmap(int, int, android.graphics.Bitmap.Config, boolean);
+    method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int, int, android.graphics.Bitmap.Config, boolean);
     method public static android.graphics.Bitmap createBitmap(int[], int, int, int, int, android.graphics.Bitmap.Config);
     method public static android.graphics.Bitmap createBitmap(android.util.DisplayMetrics, int[], int, int, int, int, android.graphics.Bitmap.Config);
     method public static android.graphics.Bitmap createBitmap(int[], int, int, android.graphics.Bitmap.Config);
@@ -11823,6 +11866,7 @@
     enum_constant public static final deprecated android.graphics.Bitmap.Config ARGB_4444;
     enum_constant public static final android.graphics.Bitmap.Config ARGB_8888;
     enum_constant public static final android.graphics.Bitmap.Config HARDWARE;
+    enum_constant public static final android.graphics.Bitmap.Config RGBA_F16;
     enum_constant public static final android.graphics.Bitmap.Config RGB_565;
   }
 
@@ -12182,6 +12226,7 @@
     method public android.graphics.Bitmap render();
     method public android.graphics.ColorSpace.Renderer showWhitePoint(boolean);
     method public android.graphics.ColorSpace.Renderer size(int);
+    method public android.graphics.ColorSpace.Renderer uniformChromaticityScale(boolean);
   }
 
   public static class ColorSpace.Rgb extends android.graphics.ColorSpace {
@@ -12717,7 +12762,9 @@
     field public static final deprecated int RGBA_4444 = 7; // 0x7
     field public static final deprecated int RGBA_5551 = 6; // 0x6
     field public static final int RGBA_8888 = 1; // 0x1
+    field public static final int RGBA_F16 = 22; // 0x16
     field public static final int RGBX_8888 = 2; // 0x2
+    field public static final int RGBX_F16 = 23; // 0x17
     field public static final deprecated int RGB_332 = 11; // 0xb
     field public static final int RGB_565 = 4; // 0x4
     field public static final int RGB_888 = 3; // 0x3
@@ -15273,6 +15320,10 @@
     field public static final int CONTROL = 1; // 0x1
     field public static final int CR = 2; // 0x2
     field public static final int EXTEND = 3; // 0x3
+    field public static final int E_BASE = 13; // 0xd
+    field public static final int E_BASE_GAZ = 14; // 0xe
+    field public static final int E_MODIFIER = 15; // 0xf
+    field public static final int GLUE_AFTER_ZWJ = 16; // 0x10
     field public static final int L = 4; // 0x4
     field public static final int LF = 5; // 0x5
     field public static final int LV = 6; // 0x6
@@ -15283,6 +15334,7 @@
     field public static final int SPACING_MARK = 10; // 0xa
     field public static final int T = 8; // 0x8
     field public static final int V = 9; // 0x9
+    field public static final int ZWJ = 17; // 0x11
   }
 
   public static abstract interface UCharacter.HangulSyllableType {
@@ -15295,6 +15347,9 @@
   }
 
   public static abstract interface UCharacter.JoiningGroup {
+    field public static final int AFRICAN_FEH = 86; // 0x56
+    field public static final int AFRICAN_NOON = 87; // 0x57
+    field public static final int AFRICAN_QAF = 88; // 0x58
     field public static final int AIN = 1; // 0x1
     field public static final int ALAPH = 2; // 0x2
     field public static final int ALEF = 3; // 0x3
@@ -15408,6 +15463,8 @@
     field public static final int CONDITIONAL_JAPANESE_STARTER = 37; // 0x25
     field public static final int CONTINGENT_BREAK = 7; // 0x7
     field public static final int EXCLAMATION = 11; // 0xb
+    field public static final int E_BASE = 40; // 0x28
+    field public static final int E_MODIFIER = 41; // 0x29
     field public static final int GLUE = 12; // 0xc
     field public static final int H2 = 31; // 0x1f
     field public static final int H3 = 32; // 0x20
@@ -15434,6 +15491,7 @@
     field public static final int SURROGATE = 25; // 0x19
     field public static final int UNKNOWN = 0; // 0x0
     field public static final int WORD_JOINER = 30; // 0x1e
+    field public static final int ZWJ = 42; // 0x2a
     field public static final int ZWSPACE = 28; // 0x1c
   }
 
@@ -15467,6 +15525,8 @@
     method public int getID();
     method public static android.icu.lang.UCharacter.UnicodeBlock getInstance(int);
     method public static android.icu.lang.UCharacter.UnicodeBlock of(int);
+    field public static final android.icu.lang.UCharacter.UnicodeBlock ADLAM;
+    field public static final int ADLAM_ID = 263; // 0x107
     field public static final android.icu.lang.UCharacter.UnicodeBlock AEGEAN_NUMBERS;
     field public static final int AEGEAN_NUMBERS_ID = 119; // 0x77
     field public static final android.icu.lang.UCharacter.UnicodeBlock AHOM;
@@ -15515,6 +15575,8 @@
     field public static final int BATAK_ID = 199; // 0xc7
     field public static final android.icu.lang.UCharacter.UnicodeBlock BENGALI;
     field public static final int BENGALI_ID = 16; // 0x10
+    field public static final android.icu.lang.UCharacter.UnicodeBlock BHAIKSUKI;
+    field public static final int BHAIKSUKI_ID = 264; // 0x108
     field public static final android.icu.lang.UCharacter.UnicodeBlock BLOCK_ELEMENTS;
     field public static final int BLOCK_ELEMENTS_ID = 53; // 0x35
     field public static final android.icu.lang.UCharacter.UnicodeBlock BOPOMOFO;
@@ -15604,6 +15666,8 @@
     field public static final int CYRILLIC_EXTENDED_A_ID = 158; // 0x9e
     field public static final android.icu.lang.UCharacter.UnicodeBlock CYRILLIC_EXTENDED_B;
     field public static final int CYRILLIC_EXTENDED_B_ID = 160; // 0xa0
+    field public static final android.icu.lang.UCharacter.UnicodeBlock CYRILLIC_EXTENDED_C;
+    field public static final int CYRILLIC_EXTENDED_C_ID = 265; // 0x109
     field public static final int CYRILLIC_ID = 9; // 0x9
     field public static final android.icu.lang.UCharacter.UnicodeBlock CYRILLIC_SUPPLEMENT;
     field public static final android.icu.lang.UCharacter.UnicodeBlock CYRILLIC_SUPPLEMENTARY;
@@ -15657,6 +15721,8 @@
     field public static final int GEORGIAN_SUPPLEMENT_ID = 135; // 0x87
     field public static final android.icu.lang.UCharacter.UnicodeBlock GLAGOLITIC;
     field public static final int GLAGOLITIC_ID = 136; // 0x88
+    field public static final android.icu.lang.UCharacter.UnicodeBlock GLAGOLITIC_SUPPLEMENT;
+    field public static final int GLAGOLITIC_SUPPLEMENT_ID = 266; // 0x10a
     field public static final android.icu.lang.UCharacter.UnicodeBlock GOTHIC;
     field public static final int GOTHIC_ID = 89; // 0x59
     field public static final android.icu.lang.UCharacter.UnicodeBlock GRANTHA;
@@ -15695,6 +15761,8 @@
     field public static final int HIRAGANA_ID = 62; // 0x3e
     field public static final android.icu.lang.UCharacter.UnicodeBlock IDEOGRAPHIC_DESCRIPTION_CHARACTERS;
     field public static final int IDEOGRAPHIC_DESCRIPTION_CHARACTERS_ID = 60; // 0x3c
+    field public static final android.icu.lang.UCharacter.UnicodeBlock IDEOGRAPHIC_SYMBOLS_AND_PUNCTUATION;
+    field public static final int IDEOGRAPHIC_SYMBOLS_AND_PUNCTUATION_ID = 267; // 0x10b
     field public static final android.icu.lang.UCharacter.UnicodeBlock IMPERIAL_ARAMAIC;
     field public static final int IMPERIAL_ARAMAIC_ID = 186; // 0xba
     field public static final android.icu.lang.UCharacter.UnicodeBlock INSCRIPTIONAL_PAHLAVI;
@@ -15779,6 +15847,8 @@
     field public static final int MANDAIC_ID = 198; // 0xc6
     field public static final android.icu.lang.UCharacter.UnicodeBlock MANICHAEAN;
     field public static final int MANICHAEAN_ID = 234; // 0xea
+    field public static final android.icu.lang.UCharacter.UnicodeBlock MARCHEN;
+    field public static final int MARCHEN_ID = 268; // 0x10c
     field public static final android.icu.lang.UCharacter.UnicodeBlock MATHEMATICAL_ALPHANUMERIC_SYMBOLS;
     field public static final int MATHEMATICAL_ALPHANUMERIC_SYMBOLS_ID = 93; // 0x5d
     field public static final android.icu.lang.UCharacter.UnicodeBlock MATHEMATICAL_OPERATORS;
@@ -15813,6 +15883,8 @@
     field public static final int MODI_ID = 236; // 0xec
     field public static final android.icu.lang.UCharacter.UnicodeBlock MONGOLIAN;
     field public static final int MONGOLIAN_ID = 37; // 0x25
+    field public static final android.icu.lang.UCharacter.UnicodeBlock MONGOLIAN_SUPPLEMENT;
+    field public static final int MONGOLIAN_SUPPLEMENT_ID = 269; // 0x10d
     field public static final android.icu.lang.UCharacter.UnicodeBlock MRO;
     field public static final int MRO_ID = 237; // 0xed
     field public static final android.icu.lang.UCharacter.UnicodeBlock MULTANI;
@@ -15827,6 +15899,8 @@
     field public static final int MYANMAR_ID = 28; // 0x1c
     field public static final android.icu.lang.UCharacter.UnicodeBlock NABATAEAN;
     field public static final int NABATAEAN_ID = 239; // 0xef
+    field public static final android.icu.lang.UCharacter.UnicodeBlock NEWA;
+    field public static final int NEWA_ID = 270; // 0x10e
     field public static final android.icu.lang.UCharacter.UnicodeBlock NEW_TAI_LUE;
     field public static final int NEW_TAI_LUE_ID = 139; // 0x8b
     field public static final android.icu.lang.UCharacter.UnicodeBlock NKO;
@@ -15858,6 +15932,8 @@
     field public static final int ORIYA_ID = 19; // 0x13
     field public static final android.icu.lang.UCharacter.UnicodeBlock ORNAMENTAL_DINGBATS;
     field public static final int ORNAMENTAL_DINGBATS_ID = 242; // 0xf2
+    field public static final android.icu.lang.UCharacter.UnicodeBlock OSAGE;
+    field public static final int OSAGE_ID = 271; // 0x10f
     field public static final android.icu.lang.UCharacter.UnicodeBlock OSMANYA;
     field public static final int OSMANYA_ID = 122; // 0x7a
     field public static final android.icu.lang.UCharacter.UnicodeBlock PAHAWH_HMONG;
@@ -15960,6 +16036,10 @@
     field public static final int TAKRI_ID = 220; // 0xdc
     field public static final android.icu.lang.UCharacter.UnicodeBlock TAMIL;
     field public static final int TAMIL_ID = 20; // 0x14
+    field public static final android.icu.lang.UCharacter.UnicodeBlock TANGUT;
+    field public static final android.icu.lang.UCharacter.UnicodeBlock TANGUT_COMPONENTS;
+    field public static final int TANGUT_COMPONENTS_ID = 273; // 0x111
+    field public static final int TANGUT_ID = 272; // 0x110
     field public static final android.icu.lang.UCharacter.UnicodeBlock TELUGU;
     field public static final int TELUGU_ID = 21; // 0x15
     field public static final android.icu.lang.UCharacter.UnicodeBlock THAANA;
@@ -16006,7 +16086,11 @@
     field public static final int DOUBLE_QUOTE = 16; // 0x10
     field public static final int EXTEND = 9; // 0x9
     field public static final int EXTENDNUMLET = 7; // 0x7
+    field public static final int E_BASE = 17; // 0x11
+    field public static final int E_BASE_GAZ = 18; // 0x12
+    field public static final int E_MODIFIER = 19; // 0x13
     field public static final int FORMAT = 2; // 0x2
+    field public static final int GLUE_AFTER_ZWJ = 20; // 0x14
     field public static final int HEBREW_LETTER = 14; // 0xe
     field public static final int KATAKANA = 3; // 0x3
     field public static final int LF = 10; // 0xa
@@ -16018,6 +16102,7 @@
     field public static final int OTHER = 0; // 0x0
     field public static final int REGIONAL_INDICATOR = 13; // 0xd
     field public static final int SINGLE_QUOTE = 15; // 0xf
+    field public static final int ZWJ = 21; // 0x15
   }
 
   public final class UCharacterCategory implements android.icu.lang.UCharacterEnums.ECharacterCategory {
@@ -16236,6 +16321,7 @@
     method public static final boolean hasScript(int, int);
     method public static final boolean isCased(int);
     method public static final boolean isRightToLeft(int);
+    field public static final int ADLAM = 167; // 0xa7
     field public static final int AFAKA = 147; // 0x93
     field public static final int AHOM = 161; // 0xa1
     field public static final int ANATOLIAN_HIEROGLYPHS = 156; // 0x9c
@@ -16247,6 +16333,7 @@
     field public static final int BASSA_VAH = 134; // 0x86
     field public static final int BATAK = 63; // 0x3f
     field public static final int BENGALI = 4; // 0x4
+    field public static final int BHAIKSUKI = 168; // 0xa8
     field public static final int BLISSYMBOLS = 64; // 0x40
     field public static final int BOOK_PAHLAVI = 124; // 0x7c
     field public static final int BOPOMOFO = 5; // 0x5
@@ -16285,6 +16372,7 @@
     field public static final int HAN = 17; // 0x11
     field public static final int HANGUL = 18; // 0x12
     field public static final int HANUNOO = 43; // 0x2b
+    field public static final int HAN_WITH_BOPOMOFO = 172; // 0xac
     field public static final int HARAPPAN_INDUS = 77; // 0x4d
     field public static final int HATRAN = 162; // 0xa2
     field public static final int HEBREW = 19; // 0x13
@@ -16295,6 +16383,7 @@
     field public static final int INSCRIPTIONAL_PAHLAVI = 122; // 0x7a
     field public static final int INSCRIPTIONAL_PARTHIAN = 125; // 0x7d
     field public static final int INVALID_CODE = -1; // 0xffffffff
+    field public static final int JAMO = 173; // 0xad
     field public static final int JAPANESE = 105; // 0x69
     field public static final int JAVANESE = 78; // 0x4e
     field public static final int JURCHEN = 148; // 0x94
@@ -16328,6 +16417,7 @@
     field public static final int MANDAEAN = 84; // 0x54
     field public static final int MANDAIC = 84; // 0x54
     field public static final int MANICHAEAN = 121; // 0x79
+    field public static final int MARCHEN = 169; // 0xa9
     field public static final int MATHEMATICAL_NOTATION = 128; // 0x80
     field public static final int MAYAN_HIEROGLYPHS = 85; // 0x55
     field public static final int MEITEI_MAYEK = 115; // 0x73
@@ -16344,6 +16434,7 @@
     field public static final int MYANMAR = 28; // 0x1c
     field public static final int NABATAEAN = 143; // 0x8f
     field public static final int NAKHI_GEBA = 132; // 0x84
+    field public static final int NEWA = 170; // 0xaa
     field public static final int NEW_TAI_LUE = 59; // 0x3b
     field public static final int NKO = 87; // 0x57
     field public static final int NUSHU = 150; // 0x96
@@ -16358,6 +16449,7 @@
     field public static final int OL_CHIKI = 109; // 0x6d
     field public static final int ORIYA = 31; // 0x1f
     field public static final int ORKHON = 88; // 0x58
+    field public static final int OSAGE = 171; // 0xab
     field public static final int OSMANYA = 50; // 0x32
     field public static final int PAHAWH_HMONG = 75; // 0x4b
     field public static final int PALMYRENE = 144; // 0x90
@@ -16383,6 +16475,7 @@
     field public static final int SUNDANESE = 113; // 0x71
     field public static final int SYLOTI_NAGRI = 58; // 0x3a
     field public static final int SYMBOLS = 129; // 0x81
+    field public static final int SYMBOLS_EMOJI = 174; // 0xae
     field public static final int SYRIAC = 34; // 0x22
     field public static final int TAGALOG = 42; // 0x2a
     field public static final int TAGBANWA = 45; // 0x2d
@@ -16909,6 +17002,8 @@
     method public static final android.icu.text.DateFormat.BooleanAttribute[] values();
     enum_constant public static final android.icu.text.DateFormat.BooleanAttribute PARSE_ALLOW_NUMERIC;
     enum_constant public static final android.icu.text.DateFormat.BooleanAttribute PARSE_ALLOW_WHITESPACE;
+    enum_constant public static final android.icu.text.DateFormat.BooleanAttribute PARSE_MULTIPLE_PATTERNS_FOR_MATCH;
+    enum_constant public static final android.icu.text.DateFormat.BooleanAttribute PARSE_PARTIAL_LITERAL_MATCH;
   }
 
   public static class DateFormat.Field extends java.text.Format.Field {
@@ -17593,6 +17688,7 @@
     field public static final int PERCENTSTYLE = 2; // 0x2
     field public static final int PLURALCURRENCYSTYLE = 6; // 0x6
     field public static final int SCIENTIFICSTYLE = 3; // 0x3
+    field public static final int STANDARDCURRENCYSTYLE = 9; // 0x9
   }
 
   public static class NumberFormat.Field extends java.text.Format.Field {
@@ -18387,7 +18483,7 @@
     field public static final int AM_PM = 9; // 0x9
     field public static final int APRIL = 3; // 0x3
     field public static final int AUGUST = 7; // 0x7
-    field protected static final int BASE_FIELD_COUNT = 23; // 0x17
+    field protected static final deprecated int BASE_FIELD_COUNT = 23; // 0x17
     field public static final int DATE = 5; // 0x5
     field public static final int DAY_OF_MONTH = 5; // 0x5
     field public static final int DAY_OF_WEEK = 7; // 0x7
@@ -18415,7 +18511,7 @@
     field public static final int MARCH = 2; // 0x2
     field protected static final int MAXIMUM = 3; // 0x3
     field protected static final java.util.Date MAX_DATE;
-    field protected static final int MAX_FIELD_COUNT = 32; // 0x20
+    field protected static final deprecated int MAX_FIELD_COUNT = 32; // 0x20
     field protected static final int MAX_JULIAN = 2130706432; // 0x7f000000
     field protected static final long MAX_MILLIS = 183882168921600000L; // 0x28d47dbbf19b000L
     field public static final int MAY = 4; // 0x4
@@ -18735,6 +18831,7 @@
     field public static final android.icu.util.MeasureUnit CELSIUS;
     field public static final android.icu.util.MeasureUnit CENTILITER;
     field public static final android.icu.util.MeasureUnit CENTIMETER;
+    field public static final android.icu.util.MeasureUnit CENTURY;
     field public static final android.icu.util.MeasureUnit CUBIC_CENTIMETER;
     field public static final android.icu.util.MeasureUnit CUBIC_FOOT;
     field public static final android.icu.util.MeasureUnit CUBIC_INCH;
@@ -18743,6 +18840,7 @@
     field public static final android.icu.util.MeasureUnit CUBIC_MILE;
     field public static final android.icu.util.MeasureUnit CUBIC_YARD;
     field public static final android.icu.util.MeasureUnit CUP;
+    field public static final android.icu.util.MeasureUnit CUP_METRIC;
     field public static final android.icu.util.TimeUnit DAY;
     field public static final android.icu.util.MeasureUnit DECILITER;
     field public static final android.icu.util.MeasureUnit DECIMETER;
@@ -18754,6 +18852,7 @@
     field public static final android.icu.util.MeasureUnit FOOT;
     field public static final android.icu.util.MeasureUnit FURLONG;
     field public static final android.icu.util.MeasureUnit GALLON;
+    field public static final android.icu.util.MeasureUnit GENERIC_TEMPERATURE;
     field public static final android.icu.util.MeasureUnit GIGABIT;
     field public static final android.icu.util.MeasureUnit GIGABYTE;
     field public static final android.icu.util.MeasureUnit GIGAHERTZ;
@@ -18781,8 +18880,10 @@
     field public static final android.icu.util.MeasureUnit KILOMETER_PER_HOUR;
     field public static final android.icu.util.MeasureUnit KILOWATT;
     field public static final android.icu.util.MeasureUnit KILOWATT_HOUR;
+    field public static final android.icu.util.MeasureUnit KNOT;
     field public static final android.icu.util.MeasureUnit LIGHT_YEAR;
     field public static final android.icu.util.MeasureUnit LITER;
+    field public static final android.icu.util.MeasureUnit LITER_PER_100KILOMETERS;
     field public static final android.icu.util.MeasureUnit LITER_PER_KILOMETER;
     field public static final android.icu.util.MeasureUnit LUX;
     field public static final android.icu.util.MeasureUnit MEGABIT;
@@ -18800,6 +18901,7 @@
     field public static final android.icu.util.MeasureUnit MILE;
     field public static final android.icu.util.MeasureUnit MILE_PER_GALLON;
     field public static final android.icu.util.MeasureUnit MILE_PER_HOUR;
+    field public static final android.icu.util.MeasureUnit MILE_SCANDINAVIAN;
     field public static final android.icu.util.MeasureUnit MILLIAMPERE;
     field public static final android.icu.util.MeasureUnit MILLIBAR;
     field public static final android.icu.util.MeasureUnit MILLIGRAM;
@@ -18819,10 +18921,12 @@
     field public static final android.icu.util.MeasureUnit PARSEC;
     field public static final android.icu.util.MeasureUnit PICOMETER;
     field public static final android.icu.util.MeasureUnit PINT;
+    field public static final android.icu.util.MeasureUnit PINT_METRIC;
     field public static final android.icu.util.MeasureUnit POUND;
     field public static final android.icu.util.MeasureUnit POUND_PER_SQUARE_INCH;
     field public static final android.icu.util.MeasureUnit QUART;
     field public static final android.icu.util.MeasureUnit RADIAN;
+    field public static final android.icu.util.MeasureUnit REVOLUTION_ANGLE;
     field public static final android.icu.util.TimeUnit SECOND;
     field public static final android.icu.util.MeasureUnit SQUARE_CENTIMETER;
     field public static final android.icu.util.MeasureUnit SQUARE_FOOT;
@@ -19134,6 +19238,7 @@
     field public static final android.icu.util.VersionInfo UNICODE_6_3;
     field public static final android.icu.util.VersionInfo UNICODE_7_0;
     field public static final android.icu.util.VersionInfo UNICODE_8_0;
+    field public static final android.icu.util.VersionInfo UNICODE_9_0;
   }
 
 }
@@ -22696,6 +22801,7 @@
     method public android.content.ComponentName getServiceComponent();
     method public android.media.session.MediaSession.Token getSessionToken();
     method public boolean isConnected();
+    method public void search(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SearchCallback);
     method public void subscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback);
     method public void subscribe(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SubscriptionCallback);
     method public void unsubscribe(java.lang.String);
@@ -22731,6 +22837,12 @@
     field public static final int FLAG_PLAYABLE = 2; // 0x2
   }
 
+  public static abstract class MediaBrowser.SearchCallback {
+    ctor public MediaBrowser.SearchCallback();
+    method public void onError(java.lang.String, android.os.Bundle);
+    method public void onSearchResult(java.lang.String, android.os.Bundle, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
+  }
+
   public static abstract class MediaBrowser.SubscriptionCallback {
     ctor public MediaBrowser.SubscriptionCallback();
     method public void onChildrenLoaded(java.lang.String, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
@@ -29201,6 +29313,27 @@
     method public abstract android.os.IBinder asBinder();
   }
 
+  public class IncidentManager {
+    method public void reportIncident(android.os.IncidentReportArgs);
+    method public void reportIncident(java.lang.String, byte[]);
+  }
+
+  public final class IncidentReportArgs implements android.os.Parcelable {
+    ctor public IncidentReportArgs();
+    ctor public IncidentReportArgs(android.os.Parcel);
+    method public void addHeader(byte[]);
+    method public void addSection(int);
+    method public boolean containsSection(int);
+    method public int describeContents();
+    method public boolean isAll();
+    method public static android.os.IncidentReportArgs parseSetting(java.lang.String) throws java.lang.IllegalArgumentException;
+    method public void readFromParcel(android.os.Parcel);
+    method public int sectionCount();
+    method public void setAll(boolean);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.os.IncidentReportArgs> CREATOR;
+  }
+
   public final class LocaleList implements android.os.Parcelable {
     ctor public LocaleList(java.util.Locale...);
     method public int describeContents();
@@ -29803,6 +29936,7 @@
     method public deprecated void setUserRestrictions(android.os.Bundle, android.os.UserHandle);
     method public static boolean supportsMultipleUsers();
     field public static final java.lang.String ALLOW_PARENT_PROFILE_APP_LINKING = "allow_parent_profile_app_linking";
+    field public static final java.lang.String DISALLOW_ADD_MANAGED_PROFILE = "no_add_managed_profile";
     field public static final java.lang.String DISALLOW_ADD_USER = "no_add_user";
     field public static final java.lang.String DISALLOW_ADJUST_VOLUME = "no_adjust_volume";
     field public static final java.lang.String DISALLOW_APPS_CONTROL = "no_control_apps";
@@ -29827,6 +29961,7 @@
     field public static final java.lang.String DISALLOW_NETWORK_RESET = "no_network_reset";
     field public static final java.lang.String DISALLOW_OUTGOING_BEAM = "no_outgoing_beam";
     field public static final java.lang.String DISALLOW_OUTGOING_CALLS = "no_outgoing_calls";
+    field public static final java.lang.String DISALLOW_REMOVE_MANAGED_PROFILE = "no_remove_managed_profile";
     field public static final java.lang.String DISALLOW_REMOVE_USER = "no_remove_user";
     field public static final java.lang.String DISALLOW_SAFE_BOOT = "no_safe_boot";
     field public static final java.lang.String DISALLOW_SET_USER_ICON = "no_set_user_icon";
@@ -35172,6 +35307,7 @@
     method public abstract void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>);
     method public void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>, android.os.Bundle);
     method public void onLoadItem(java.lang.String, android.service.media.MediaBrowserService.Result<android.media.browse.MediaBrowser.MediaItem>);
+    method public void onSearch(java.lang.String, android.os.Bundle, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>);
     method public void setSessionToken(android.media.session.MediaSession.Token);
     field public static final java.lang.String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
   }
@@ -37465,6 +37601,7 @@
     field public static final java.lang.String KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL = "show_onscreen_dial_button_bool";
     field public static final java.lang.String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
     field public static final java.lang.String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
+    field public static final java.lang.String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = "support_3gpp_call_forwarding_while_roaming_bool";
     field public static final java.lang.String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
     field public static final java.lang.String KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL = "support_pause_ims_video_calls_bool";
     field public static final java.lang.String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool";
@@ -38002,6 +38139,7 @@
     method public java.lang.String getSimOperatorName();
     method public java.lang.String getSimSerialNumber();
     method public int getSimState();
+    method public int getSimState(int);
     method public java.lang.String getSubscriberId();
     method public java.lang.String getVoiceMailAlphaTag();
     method public java.lang.String getVoiceMailNumber();
@@ -38086,7 +38224,11 @@
     field public static final int PHONE_TYPE_NONE = 0; // 0x0
     field public static final int PHONE_TYPE_SIP = 3; // 0x3
     field public static final int SIM_STATE_ABSENT = 1; // 0x1
+    field public static final int SIM_STATE_CARD_IO_ERROR = 8; // 0x8
+    field public static final int SIM_STATE_CARD_RESTRICTED = 9; // 0x9
     field public static final int SIM_STATE_NETWORK_LOCKED = 4; // 0x4
+    field public static final int SIM_STATE_NOT_READY = 6; // 0x6
+    field public static final int SIM_STATE_PERM_DISABLED = 7; // 0x7
     field public static final int SIM_STATE_PIN_REQUIRED = 2; // 0x2
     field public static final int SIM_STATE_PUK_REQUIRED = 3; // 0x3
     field public static final int SIM_STATE_READY = 5; // 0x5
@@ -38853,7 +38995,7 @@
     method public java.lang.CharSequence subSequence(int, int);
   }
 
-  public class AndroidCharacter {
+  public deprecated class AndroidCharacter {
     ctor public AndroidCharacter();
     method public static void getDirectionalities(char[], byte[], int);
     method public static int getEastAsianWidth(char);
@@ -41883,6 +42025,7 @@
     method public android.view.View findNearestTouchable(android.view.ViewGroup, int, int, int, int[]);
     method public final android.view.View findNextFocus(android.view.ViewGroup, android.view.View, int);
     method public android.view.View findNextFocusFromRect(android.view.ViewGroup, android.graphics.Rect, int);
+    method public android.view.View findNextKeyboardNavigationCluster(android.view.ViewGroup, android.view.View, int);
     method public static android.view.FocusFinder getInstance();
   }
 
@@ -43177,6 +43320,7 @@
     method public void addChildrenForAccessibility(java.util.ArrayList<android.view.View>);
     method public void addFocusables(java.util.ArrayList<android.view.View>, int);
     method public void addFocusables(java.util.ArrayList<android.view.View>, int, int);
+    method public void addKeyboardNavigationClusters(java.util.Collection<android.view.View>, int);
     method public void addOnAttachStateChangeListener(android.view.View.OnAttachStateChangeListener);
     method public void addOnLayoutChangeListener(android.view.View.OnLayoutChangeListener);
     method public void addTouchables(java.util.ArrayList<android.view.View>);
@@ -43465,6 +43609,7 @@
     method public boolean isVerticalFadingEdgeEnabled();
     method public boolean isVerticalScrollBarEnabled();
     method public void jumpDrawablesToCurrentState();
+    method public android.view.View keyboardNavigationClusterSearch(int);
     method public void layout(int, int, int, int);
     method public final void measure(int, int);
     method protected static int[] mergeDrawableStates(int[], int[]);
@@ -44116,6 +44261,7 @@
     method protected deprecated boolean isChildrenDrawnWithCacheEnabled();
     method public boolean isMotionEventSplittingEnabled();
     method public boolean isTransitionGroup();
+    method public android.view.View keyboardNavigationClusterSearch(android.view.View, int);
     method public final void layout(int, int, int, int);
     method protected void measureChild(android.view.View, int, int);
     method protected void measureChildWithMargins(android.view.View, int, int, int, int);
@@ -44278,6 +44424,7 @@
     method public abstract boolean isLayoutRequested();
     method public abstract boolean isTextAlignmentResolved();
     method public abstract boolean isTextDirectionResolved();
+    method public abstract android.view.View keyboardNavigationClusterSearch(android.view.View, int);
     method public abstract void notifySubtreeAccessibilityStateChanged(android.view.View, android.view.View, int);
     method public abstract boolean onNestedFling(android.view.View, float, float, boolean);
     method public abstract boolean onNestedPreFling(android.view.View, float, float);
@@ -61590,31 +61737,31 @@
     ctor public CopyOnWriteArrayList();
     ctor public CopyOnWriteArrayList(java.util.Collection<? extends E>);
     ctor public CopyOnWriteArrayList(E[]);
-    method public synchronized boolean add(E);
-    method public synchronized void add(int, E);
-    method public synchronized boolean addAll(java.util.Collection<? extends E>);
-    method public synchronized boolean addAll(int, java.util.Collection<? extends E>);
-    method public synchronized int addAllAbsent(java.util.Collection<? extends E>);
-    method public synchronized boolean addIfAbsent(E);
-    method public synchronized void clear();
+    method public boolean add(E);
+    method public void add(int, E);
+    method public boolean addAll(java.util.Collection<? extends E>);
+    method public boolean addAll(int, java.util.Collection<? extends E>);
+    method public int addAllAbsent(java.util.Collection<? extends E>);
+    method public boolean addIfAbsent(E);
+    method public void clear();
     method public java.lang.Object clone();
     method public boolean contains(java.lang.Object);
     method public boolean containsAll(java.util.Collection<?>);
     method public void forEach(java.util.function.Consumer<? super E>);
     method public E get(int);
-    method public int indexOf(E, int);
     method public int indexOf(java.lang.Object);
+    method public int indexOf(E, int);
     method public boolean isEmpty();
     method public java.util.Iterator<E> iterator();
-    method public int lastIndexOf(E, int);
     method public int lastIndexOf(java.lang.Object);
-    method public java.util.ListIterator<E> listIterator(int);
+    method public int lastIndexOf(E, int);
     method public java.util.ListIterator<E> listIterator();
-    method public synchronized E remove(int);
-    method public synchronized boolean remove(java.lang.Object);
-    method public synchronized boolean removeAll(java.util.Collection<?>);
-    method public synchronized boolean retainAll(java.util.Collection<?>);
-    method public synchronized E set(int, E);
+    method public java.util.ListIterator<E> listIterator(int);
+    method public E remove(int);
+    method public boolean remove(java.lang.Object);
+    method public boolean removeAll(java.util.Collection<?>);
+    method public boolean retainAll(java.util.Collection<?>);
+    method public E set(int, E);
     method public int size();
     method public java.util.List<E> subList(int, int);
     method public java.lang.Object[] toArray();
diff --git a/cmds/incident/Android.mk b/cmds/incident/Android.mk
new file mode 100644
index 0000000..e1c9b93
--- /dev/null
+++ b/cmds/incident/Android.mk
@@ -0,0 +1,48 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+        main.cpp
+
+LOCAL_MODULE := incident
+
+LOCAL_SHARED_LIBRARIES := \
+        libbase \
+        libbinder \
+        libcutils \
+        liblog \
+        libutils \
+        libincident
+
+LOCAL_CFLAGS += \
+        -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
+
+LOCAL_MODULE_CLASS := EXECUTABLES
+gen_src_dir := $(local-generated-sources-dir)
+
+gen := $(gen_src_dir)/incident_sections.cpp
+$(gen): $(HOST_OUT_EXECUTABLES)/incident-section-gen
+$(gen): PRIVATE_CUSTOM_TOOL = \
+    $(HOST_OUT_EXECUTABLES)/incident-section-gen > $@
+$(gen): $(HOST_OUT_EXECUTABLES)/incident-section-gen
+	$(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(gen)
+
+gen_src_dir:=
+gen:=
+
+include $(BUILD_EXECUTABLE)
diff --git a/cmds/incident/incident_sections.h b/cmds/incident/incident_sections.h
new file mode 100644
index 0000000..1972088
--- /dev/null
+++ b/cmds/incident/incident_sections.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCIDENT_SECTIONS_H
+#define INCIDENT_SECTIONS_H
+
+struct IncidentSection
+{
+    int id;
+    char const* name;
+};
+
+extern IncidentSection const INCIDENT_SECTIONS[];
+extern const int INCIDENT_SECTION_COUNT;
+
+#endif // INCIDENT_SECTIONS_H
diff --git a/cmds/incident/main.cpp b/cmds/incident/main.cpp
new file mode 100644
index 0000000..91b7c22
--- /dev/null
+++ b/cmds/incident/main.cpp
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#define LOG_TAG "incident"
+
+#include "incident_sections.h"
+
+#include <android/os/BnIncidentReportStatusListener.h>
+#include <android/os/IIncidentManager.h>
+#include <android/os/IncidentReportArgs.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <utils/Looper.h>
+
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+using namespace android;
+using namespace android::base;
+using namespace android::binder;
+using namespace android::os;
+
+// ================================================================================
+class StatusListener : public BnIncidentReportStatusListener {
+public:
+    StatusListener();
+    virtual ~StatusListener();
+
+    virtual Status onReportStarted();
+    virtual Status onReportSectionStatus(int32_t section, int32_t status);
+    virtual Status onReportServiceStatus(const String16& service, int32_t status);
+    virtual Status onReportFinished();
+    virtual Status onReportFailed();
+};
+
+StatusListener::StatusListener()
+{
+}
+
+StatusListener::~StatusListener()
+{
+}
+
+Status
+StatusListener::onReportStarted()
+{
+    return Status::ok();
+}
+
+Status
+StatusListener::onReportSectionStatus(int32_t section, int32_t status)
+{
+    fprintf(stderr, "section %d status %d\n", section, status);
+    return Status::ok();
+}
+
+Status
+StatusListener::onReportServiceStatus(const String16& service, int32_t status)
+{
+    fprintf(stderr, "service '%s' status %d\n", String8(service).string(), status);
+    return Status::ok();
+}
+
+Status
+StatusListener::onReportFinished()
+{
+    fprintf(stderr, "done\n");
+    exit(0);
+    return Status::ok();
+}
+
+Status
+StatusListener::onReportFailed()
+{
+    fprintf(stderr, "failed\n");
+    exit(1);
+    return Status::ok();
+}
+
+// ================================================================================
+static IncidentSection const*
+find_section(const char* name)
+{
+    size_t low = 0;
+    size_t high = INCIDENT_SECTION_COUNT - 1;
+
+    while (low <= high) {
+        size_t mid = (low + high) >> 1;
+        IncidentSection const* section = INCIDENT_SECTIONS + mid;
+
+        int cmp = strcmp(section->name, name);
+        if (cmp < 0) {
+            low = mid + 1;
+        } else if (cmp > 0) {
+            high = mid - 1;
+        } else {
+            return section;
+        }
+    }
+    return NULL;
+}
+
+// ================================================================================
+static void
+usage(FILE* out)
+{
+    fprintf(out, "usage: incident OPTIONS [SECTION...]\n");
+    fprintf(out, "\n");
+    fprintf(out, "Takes an incident report.\n");
+    fprintf(out, "\n");
+    fprintf(out, "OPTIONS\n");
+    fprintf(out, "  -b           (default) print the report to stdout (in proto format)\n");
+    fprintf(out, "  -d           send the report into dropbox\n");
+    fprintf(out, "\n");
+    fprintf(out, "  SECTION     the field numbers of the incident report fields to include\n");
+    fprintf(out, "\n");
+}
+
+int
+main(int argc, char** argv)
+{
+    Status status;
+    IncidentReportArgs args;
+    enum { DEST_DROPBOX, DEST_STDOUT } destination = DEST_STDOUT;
+
+    // Parse the args
+    int opt;
+    while ((opt = getopt(argc, argv, "bhd")) != -1) {
+        switch (opt) {
+            case 'b':
+                destination = DEST_STDOUT;
+                break;
+            case 'h':
+                usage(stdout);
+                return 0;
+            case 'd':
+                destination = DEST_DROPBOX;
+                break;
+            default:
+                usage(stderr);
+                return 1;
+        }
+    }
+
+    if (optind == argc) {
+        args.setAll(true);
+    } else {
+        for (int i=optind; i<argc; i++) {
+            const char* arg = argv[i];
+            char* end;
+            if (arg[0] != '\0') {
+                int section = strtol(arg, &end, 0);
+                if (*end == '\0') {
+                    args.addSection(section);
+                } else {
+                    IncidentSection const* ic = find_section(arg);
+                    if (ic == NULL) {
+                        fprintf(stderr, "Invalid section: %s\n", arg);
+                        return 1;
+                    }
+                    args.addSection(ic->id);
+                }
+            }
+        }
+    }
+
+
+
+    // Start the thread pool.
+    sp<ProcessState> ps(ProcessState::self());
+    ps->startThreadPool();
+    ps->giveThreadPoolName();
+
+    // Look up the service
+    sp<IIncidentManager> service = interface_cast<IIncidentManager>(
+            defaultServiceManager()->getService(android::String16("incident")));
+    if (service == NULL) {
+        fprintf(stderr, "Couldn't look up the incident service\n");
+        return 1;
+    }
+
+    // Construct the stream
+    int fds[2];
+    pipe(fds);
+
+    unique_fd readEnd(fds[0]);
+    unique_fd writeEnd(fds[1]);
+
+    if (destination == DEST_STDOUT) {
+        // Call into the service
+        sp<StatusListener> listener(new StatusListener());
+        status = service->reportIncidentToStream(args, listener, writeEnd);
+
+        if (!status.isOk()) {
+            fprintf(stderr, "reportIncident returned \"%s\"\n", status.toString8().string());
+        }
+
+        // Wait for the result and print out the data they send.
+        //IPCThreadState::self()->joinThreadPool();
+
+        while (true) {
+            int amt = splice(fds[0], NULL, STDOUT_FILENO, NULL, 4096, 0);
+            fprintf(stderr, "spliced %d bytes\n", amt);
+            if (amt < 0) {
+                return errno;
+            } else if (amt == 0) {
+                return 0;
+            }
+        }
+    } else {
+        status = service->reportIncident(args);
+        if (!status.isOk()) {
+            fprintf(stderr, "reportIncident returned \"%s\"\n", status.toString8().string());
+            return 1;
+        } else {
+            return 0;
+        }
+    }
+
+}
diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk
new file mode 100644
index 0000000..bacf672
--- /dev/null
+++ b/cmds/incidentd/Android.mk
@@ -0,0 +1,56 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := incidentd
+
+LOCAL_SRC_FILES := \
+        src/FdBuffer.cpp \
+        src/IncidentService.cpp \
+        src/Reporter.cpp \
+        src/Section.cpp \
+        src/main.cpp \
+        src/protobuf.cpp \
+        src/report_directory.cpp \
+        src/section_list.cpp
+
+LOCAL_CFLAGS += \
+        -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
+
+ifeq (debug,)
+    LOCAL_CFLAGS += \
+            -g -O0
+else
+    # optimize for size (protobuf glop can get big)
+    LOCAL_CFLAGS += \
+            -Os
+endif
+
+LOCAL_SHARED_LIBRARIES := \
+        libbase \
+        libbinder \
+        libcutils \
+        libincident \
+        liblog \
+        libselinux \
+        libservices \
+        libutils
+
+ifeq (BUILD_WITH_INCIDENTD_RC,true)
+LOCAL_INIT_RC := incidentd.rc
+endif
+
+include $(BUILD_EXECUTABLE)
diff --git a/cmds/incidentd/incidentd.rc b/cmds/incidentd/incidentd.rc
new file mode 100644
index 0000000..d11e3cf
--- /dev/null
+++ b/cmds/incidentd/incidentd.rc
@@ -0,0 +1,16 @@
+# Copyright (C) 2016 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.
+
+#service incidentd /system/bin/incidentd
+#    class main
diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp
new file mode 100644
index 0000000..527d7ee
--- /dev/null
+++ b/cmds/incidentd/src/FdBuffer.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#define LOG_TAG "incidentd"
+
+#include "FdBuffer.h"
+
+#include <cutils/log.h>
+#include <utils/SystemClock.h>
+
+#include <fcntl.h>
+#include <poll.h>
+#include <unistd.h>
+
+const ssize_t BUFFER_SIZE = 16 * 1024;
+const ssize_t MAX_BUFFER_COUNT = 256; // 4 MB max
+
+
+FdBuffer::FdBuffer()
+    :mBuffers(),
+     mStartTime(-1),
+     mFinishTime(-1),
+     mCurrentWritten(-1),
+     mTimedOut(false),
+     mTruncated(false)
+{
+}
+
+FdBuffer::~FdBuffer()
+{
+    const int N = mBuffers.size();
+    for (int i=0; i<N; i++) {
+        uint8_t* buf = mBuffers[i];
+        free(buf);
+    }
+}
+
+status_t
+FdBuffer::read(int fd, int64_t timeout)
+{
+    struct pollfd pfds = {
+        .fd = fd,
+        .events = POLLIN
+    };
+    mStartTime = uptimeMillis();
+
+    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
+
+    uint8_t* buf = NULL;
+    while (true) {
+        if (mCurrentWritten >= BUFFER_SIZE || mCurrentWritten < 0) {
+            if (mBuffers.size() == MAX_BUFFER_COUNT) {
+                mTruncated = true;
+                break;
+            }
+            buf = (uint8_t*)malloc(BUFFER_SIZE);
+            if (buf == NULL) {
+                return NO_MEMORY;
+            }
+            mBuffers.push_back(buf);
+            mCurrentWritten = 0;
+        }
+
+        int64_t remainingTime = (mStartTime + timeout) - uptimeMillis();
+        if (remainingTime <= 0) {
+            mTimedOut = true;
+            break;
+        }
+
+        int count = poll(&pfds, 1, remainingTime);
+        if (count == 0) {
+            mTimedOut = true;
+            break;
+        } else if (count < 0) {
+            return -errno;
+        } else {
+            if ((pfds.revents & POLLERR) != 0) {
+                return errno != 0 ? -errno : UNKNOWN_ERROR;
+            } else {
+                ssize_t amt = ::read(fd, buf + mCurrentWritten, BUFFER_SIZE - mCurrentWritten);
+                if (amt < 0) {
+                    if (errno == EAGAIN || errno == EWOULDBLOCK) {
+                        continue;
+                    } else {
+                        return -errno;
+                    }
+                } else if (amt == 0) {
+                    break;
+                }
+                mCurrentWritten += amt;
+            }
+        }
+    }
+
+    mFinishTime = uptimeMillis();
+    return NO_ERROR;
+}
+
+size_t
+FdBuffer::size()
+{
+    return ((mBuffers.size() - 1) * BUFFER_SIZE) + mCurrentWritten;
+}
+
+status_t
+FdBuffer::write(ReportRequestSet* reporter)
+{
+    const int N = mBuffers.size() - 1;
+    for (int i=0; i<N; i++) {
+        reporter->write(mBuffers[i], BUFFER_SIZE);
+    }
+    reporter->write(mBuffers[N], mCurrentWritten);
+    return NO_ERROR;
+}
+
+
diff --git a/cmds/incidentd/src/FdBuffer.h b/cmds/incidentd/src/FdBuffer.h
new file mode 100644
index 0000000..e12374f
--- /dev/null
+++ b/cmds/incidentd/src/FdBuffer.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FD_BUFFER_H
+#define FD_BUFFER_H
+
+#include "Reporter.h"
+
+#include <utils/Errors.h>
+
+#include <set>
+#include <vector>
+
+using namespace android;
+using namespace std;
+
+/**
+ * Reads a file into a buffer, and then writes that data to an FdSet.
+ */
+class FdBuffer
+{
+public:
+    FdBuffer();
+    ~FdBuffer();
+
+    /**
+     * Read the data until the timeout is hit or we hit eof.
+     * Returns NO_ERROR if there were no errors or if we timed out.
+     * Will mark the file O_NONBLOCK.
+     */
+    status_t read(int fd, int64_t timeoutMs);
+
+    /**
+     * Whether we timed out.
+     */
+    bool timedOut() { return mTimedOut; }
+
+    /**
+     * If more than 4 MB is read, we truncate the data and return success.
+     * Downstream tools must handle truncated incident reports as best as possible
+     * anyway because they could be cut off for a lot of reasons and it's best
+     * to get as much useful information out of the system as possible. If this
+     * happens, truncated() will return true so it can be marked. If the data is
+     * exactly 4 MB, truncated is still set. Sorry.
+     */
+    bool truncated() { return mTruncated; }
+
+    /**
+     * How much data was read.
+     */
+    size_t size();
+
+    /**
+     * Write the data that we recorded to the fd given.
+     */
+    status_t write(ReportRequestSet* requests);
+
+    /**
+     * How long the read took in milliseconds.
+     */
+    int64_t durationMs() { return mFinishTime - mStartTime; }
+
+private:
+    vector<uint8_t*> mBuffers;
+    int64_t mStartTime;
+    int64_t mFinishTime;
+    ssize_t mCurrentWritten;
+    bool mTimedOut;
+    bool mTruncated;
+};
+
+
+#endif // FD_BUFFER_H
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
new file mode 100644
index 0000000..7c6789e
--- /dev/null
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#define LOG_TAG "incidentd"
+
+#include "IncidentService.h"
+
+#include "Reporter.h"
+
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <cutils/log.h>
+#include <private/android_filesystem_config.h>
+#include <utils/Looper.h>
+
+#include <unistd.h>
+
+using namespace android;
+
+enum {
+    WHAT_RUN_REPORT = 1,
+    WHAT_SEND_BACKLOG_TO_DROPBOX = 2
+};
+
+//#define DEFAULT_BACKLOG_DELAY_NS (1000000000LL * 60 * 5)
+#define DEFAULT_BACKLOG_DELAY_NS (1000000000LL)
+
+// ================================================================================
+String16 const DUMP_PERMISSION("android.permission.DUMP");
+String16 const USAGE_STATS_PERMISSION("android.permission.PACKAGE_USAGE_STATS");
+
+static Status
+checkIncidentPermissions()
+{
+    if (!checkCallingPermission(DUMP_PERMISSION)) {
+        ALOGW("Calling pid %d and uid %d does not have permission: android.permission.DUMP",
+                IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid());
+        return Status::fromExceptionCode(Status::EX_SECURITY,
+                "Calling process does not have permission: android.permission.DUMP");
+    }
+    if (!checkCallingPermission(USAGE_STATS_PERMISSION)) {
+        ALOGW("Calling pid %d and uid %d does not have permission: android.permission.USAGE_STATS",
+                IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid());
+        return Status::fromExceptionCode(Status::EX_SECURITY,
+                "Calling process does not have permission: android.permission.USAGE_STATS");
+    }
+    return Status::ok();
+}
+
+
+// ================================================================================
+ReportRequestQueue::ReportRequestQueue()
+{
+}
+
+ReportRequestQueue::~ReportRequestQueue()
+{
+}
+
+void
+ReportRequestQueue::addRequest(const sp<ReportRequest>& request) 
+{
+    unique_lock<mutex> lock(mLock);
+    mQueue.push_back(request);
+}
+
+sp<ReportRequest>
+ReportRequestQueue::getNextRequest()
+{
+    unique_lock<mutex> lock(mLock);
+    if (mQueue.empty()) {
+        return NULL;
+    } else {
+        sp<ReportRequest> front(mQueue.front());
+        mQueue.pop_front();
+        return front;
+    }
+}
+
+
+// ================================================================================
+ReportHandler::ReportHandler(const sp<Looper>& handlerLooper, const sp<ReportRequestQueue>& queue)
+    :mBacklogDelay(DEFAULT_BACKLOG_DELAY_NS),
+     mHandlerLooper(handlerLooper),
+     mQueue(queue)
+{
+}
+
+ReportHandler::~ReportHandler()
+{
+}
+
+void
+ReportHandler::handleMessage(const Message& message)
+{
+    switch (message.what) {
+        case WHAT_RUN_REPORT:
+            run_report();
+            break;
+        case WHAT_SEND_BACKLOG_TO_DROPBOX:
+            send_backlog_to_dropbox();
+            break;
+    }
+}
+
+void
+ReportHandler::scheduleRunReport(const sp<ReportRequest>& request)
+{
+    mQueue->addRequest(request);
+    mHandlerLooper->removeMessages(this, WHAT_RUN_REPORT);
+    mHandlerLooper->sendMessage(this, Message(WHAT_RUN_REPORT));
+}
+
+void
+ReportHandler::scheduleSendBacklogToDropbox()
+{
+    unique_lock<mutex> lock(mLock);
+    mBacklogDelay = DEFAULT_BACKLOG_DELAY_NS;
+    schedule_send_backlog_to_dropbox_locked();
+}
+
+void
+ReportHandler::schedule_send_backlog_to_dropbox_locked()
+{
+    mHandlerLooper->removeMessages(this, WHAT_SEND_BACKLOG_TO_DROPBOX);
+    mHandlerLooper->sendMessageDelayed(mBacklogDelay, this,
+            Message(WHAT_SEND_BACKLOG_TO_DROPBOX));
+}
+
+void
+ReportHandler::run_report()
+{
+    sp<Reporter> reporter = new Reporter();
+
+    // Merge all of the requests into one that has all of the
+    // requested fields.
+    while (true) {
+        sp<ReportRequest> request = mQueue->getNextRequest();
+        if (request == NULL) {
+            break;
+        }
+        reporter->batch.add(request);
+        reporter->args.merge(request->args);
+    }
+
+    // Take the report, which might take a while. More requests might queue
+    // up while we're doing this, and we'll handle them in their next batch.
+    // TODO: We should further rate-limit the reports to no more than N per time-period.
+    Reporter::run_report_status_t reportStatus = reporter->runReport();
+    if (reportStatus == Reporter::REPORT_NEEDS_DROPBOX) {
+        unique_lock<mutex> lock(mLock);
+        schedule_send_backlog_to_dropbox_locked();
+    }
+}
+
+void
+ReportHandler::send_backlog_to_dropbox()
+{
+    if (Reporter::upload_backlog() == Reporter::REPORT_NEEDS_DROPBOX) {
+        // There was a failure. Exponential backoff.
+        unique_lock<mutex> lock(mLock);
+        mBacklogDelay *= 2;
+        ALOGI("Error sending to dropbox. Trying again in %lld minutes",
+                (mBacklogDelay / (1000000000LL * 60)));
+        schedule_send_backlog_to_dropbox_locked();
+    } else {
+        mBacklogDelay = DEFAULT_BACKLOG_DELAY_NS;
+    }
+}
+
+// ================================================================================
+IncidentService::IncidentService(const sp<Looper>& handlerLooper)
+    :mQueue(new ReportRequestQueue())
+{
+    mHandler = new ReportHandler(handlerLooper, mQueue);
+}
+
+IncidentService::~IncidentService()
+{
+}
+
+Status
+IncidentService::reportIncident(const IncidentReportArgs& args)
+{
+    ALOGI("reportIncident");
+
+    Status status = checkIncidentPermissions();
+    if (!status.isOk()) {
+        return status;
+    }
+
+    mHandler->scheduleRunReport(new ReportRequest(args, NULL, -1));
+
+    return Status::ok();
+}
+
+Status
+IncidentService::reportIncidentToStream(const IncidentReportArgs& args,
+            const sp<IIncidentReportStatusListener>& listener, const unique_fd& stream)
+{
+    ALOGI("reportIncidentToStream");
+
+    Status status = checkIncidentPermissions();
+    if (!status.isOk()) {
+        return status;
+    }
+
+    int fd = dup(stream.get());
+    if (fd < 0) {
+        return Status::fromStatusT(-errno);
+    }
+
+    mHandler->scheduleRunReport(new ReportRequest(args, listener, fd));
+
+    return Status::ok();
+}
+
+Status
+IncidentService::systemRunning()
+{
+    if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
+        return Status::fromExceptionCode(Status::EX_SECURITY,
+                "Only system uid can call systemRunning");
+    }
+    
+    // When system_server is up and running, schedule the dropbox task to run.
+    mHandler->scheduleSendBacklogToDropbox();
+
+    return Status::ok();
+}
+
diff --git a/cmds/incidentd/src/IncidentService.h b/cmds/incidentd/src/IncidentService.h
new file mode 100644
index 0000000..d6f33df
--- /dev/null
+++ b/cmds/incidentd/src/IncidentService.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCIDENT_SERVICE_H
+#define INCIDENT_SERVICE_H
+
+#include "Reporter.h"
+
+#include <android/os/BnIncidentManager.h>
+#include <utils/Looper.h>
+
+#include <deque>
+#include <mutex>
+
+using namespace android;
+using namespace android::base;
+using namespace android::binder;
+using namespace android::os;
+using namespace std;
+
+// ================================================================================
+class ReportRequestQueue : public virtual RefBase
+{
+public:
+    ReportRequestQueue();
+    virtual ~ReportRequestQueue();
+
+    void addRequest(const sp<ReportRequest>& request);
+    sp<ReportRequest> getNextRequest();
+
+private:
+    mutex mLock;
+    deque<sp<ReportRequest> > mQueue;
+};
+
+
+// ================================================================================
+class ReportHandler : public MessageHandler
+{
+public:
+    ReportHandler(const sp<Looper>& handlerLooper, const sp<ReportRequestQueue>& queue);
+    virtual ~ReportHandler();
+
+    virtual void handleMessage(const Message& message);
+
+    /**
+     * Adds a ReportRequest to the queue.
+     */
+    void scheduleRunReport(const sp<ReportRequest>& request);
+
+    /**
+     * Resets mBacklogDelay to the default and schedules sending
+     * the messages to dropbox.
+     */
+    void scheduleSendBacklogToDropbox();
+
+private:
+    mutex mLock;
+    nsecs_t mBacklogDelay;
+    sp<Looper> mHandlerLooper;
+    sp<ReportRequestQueue> mQueue;
+
+    /**
+     * Runs all of the reports that have been queued.
+     */
+    void run_report();
+
+    /**
+     * Schedules a dropbox task mBacklogDelay nanoseconds from now.
+     */
+    void schedule_send_backlog_to_dropbox_locked();
+
+    /**
+     * Sends the backlog to the dropbox service.
+     */
+    void send_backlog_to_dropbox();
+};
+
+
+// ================================================================================
+class IncidentService : public BnIncidentManager {
+public:
+    IncidentService(const sp<Looper>& handlerLooper);
+    virtual ~IncidentService();
+
+    virtual Status reportIncident(const IncidentReportArgs& args);
+
+    virtual Status reportIncidentToStream(const IncidentReportArgs& args,
+            const sp<IIncidentReportStatusListener>& listener, const unique_fd& stream);
+
+    virtual Status systemRunning();
+
+private:
+    sp<ReportRequestQueue> mQueue;
+    sp<ReportHandler> mHandler;
+};
+
+
+#endif // INCIDENT_SERVICE_H
diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp
new file mode 100644
index 0000000..1ecb291
--- /dev/null
+++ b/cmds/incidentd/src/Reporter.cpp
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#define LOG_TAG "incidentd"
+
+#include "Reporter.h"
+#include "protobuf.h"
+
+#include "report_directory.h"
+#include "section_list.h"
+
+#include <private/android_filesystem_config.h>
+#include <android/os/DropBoxManager.h>
+#include <utils/SystemClock.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <errno.h>
+
+/**
+ * The directory where the incident reports are stored.
+ */
+static const String8 INCIDENT_DIRECTORY("/data/incidents");
+
+static status_t
+write_all(int fd, uint8_t const* buf, size_t size)
+{
+    while (size > 0) {
+        ssize_t amt = ::write(fd, buf, size);
+        if (amt < 0) {
+            return -errno;
+        }
+        size -= amt;
+        buf += amt;
+    }
+    return NO_ERROR;
+}
+
+// ================================================================================
+ReportRequest::ReportRequest(const IncidentReportArgs& a,
+            const sp<IIncidentReportStatusListener> &l, int f)
+    :args(a),
+     listener(l),
+     fd(f),
+     err(NO_ERROR)
+{
+}
+
+ReportRequest::~ReportRequest()
+{
+}
+
+// ================================================================================
+ReportRequestSet::ReportRequestSet()
+    :mRequests(),
+     mWritableCount(0),
+     mMainFd(-1)
+{
+}
+
+ReportRequestSet::~ReportRequestSet()
+{
+}
+
+void
+ReportRequestSet::add(const sp<ReportRequest>& request)
+{
+    mRequests.push_back(request);
+    mWritableCount++;
+}
+
+void
+ReportRequestSet::setMainFd(int fd)
+{
+    mMainFd = fd;
+    mWritableCount++;
+}
+
+status_t
+ReportRequestSet::write(uint8_t const* buf, size_t size)
+{
+    status_t err = EBADF;
+
+    // The streaming ones
+    int const N = mRequests.size();
+    for (int i=N-1; i>=0; i--) {
+        sp<ReportRequest> request = mRequests[i];
+        if (request->fd >= 0 && request->err == NO_ERROR) {
+            err = write_all(request->fd, buf, size);
+            if (err != NO_ERROR) {
+                request->err = err;
+                mWritableCount--;
+            }
+        }
+    }
+
+    // The dropbox file
+    if (mMainFd >= 0) {
+        err = write_all(mMainFd, buf, size);
+        if (err != NO_ERROR) {
+            mMainFd = -1;
+            mWritableCount--;
+        }
+    }
+
+    // Return an error only when there are no FDs to write.
+    return mWritableCount > 0 ? NO_ERROR : err;
+}
+
+
+// ================================================================================
+Reporter::Reporter()
+    :args(),
+     batch()
+{
+    char buf[100];
+
+    // TODO: Make the max size smaller for user builds.
+    mMaxSize = 100 * 1024 * 1024;
+    mMaxCount = 100;
+
+    // There can't be two at the same time because it's on one thread.
+    mStartTime = time(NULL);
+    strftime(buf, sizeof(buf), "/incident-%Y%m%d-%H%M%S", localtime(&mStartTime));
+    mFilename = INCIDENT_DIRECTORY + buf;
+}
+
+Reporter::~Reporter()
+{
+}
+
+Reporter::run_report_status_t
+Reporter::runReport()
+{
+
+    status_t err = NO_ERROR;
+    bool needMainFd = false;
+    int mainFd = -1;
+
+    // See if we need the main file
+    for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
+        if ((*it)->fd < 0 && mainFd < 0) {
+            needMainFd = true;
+            break;
+        }
+    }
+    if (needMainFd) {
+        // Create the directory
+        err = create_directory(INCIDENT_DIRECTORY);
+        if (err != NO_ERROR) {
+            goto done;
+        }
+
+        // If there are too many files in the directory (for whatever reason),
+        // delete the oldest ones until it's under the limit. Doing this first
+        // does mean that we can go over, so the max size is not a hard limit.
+        clean_directory(INCIDENT_DIRECTORY, mMaxSize, mMaxCount);
+
+        // Open the file.
+        err = create_file(&mainFd);
+        if (err != NO_ERROR) {
+            goto done;
+        }
+
+        // Add to the set
+        batch.setMainFd(mainFd);
+    }
+
+    // Tell everyone that we're starting.
+    for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
+        if ((*it)->listener != NULL) {
+            (*it)->listener->onReportStarted();
+        }
+    }
+
+    // Write the incident headers
+    for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
+        const sp<ReportRequest> request = (*it);
+        const vector<vector<int8_t>>& headers = request->args.headers();
+
+        for (vector<vector<int8_t>>::const_iterator buf=headers.begin(); buf!=headers.end();
+                buf++) {
+            int fd = request->fd >= 0 ? request->fd : mainFd;
+
+            uint8_t buffer[20];
+            uint8_t* p = write_length_delimited_tag_header(buffer, FIELD_ID_INCIDENT_HEADER,
+                    buf->size());
+            write_all(fd, buffer, p-buffer);
+
+            write_all(fd, (uint8_t const*)buf->data(), buf->size());
+            // If there was an error now, there will be an error later and we will remove
+            // it from the list then.
+        }
+    }
+
+    // For each of the report fields, see if we need it, and if so, execute the command
+    // and report to those that care that we're doing it.
+    for (const Section** section=SECTION_LIST; *section; section++) {
+        const int id = (*section)->id;
+        ALOGD("Taking incident report section %d '%s'", id, (*section)->name.string());
+
+        if (this->args.containsSection(id)) {
+            // Notify listener of starting
+            for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
+                if ((*it)->listener != NULL && (*it)->args.containsSection(id)) {
+                    (*it)->listener->onReportSectionStatus(id,
+                            IIncidentReportStatusListener::STATUS_STARTING);
+                }
+            }
+
+            // Execute - go get the data and write it into the file descriptors.
+            err = (*section)->Execute(&batch);
+            if (err != NO_ERROR) {
+                ALOGW("Incident section %s (%d) failed. Stopping report.",
+                        (*section)->name.string(), id);
+                goto done;
+            }
+
+            // Notify listener of starting
+            for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
+                if ((*it)->listener != NULL && (*it)->args.containsSection(id)) {
+                    (*it)->listener->onReportSectionStatus(id,
+                            IIncidentReportStatusListener::STATUS_FINISHED);
+                }
+            }
+        }
+    }
+
+done:
+    // Close the file.
+    if (mainFd >= 0) {
+        close(mainFd);
+    }
+
+    // Tell everyone that we're done.
+    for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
+        if ((*it)->listener != NULL) {
+            if (err == NO_ERROR) {
+                (*it)->listener->onReportFinished();
+            } else {
+                (*it)->listener->onReportFailed();
+            }
+        }
+    }
+
+    // Put the report into dropbox.
+    if (needMainFd && err == NO_ERROR) {
+        sp<DropBoxManager> dropbox = new DropBoxManager();
+        Status status = dropbox->addFile(String16("incident"), mFilename, 0);
+        ALOGD("Incident report done. dropbox status=%s\n", status.toString8().string());
+        if (!status.isOk()) {
+            return REPORT_NEEDS_DROPBOX;
+        }
+
+        // If the status was ok, delete the file. If not, leave it around until the next
+        // boot or the next checkin. If the directory gets too big older files will
+        // be rotated out.
+        unlink(mFilename.c_str());
+    }
+
+    return REPORT_FINISHED;
+}
+
+/**
+ * Create our output file and set the access permissions to -rw-rw----
+ */
+status_t
+Reporter::create_file(int* fd)
+{
+    const char* filename = mFilename.c_str();
+
+    *fd = open(filename, O_CREAT | O_TRUNC | O_RDWR, 0660);
+    if (*fd < 0) {
+        ALOGE("Couldn't open incident file: %s (%s)", filename, strerror(errno));
+        return -errno;
+    }
+
+    // Override umask. Not super critical. If it fails go on with life.
+    chmod(filename, 0660);
+
+    if (chown(filename, AID_SYSTEM, AID_SYSTEM)) {
+        ALOGE("Unable to change ownership of incident file %s: %s\n", filename, strerror(errno));
+        status_t err = -errno;
+        unlink(mFilename.c_str());
+        return err;
+    }
+
+    return NO_ERROR;
+}
+
+// ================================================================================
+Reporter::run_report_status_t
+Reporter::upload_backlog()
+{
+    DIR* dir;
+    struct dirent* entry;
+    struct stat st;
+
+    if ((dir = opendir(INCIDENT_DIRECTORY.string())) == NULL) {
+        ALOGE("Couldn't open incident directory: %s", INCIDENT_DIRECTORY.string());
+        return REPORT_NEEDS_DROPBOX;
+    }
+
+    String8 dirbase(INCIDENT_DIRECTORY + "/");
+    sp<DropBoxManager> dropbox = new DropBoxManager();
+
+    // Enumerate, count and add up size
+    while ((entry = readdir(dir)) != NULL) {
+        if (entry->d_name[0] == '.') {
+            continue;
+        }
+        String8 filename = dirbase + entry->d_name;
+        if (stat(filename.string(), &st) != 0) {
+            ALOGE("Unable to stat file %s", filename.string());
+            continue;
+        }
+        if (!S_ISREG(st.st_mode)) {
+            continue;
+        }
+
+        Status status = dropbox->addFile(String16("incident"), filename.string(), 0);
+        ALOGD("Incident report done. dropbox status=%s\n", status.toString8().string());
+        if (!status.isOk()) {
+            return REPORT_NEEDS_DROPBOX;
+        }
+
+        // If the status was ok, delete the file. If not, leave it around until the next
+        // boot or the next checkin. If the directory gets too big older files will
+        // be rotated out.
+        unlink(filename.string());
+    }
+
+    closedir(dir);
+
+    return REPORT_FINISHED;
+}
+
diff --git a/cmds/incidentd/src/Reporter.h b/cmds/incidentd/src/Reporter.h
new file mode 100644
index 0000000..5b86561
--- /dev/null
+++ b/cmds/incidentd/src/Reporter.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef REPORTER_H
+#define REPORTER_H
+
+#include <android/os/IIncidentReportStatusListener.h>
+#include <android/os/IncidentReportArgs.h>
+
+#include <string>
+#include <vector>
+
+#include <time.h>
+
+using namespace android;
+using namespace android::os;
+using namespace std;
+
+// ================================================================================
+struct ReportRequest : public virtual RefBase
+{
+    IncidentReportArgs args;
+    sp<IIncidentReportStatusListener> listener;
+    int fd;
+    status_t err;
+
+    ReportRequest(const IncidentReportArgs& args,
+            const sp<IIncidentReportStatusListener> &listener, int fd);
+    virtual ~ReportRequest();
+};
+
+// ================================================================================
+class ReportRequestSet
+{
+public:
+    ReportRequestSet();
+    ~ReportRequestSet();
+
+    void add(const sp<ReportRequest>& request);
+    void setMainFd(int fd);
+
+    // Write to all of the fds for the requests. If a write fails, it stops
+    // writing to that fd and returns NO_ERROR. When we are out of fds to write
+    // to it returns an error.
+    status_t write(uint8_t const* buf, size_t size);
+
+    typedef vector<sp<ReportRequest>>::iterator iterator;
+
+    iterator begin() { return mRequests.begin(); }
+    iterator end() { return mRequests.end(); }
+
+private:
+    vector<sp<ReportRequest>> mRequests;
+    int mWritableCount;
+    int mMainFd;
+};
+
+// ================================================================================
+class Reporter : public virtual RefBase
+{
+public:
+    enum run_report_status_t {
+        REPORT_FINISHED = 0,
+        REPORT_NEEDS_DROPBOX = 1
+    };
+
+    IncidentReportArgs args;
+    ReportRequestSet batch;
+
+    Reporter();
+    virtual ~Reporter();
+
+    // Run the report as described in the batch and args parameters.
+    run_report_status_t runReport();
+
+    static run_report_status_t upload_backlog();
+
+private:
+    string mFilename;
+    off_t mMaxSize;
+    size_t mMaxCount;
+    time_t mStartTime;
+
+    status_t create_file(int* fd);
+};
+
+
+#endif // REPORTER_H
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
new file mode 100644
index 0000000..fac299e
--- /dev/null
+++ b/cmds/incidentd/src/Section.cpp
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#define LOG_TAG "incidentd"
+
+#include "Section.h"
+#include "protobuf.h"
+
+#include <binder/IServiceManager.h>
+#include <mutex>
+
+using namespace std;
+
+const int64_t REMOTE_CALL_TIMEOUT_MS = 10 * 1000; // 10 seconds
+
+// ================================================================================
+Section::Section(int i)
+    :id(i)
+{
+}
+
+Section::~Section()
+{
+}
+
+status_t
+Section::WriteHeader(ReportRequestSet* requests, size_t size) const
+{
+    ssize_t amt;
+    uint8_t buf[20];
+    uint8_t* p = write_length_delimited_tag_header(buf, this->id, size);
+    return requests->write(buf, p-buf);
+}
+
+// ================================================================================
+struct WorkerThreadData : public virtual RefBase
+{
+    const WorkerThreadSection* section;
+    int fds[2];
+
+    // Lock protects these fields
+    mutex lock;
+    bool workerDone;
+    status_t workerError;
+
+    WorkerThreadData(const WorkerThreadSection* section);
+    virtual ~WorkerThreadData();
+
+    int readFd() { return fds[0]; }
+    int writeFd() { return fds[1]; }
+};
+
+WorkerThreadData::WorkerThreadData(const WorkerThreadSection* sec)
+    :section(sec),
+     workerDone(false),
+     workerError(NO_ERROR)
+{
+    fds[0] = -1;
+    fds[1] = -1;
+}
+
+WorkerThreadData::~WorkerThreadData()
+{
+}
+
+// ================================================================================
+WorkerThreadSection::WorkerThreadSection(int id)
+    :Section(id)
+{
+}
+
+WorkerThreadSection::~WorkerThreadSection()
+{
+}
+
+static void*
+worker_thread_func(void* cookie)
+{
+    WorkerThreadData* data = (WorkerThreadData*)cookie;
+    status_t err = data->section->BlockingCall(data->writeFd());
+
+    {
+        unique_lock<mutex> lock(data->lock);
+        data->workerDone = true;
+        data->workerError = err;
+    }
+
+    close(data->writeFd());
+    data->decStrong(data->section);
+    // data might be gone now. don't use it after this point in this thread.
+    return NULL;
+}
+
+status_t
+WorkerThreadSection::Execute(ReportRequestSet* requests) const
+{
+    status_t err = NO_ERROR;
+    pthread_t thread;
+    pthread_attr_t attr;
+    bool timedOut = false;
+    FdBuffer buffer;
+
+    // Data shared between this thread and the worker thread.
+    sp<WorkerThreadData> data = new WorkerThreadData(this);
+
+    // Create the pipe
+    err = pipe(data->fds);
+    if (err != 0) {
+        return -errno;
+    }
+
+    // The worker thread needs a reference and we can't let the count go to zero
+    // if that thread is slow to start.
+    data->incStrong(this);
+
+    // Create the thread
+    err = pthread_attr_init(&attr);
+    if (err != 0) {
+        return -err;
+    }
+    // TODO: Do we need to tweak thread priority?
+    err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+    if (err != 0) {
+        pthread_attr_destroy(&attr);
+        return -err;
+    }
+    err = pthread_create(&thread, &attr, worker_thread_func, (void*)data.get());
+    if (err != 0) {
+        pthread_attr_destroy(&attr);
+        return -err;
+    }
+    pthread_attr_destroy(&attr);
+
+    // Loop reading until either the timeout or the worker side is done (i.e. eof).
+    err = buffer.read(data->readFd(), REMOTE_CALL_TIMEOUT_MS);
+    if (err != NO_ERROR) {
+        // TODO: Log this error into the incident report.
+        ALOGW("WorkerThreadSection '%s' reader failed with error '%s'", this->name.string(),
+                strerror(-err));
+    }
+
+    // Done with the read fd. The worker thread closes the write one so
+    // we never race and get here first.
+    close(data->readFd());
+
+    // If the worker side is finished, then return its error (which may overwrite
+    // our possible error -- but it's more interesting anyway).  If not, then we timed out.
+    {
+        unique_lock<mutex> lock(data->lock);
+        if (!data->workerDone) {
+            // We timed out
+            timedOut = true;
+        } else {
+            if (data->workerError != NO_ERROR) {
+                err = data->workerError;
+                // TODO: Log this error into the incident report.
+                ALOGW("WorkerThreadSection '%s' worker failed with error '%s'", this->name.string(),
+                        strerror(-err));
+            }
+        }
+    }
+
+    if (timedOut || buffer.timedOut()) {
+        ALOGW("WorkerThreadSection '%s' timed out", this->name.string());
+        return NO_ERROR;
+    }
+
+    if (buffer.truncated()) {
+        // TODO: Log this into the incident report.
+    }
+
+    // TODO: There was an error with the command or buffering. Report that.  For now
+    // just exit with a log messasge.
+    if (err != NO_ERROR) {
+        ALOGW("WorkerThreadSection '%s' failed with error '%s'", this->name.string(),
+                strerror(-err));
+        return NO_ERROR;
+    }
+
+    // Write the data that was collected
+    ALOGD("section '%s' wrote %zd bytes in %d ms", name.string(), buffer.size(),
+            (int)buffer.durationMs());
+    WriteHeader(requests, buffer.size());
+    err = buffer.write(requests);
+    if (err != NO_ERROR) {
+        ALOGW("WorkerThreadSection '%s' failed writing: '%s'", this->name.string(), strerror(-err));
+        return err;
+    }
+
+    return NO_ERROR;
+}
+
+// ================================================================================
+CommandSection::CommandSection(int id, const char* first, ...)
+    :Section(id)
+{
+    va_list args;
+    int count = 0;
+
+    va_start(args, first);
+    while (va_arg(args, const char*) != NULL) {
+        count++;
+    }
+    va_end(args);
+
+    mCommand = (const char**)malloc(sizeof(const char*) * count);
+
+    mCommand[0] = first;
+    name = first;
+    name += " ";
+    va_start(args, first);
+    for (int i=0; i<count; i++) {
+        const char* arg = va_arg(args, const char*); 
+        mCommand[i+1] = arg;
+        if (arg != NULL) {
+            name += va_arg(args, const char*);
+            name += " ";
+        }
+    }
+    va_end(args);
+}
+
+CommandSection::~CommandSection()
+{
+}
+
+status_t
+CommandSection::Execute(ReportRequestSet* /*requests*/) const
+{
+    return NO_ERROR;
+}
+
+// ================================================================================
+DumpsysSection::DumpsysSection(int id, const char* service, ...)
+    :WorkerThreadSection(id),
+     mService(service)
+{
+    name = "dumpsys ";
+    name += service;
+
+    va_list args;
+    va_start(args, service);
+    while (true) {
+        const char* arg = va_arg(args, const char*); 
+        if (arg == NULL) {
+            break;
+        }
+        mArgs.add(String16(arg));
+        name += " ";
+        name += arg;
+    }
+    va_end(args);
+}
+
+DumpsysSection::~DumpsysSection()
+{
+}
+
+status_t
+DumpsysSection::BlockingCall(int pipeWriteFd) const
+{
+    // checkService won't wait for the service to show up like getService will.
+    sp<IBinder> service = defaultServiceManager()->checkService(mService);
+    
+    if (service == NULL) {
+        // Returning an error interrupts the entire incident report, so just
+        // log the failure.
+        // TODO: have a meta record inside the report that would log this
+        // failure inside the report, because the fact that we can't find
+        // the service is good data in and of itself. This is running in
+        // another thread so lock that carefully...
+        ALOGW("DumpsysSection: Can't lookup service: %s", String8(mService).string());
+        return NO_ERROR;
+    }
+
+    service->dump(pipeWriteFd, mArgs);
+
+    return NO_ERROR;
+}
diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h
new file mode 100644
index 0000000..35740e9
--- /dev/null
+++ b/cmds/incidentd/src/Section.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SECTIONS_H
+#define SECTIONS_H
+
+#include "FdBuffer.h"
+
+#include <utils/String8.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+using namespace android;
+
+/**
+ * Base class for sections
+ */
+class Section
+{
+public:
+    int id;
+    String8 name;
+
+    Section(int id);
+    virtual ~Section();
+
+    virtual status_t Execute(ReportRequestSet* requests) const = 0;
+
+    status_t WriteHeader(ReportRequestSet* requests, size_t size) const;
+};
+
+/**
+ * Section that reads in a file.
+ */
+class FileSection : public Section
+{
+public:
+    FileSection(int id, const char* filename);
+    virtual ~FileSection();
+
+    virtual status_t Execute(ReportRequestSet* requests) const;
+
+private:
+    const char* mFilename;
+};
+
+/**
+ * Base class for sections that call a command that might need a timeout.
+ */
+class WorkerThreadSection : public Section
+{
+public:
+    WorkerThreadSection(int id);
+    virtual ~WorkerThreadSection();
+
+    virtual status_t Execute(ReportRequestSet* requests) const;
+
+    virtual status_t BlockingCall(int pipeWriteFd) const = 0;
+};
+
+/**
+ * Section that forks and execs a command, and puts stdout as the section.
+ */
+class CommandSection : public Section
+{
+public:
+    CommandSection(int id, const char* first, ...);
+    virtual ~CommandSection();
+
+    virtual status_t Execute(ReportRequestSet* requests) const;
+
+private:
+    const char** mCommand;
+};
+
+/**
+ * Section that calls dumpsys on a system service.
+ */
+class DumpsysSection : public WorkerThreadSection
+{
+public:
+    DumpsysSection(int id, const char* service, ...);
+    virtual ~DumpsysSection();
+
+    virtual status_t BlockingCall(int pipeWriteFd) const;
+
+private:
+    String16 mService;
+    Vector<String16> mArgs;
+};
+
+#endif // SECTIONS_H
+
diff --git a/cmds/incidentd/src/main.cpp b/cmds/incidentd/src/main.cpp
new file mode 100644
index 0000000..3a7511d
--- /dev/null
+++ b/cmds/incidentd/src/main.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#define LOG_TAG "incidentd"
+
+#include "IncidentService.h"
+
+#include <binder/IInterface.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <binder/Status.h>
+#include <cutils/log.h>
+#include <utils/Looper.h>
+#include <utils/StrongPointer.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+using namespace android;
+
+// ================================================================================
+int
+main(int /*argc*/, char** /*argv*/)
+{
+    // Set up the looper
+    sp<Looper> looper(Looper::prepare(0 /* opts */));
+
+    // Set up the binder
+    sp<ProcessState> ps(ProcessState::self());
+    ps->setThreadPoolMaxThreadCount(1); // everything is oneway, let it queue and save ram
+    ps->startThreadPool();
+    ps->giveThreadPoolName();
+    IPCThreadState::self()->disableBackgroundScheduling(true);
+
+    // Create the service
+    android::sp<IncidentService> service = new IncidentService(looper);
+    if (defaultServiceManager()->addService(String16("incident"), service) != 0) {
+        ALOGE("Failed to add service");
+        return -1;
+    }
+
+    // Loop forever -- the reports run on this thread in a handler, and the
+    // binder calls remain responsive in their pool of one thread.
+    while (true) {
+        looper->pollAll(-1 /* timeoutMillis */);
+    }
+    ALOGW("incidentd escaped from its loop.");
+
+    return 1;
+}
diff --git a/cmds/incidentd/src/protobuf.cpp b/cmds/incidentd/src/protobuf.cpp
new file mode 100644
index 0000000..cb864fd
--- /dev/null
+++ b/cmds/incidentd/src/protobuf.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 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 "protobuf.h"
+
+uint8_t*
+write_raw_varint(uint8_t* buf, uint32_t val)
+{
+    uint8_t* p = buf;
+    while (true) {
+        if ((val & ~0x7F) == 0) {
+            *p++ = (uint8_t)val;
+            return p;
+        } else {
+            *p++ = (uint8_t)((val & 0x7F) | 0x80);
+            val >>= 7;
+        }
+    }
+}
+
+uint8_t*
+write_length_delimited_tag_header(uint8_t* buf, uint32_t fieldId, size_t size)
+{
+    buf = write_raw_varint(buf, (fieldId << 3) | 2);
+    buf = write_raw_varint(buf, size);
+    return buf;
+}
+
diff --git a/cmds/incidentd/src/protobuf.h b/cmds/incidentd/src/protobuf.h
new file mode 100644
index 0000000..a243998
--- /dev/null
+++ b/cmds/incidentd/src/protobuf.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef PROTOBUF_H
+#define PROTOBUF_H
+
+#include <stdint.h>
+
+/**
+ * Write a varint into the buffer. Return the next position to write at.
+ * There must be 10 bytes in the buffer. The same as EncodedBuffer.writeRawVarint32
+ */
+uint8_t* write_raw_varint(uint8_t* buf, uint32_t val);
+
+/**
+ * Write a protobuf WIRE_TYPE_LENGTH_DELIMITED header. Return the next position to write at.
+ * There must be 20 bytes in the buffer.
+ */
+uint8_t* write_length_delimited_tag_header(uint8_t* buf, uint32_t fieldId, size_t size);
+
+enum {
+    // IncidentProto.header
+    FIELD_ID_INCIDENT_HEADER = 1
+};
+
+#endif // PROTOBUF_H
+
diff --git a/cmds/incidentd/src/report_directory.cpp b/cmds/incidentd/src/report_directory.cpp
new file mode 100644
index 0000000..f60b8ac
--- /dev/null
+++ b/cmds/incidentd/src/report_directory.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#define LOG_TAG "incidentd"
+
+#include "report_directory.h"
+
+#include <cutils/log.h>
+#include <private/android_filesystem_config.h>
+#include <utils/String8.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <libgen.h>
+#include <unistd.h>
+
+#include <vector>
+
+using namespace android;
+using namespace std;
+
+status_t
+create_directory(const char* directory)
+{
+    struct stat st;
+    status_t err = NO_ERROR;
+    char* dir = strdup(directory);
+
+    // Skip first slash
+    char* d = dir + 1;
+
+    // Create directories, assigning them to the system user
+    bool last = false;
+    while (!last) {
+        d = strchr(d, '/');
+        if (d != NULL) {
+            *d = '\0';
+        } else {
+            last = true;
+        }
+        if (stat(dir, &st) == 0) {
+            if (!S_ISDIR(st.st_mode)) {
+                err = ALREADY_EXISTS;
+                goto done;
+            }
+        } else {
+            if (mkdir(dir, 0770)) {
+                ALOGE("No incident reports today. "
+                        "Unable to create incident report dir %s: %s", dir,
+                        strerror(errno));
+                err = -errno;
+                goto done;
+            }
+            if (chmod(dir, 0770)) {
+                ALOGE("No incident reports today. "
+                        "Unable to set permissions for incident report dir %s: %s", dir,
+                        strerror(errno));
+                err = -errno;
+                goto done;
+            }
+            if (chown(dir, AID_SYSTEM, AID_SYSTEM)) {
+                ALOGE("No incident reports today. Unable to change ownership of dir %s: %s\n",
+                        dir, strerror(errno));
+                err = -errno;
+                goto done;
+            }
+        }
+        if (!last) {
+            *d++ = '/';
+        }
+    }
+
+    // Ensure that the final directory is owned by the system with 0770. If it isn't
+    // we won't write into it.
+    if (stat(directory, &st) != 0) {
+        ALOGE("No incident reports today. Can't stat: %s", directory);
+        err = -errno;
+        goto done;
+    }
+    if ((st.st_mode & 0777) != 0770) {
+        ALOGE("No incident reports today. Mode is %0o on report directory %s",
+                st.st_mode, directory);
+        err = BAD_VALUE;
+        goto done;
+    }
+    if (st.st_uid != AID_SYSTEM || st.st_gid != AID_SYSTEM) {
+        ALOGE("No incident reports today. Owner is %d and group is %d on report directory %s",
+                st.st_uid, st.st_gid, directory);
+        err = BAD_VALUE;
+        goto done;
+    }
+
+done:
+    free(dir);
+    return err;
+}
+
+static bool
+stat_mtime_cmp(const pair<String8,struct stat>& a, const pair<String8,struct stat>& b)
+{
+    return a.second.st_mtime < b.second.st_mtime;
+}
+
+void
+clean_directory(const char* directory, off_t maxSize, size_t maxCount)
+{
+    DIR* dir;
+    struct dirent* entry;
+    struct stat st;
+
+    vector<pair<String8,struct stat>> files;
+
+    if ((dir = opendir(directory)) == NULL) {
+        ALOGE("Couldn't open incident directory: %s", directory);
+        return;
+    }
+
+    String8 dirbase(String8(directory) + "/");
+
+    off_t totalSize = 0;
+    size_t totalCount = 0;
+
+    // Enumerate, count and add up size
+    while ((entry = readdir(dir)) != NULL) {
+        if (entry->d_name[0] == '.') {
+            continue;
+        }
+        String8 filename = dirbase + entry->d_name;
+        if (stat(filename.string(), &st) != 0) {
+            ALOGE("Unable to stat file %s", filename.string());
+            continue;
+        }
+        if (!S_ISREG(st.st_mode)) {
+            continue;
+        }
+        files.push_back(pair<String8,struct stat>(filename, st));
+
+        totalSize += st.st_size;
+        totalCount++;
+    }
+
+    closedir(dir);
+
+    // Count or size is less than max, then we're done.
+    if (totalSize < maxSize && totalCount < maxCount) {
+        return;
+    }
+
+    // Oldest files first.
+    sort(files.begin(), files.end(), stat_mtime_cmp);
+
+    // Remove files until we're under our limits.
+    for (vector<pair<String8,struct stat>>::iterator it = files.begin();
+            it != files.end() && totalSize >= maxSize && totalCount >= maxCount; it++) {
+        remove(it->first.string());
+        totalSize -= it->second.st_size;
+        totalCount--;
+    }
+}
diff --git a/cmds/incidentd/src/report_directory.h b/cmds/incidentd/src/report_directory.h
new file mode 100644
index 0000000..bed4f86
--- /dev/null
+++ b/cmds/incidentd/src/report_directory.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DIRECTORY_CLEANER_H
+#define DIRECTORY_CLEANER_H
+
+#include <utils/Errors.h>
+
+#include <sys/types.h>
+
+using namespace android;
+
+status_t create_directory(const char* directory);
+void clean_directory(const char* directory, off_t maxSize, size_t maxCount);
+
+#endif // DIRECTORY_CLEANER_H
diff --git a/cmds/incidentd/src/section_list.cpp b/cmds/incidentd/src/section_list.cpp
new file mode 100644
index 0000000..b6112ed
--- /dev/null
+++ b/cmds/incidentd/src/section_list.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 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 "section_list.h"
+
+//using namespace android::util;
+
+/**
+ * This is the mapping of section IDs to the commands that are run to get those commands.
+ */
+const Section* SECTION_LIST[] = {
+    new DumpsysSection(3000,
+            "fingerprint", "--proto", "--incident", NULL),
+    NULL
+};
+
diff --git a/cmds/incidentd/src/section_list.h b/cmds/incidentd/src/section_list.h
new file mode 100644
index 0000000..c977519
--- /dev/null
+++ b/cmds/incidentd/src/section_list.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SECTION_LIST_H
+#define SECTION_LIST_H
+
+#include "Section.h"
+
+/**
+ * This is the mapping of section IDs to the commands that are run to get those commands.
+ */
+extern const Section* SECTION_LIST[];
+
+#endif // SECTION_LIST_H
+
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 2cdda3d..2ccfe0e 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -45,6 +45,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ParceledListSlice;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -2019,7 +2020,29 @@
     }
 
     /**
-     * Updates the aspect ratio of the current picture-in-picture activity.
+     * Requests to the system that the activity can be automatically put into picture-in-picture
+     * mode when the user leaves the activity causing it normally to be hidden.  Generally, this
+     * happens when another task is brought to the forground or the task containing this activity
+     * is moved to the background.  This is a *not* a guarantee that the activity will actually be
+     * put in picture-in-picture mode, and depends on a number of factors, including whether there
+     * is already something in picture-in-picture.
+     *
+     * @param enterPictureInPictureOnMoveToBg whether or not this activity can automatically enter
+     *                                        picture-in-picture
+     */
+    public void enterPictureInPictureModeOnMoveToBackground(
+            boolean enterPictureInPictureOnMoveToBg) {
+        try {
+            ActivityManagerNative.getDefault().enterPictureInPictureModeOnMoveToBackground(mToken,
+                    enterPictureInPictureOnMoveToBg);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Updates the aspect ratio of the current picture-in-picture activity if this activity is
+     * already in picture-in-picture mode, or sets it to be used later if
+     * {@link #enterPictureInPictureModeOnMoveToBackground(boolean)} is requested.
      *
      * @param aspectRatio the new aspect ratio of the picture-in-picture.
      */
@@ -2031,23 +2054,19 @@
     }
 
     /**
-     * Requests to the system that the activity can be automatically put into picture-in-picture
-     * mode when the user leaves the activity causing it normally to be hidden.  This is a *not*
-     * a guarantee that the activity will actually be put in picture-in-picture mode, and depends
-     * on a number of factors, including whether there is already something in picture-in-picture.
+     * Updates the set of user actions associated with the picture-in-picture activity.
      *
-     * If {@param enterPictureInPictureOnMoveToBg} is true, then you may also call
-     * {@link #setPictureInPictureAspectRatio(float)} to specify the aspect ratio to automatically
-     * enter picture-in-picture with.
-     *
-     * @param enterPictureInPictureOnMoveToBg whether or not this activity can automatically enter
-     *                                     picture-in-picture
+     * @param actions the new actions for picture-in-picture (can be null to reset the set of
+     *                actions).  The maximum number of actions that will be displayed on this device
+     *                is defined by {@link ActivityManager#getMaxNumPictureInPictureActions()}.
      */
-    public void enterPictureInPictureModeOnMoveToBackground(
-            boolean enterPictureInPictureOnMoveToBg) {
+    public void setPictureInPictureActions(List<RemoteAction> actions) {
         try {
-            ActivityManagerNative.getDefault().enterPictureInPictureModeOnMoveToBackground(mToken,
-                    enterPictureInPictureOnMoveToBg);
+            if (actions == null) {
+                actions = new ArrayList<>();
+            }
+            ActivityManagerNative.getDefault().setPictureInPictureActions(mToken,
+                    new ParceledListSlice<RemoteAction>(actions));
         } catch (RemoteException e) {
         }
     }
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index f6f5472..761da35 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -87,8 +87,9 @@
 
     private static int gMaxRecentTasks = -1;
 
+    private static final int NUM_ALLOWED_PIP_ACTIONS = 3;
+
     private final Context mContext;
-    private final Handler mHandler;
 
     private static volatile boolean sSystemReady = false;
 
@@ -491,7 +492,6 @@
 
     /*package*/ ActivityManager(Context context, Handler handler) {
         mContext = context;
-        mHandler = handler;
     }
 
     /**
@@ -1002,6 +1002,24 @@
     }
 
     /**
+     * Returns true if the system supports split screen multi-window.
+     * @hide
+     */
+    static public boolean supportsSplitScreenMultiWindow() {
+        return supportsMultiWindow()
+                && Resources.getSystem().getBoolean(
+                    com.android.internal.R.bool.config_supportsSplitScreenMultiWindow);
+    }
+
+    /**
+     * Return the maximum number of actions that will be displayed in the picture-in-picture UI when
+     * the user interacts with the activity currently in picture-in-picture mode.
+     */
+    public static int getMaxNumPictureInPictureActions() {
+        return NUM_ALLOWED_PIP_ACTIONS;
+    }
+
+    /**
      * Information you can set and retrieve about the current activity within the recent task list.
      */
     public static class TaskDescription implements Parcelable {
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 913edfd8..87700dc 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -28,6 +28,8 @@
 
 import com.android.internal.app.IVoiceInteractor;
 
+import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -62,6 +64,36 @@
     public static final int APP_TRANSITION_TIMEOUT = 3;
 
     /**
+     * Class to hold deferred properties to apply for picture-in-picture for a given activity.
+     */
+    public static class PictureInPictureArguments {
+        /**
+         * The expected aspect ratio of the picture-in-picture.
+         */
+        public float aspectRatio;
+
+        /**
+         * The set of actions that are associated with this activity when in picture in picture.
+         */
+        public List<RemoteAction> userActions = new ArrayList<>();
+
+        public void dump(PrintWriter pw, String prefix) {
+            pw.println(prefix + "aspectRatio=" + aspectRatio);
+            if (userActions.isEmpty()) {
+                pw.println(prefix + "  userActions=[]");
+            } else {
+                pw.println(prefix + "  userActions=[");
+                for (int i = 0; i < userActions.size(); i++) {
+                    RemoteAction action = userActions.get(i);
+                    pw.print(prefix + "    Action[" + i + "]: ");
+                    action.dump("", pw);
+                }
+                pw.println(prefix + "  ]");
+            }
+        }
+    }
+
+    /**
      * Grant Uri permissions from one app to another. This method only extends
      * permission grants if {@code callingUid} has permission to them.
      */
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 9a92764..e34fabc 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -113,9 +113,6 @@
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 import android.renderscript.RenderScriptCacheDir;
-import android.system.Os;
-import android.system.OsConstants;
-import android.system.ErrnoException;
 import android.webkit.WebView;
 
 import com.android.internal.annotations.GuardedBy;
@@ -138,11 +135,9 @@
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
 import java.net.InetAddress;
 import java.text.DateFormat;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -653,8 +648,6 @@
         }
     }
 
-    private native void dumpGraphicsInfo(FileDescriptor fd);
-
     private class ApplicationThread extends IApplicationThread.Stub {
         private static final String DB_INFO_FORMAT = "  %8s %8s %14s %14s  %s";
 
@@ -1195,7 +1188,7 @@
 
         @Override
         public void dumpGfxInfo(ParcelFileDescriptor pfd, String[] args) {
-            dumpGraphicsInfo(pfd.getFileDescriptor());
+            nDumpGraphicsInfo(pfd.getFileDescriptor());
             WindowManagerGlobal.getInstance().dumpGfxInfo(pfd.getFileDescriptor(), args);
             IoUtils.closeQuietly(pfd);
         }
@@ -6240,4 +6233,8 @@
 
         throw new RuntimeException("Main thread loop unexpectedly exited");
     }
+
+    // ------------------ Regular JNI ------------------------
+
+    private native void nDumpGraphicsInfo(FileDescriptor fd);
 }
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index a10fffe..fcc6e3d 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -474,6 +474,7 @@
     void enterPictureInPictureModeOnMoveToBackground(in IBinder token,
             boolean enterPictureInPictureOnMoveToBg);
     void setPictureInPictureAspectRatio(in IBinder token, float aspectRatio);
+    void setPictureInPictureActions(in IBinder token, in ParceledListSlice actions);
     void activityRelaunched(in IBinder token);
     IBinder getUriPermissionOwnerForActivity(in IBinder activityToken);
     /**
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index c1e2072..119b055 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3985,19 +3985,6 @@
             return new Builder(builderContext, n);
         }
 
-        private static Class<? extends Style> getNotificationStyleClass(String templateClass) {
-            Class<? extends Style>[] classes = new Class[] {
-                    BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
-                    DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
-                    MessagingStyle.class };
-            for (Class<? extends Style> innerClass : classes) {
-                if (templateClass.equals(innerClass.getName())) {
-                    return innerClass;
-                }
-            }
-            return null;
-        }
-
         /**
          * @deprecated Use {@link #build()} instead.
          */
@@ -4175,6 +4162,23 @@
     }
 
     /**
+     * @hide
+     */
+    @SystemApi
+    public static Class<? extends Style> getNotificationStyleClass(String templateClass) {
+        Class<? extends Style>[] classes = new Class[] {
+                BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
+                DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
+                MessagingStyle.class };
+        for (Class<? extends Style> innerClass : classes) {
+            if (templateClass.equals(innerClass.getName())) {
+                return innerClass;
+            }
+        }
+        return null;
+    }
+
+    /**
      * An object that can apply a rich notification style to a {@link Notification.Builder}
      * object.
      */
diff --git a/core/java/android/app/QueuedWork.java b/core/java/android/app/QueuedWork.java
index 6ee4780..0ae8505 100644
--- a/core/java/android/app/QueuedWork.java
+++ b/core/java/android/app/QueuedWork.java
@@ -16,86 +16,197 @@
 
 package android.app;
 
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.LinkedList;
 
 /**
- * Internal utility class to keep track of process-global work that's
- * outstanding and hasn't been finished yet.
+ * Internal utility class to keep track of process-global work that's outstanding and hasn't been
+ * finished yet.
  *
- * This was created for writing SharedPreference edits out
- * asynchronously so we'd have a mechanism to wait for the writes in
- * Activity.onPause and similar places, but we may use this mechanism
- * for other things in the future.
+ * New work will be {@link #queue queued}.
+ *
+ * It is possible to add 'finisher'-runnables that are {@link #waitToFinish guaranteed to be run}.
+ * This is used to make sure the work has been finished.
+ *
+ * This was created for writing SharedPreference edits out asynchronously so we'd have a mechanism
+ * to wait for the writes in Activity.onPause and similar places, but we may use this mechanism for
+ * other things in the future.
+ *
+ * The queued asynchronous work is performed on a separate, dedicated thread.
  *
  * @hide
  */
 public class QueuedWork {
+    private static final String LOG_TAG = QueuedWork.class.getSimpleName();
 
-    // The set of Runnables that will finish or wait on any async
-    // activities started by the application.
-    private static final ConcurrentLinkedQueue<Runnable> sPendingWorkFinishers =
-            new ConcurrentLinkedQueue<Runnable>();
+    /** Delay for delayed runnables */
+    private static final long DELAY = 50;
 
-    private static ExecutorService sSingleThreadExecutor = null; // lazy, guarded by class
+    /** Lock for this class */
+    private static final Object sLock = new Object();
+
+    /** Finishers {@link #addFinisher added} and not yet {@link #removeFinisher removed} */
+    @GuardedBy("sLock")
+    private static final LinkedList<Runnable> sFinishers = new LinkedList<>();
+
+    /** {@link #getHandler() Lazily} created handler */
+    @GuardedBy("sLock")
+    private static Handler sHandler = null;
+
+    /** Work queued via {@link #queue} */
+    @GuardedBy("sLock")
+    private static final LinkedList<Runnable> sWork = new LinkedList<>();
+
+    /** If new work can be delayed or not */
+    @GuardedBy("sLock")
+    private static boolean sCanDelay = true;
 
     /**
-     * Returns a single-thread Executor shared by the entire process,
-     * creating it if necessary.
+     * Lazily create a handler on a separate thread.
+     *
+     * @return the handler
      */
-    public static ExecutorService singleThreadExecutor() {
-        synchronized (QueuedWork.class) {
-            if (sSingleThreadExecutor == null) {
-                // TODO: can we give this single thread a thread name?
-                sSingleThreadExecutor = Executors.newSingleThreadExecutor();
+    private static Handler getHandler() {
+        synchronized (sLock) {
+            if (sHandler == null) {
+                HandlerThread handlerThread = new HandlerThread("queued-work-looper",
+                        Process.THREAD_PRIORITY_BACKGROUND);
+                handlerThread.start();
+
+                sHandler = new QueuedWorkHandler(handlerThread.getLooper());
             }
-            return sSingleThreadExecutor;
+            return sHandler;
         }
     }
 
     /**
-     * Add a runnable to finish (or wait for) a deferred operation
-     * started in this context earlier.  Typically finished by e.g.
-     * an Activity#onPause.  Used by SharedPreferences$Editor#startCommit().
+     * Add a finisher-runnable to wait for {@link #queue asynchronously processed work}.
      *
-     * Note that this doesn't actually start it running.  This is just
-     * a scratch set for callers doing async work to keep updated with
-     * what's in-flight.  In the common case, caller code
-     * (e.g. SharedPreferences) will pretty quickly call remove()
-     * after an add().  The only time these Runnables are run is from
-     * waitToFinish(), below.
+     * Used by SharedPreferences$Editor#startCommit().
+     *
+     * Note that this doesn't actually start it running.  This is just a scratch set for callers
+     * doing async work to keep updated with what's in-flight. In the common case, caller code
+     * (e.g. SharedPreferences) will pretty quickly call remove() after an add(). The only time
+     * these Runnables are run is from {@link #waitToFinish}.
+     *
+     * @param finisher The runnable to add as finisher
      */
-    public static void add(Runnable finisher) {
-        sPendingWorkFinishers.add(finisher);
-    }
-
-    public static void remove(Runnable finisher) {
-        sPendingWorkFinishers.remove(finisher);
+    public static void addFinisher(Runnable finisher) {
+        synchronized (sLock) {
+            sFinishers.add(finisher);
+        }
     }
 
     /**
-     * Finishes or waits for async operations to complete.
-     * (e.g. SharedPreferences$Editor#startCommit writes)
+     * Remove a previously {@link #addFinisher added} finisher-runnable.
      *
-     * Is called from the Activity base class's onPause(), after
-     * BroadcastReceiver's onReceive, after Service command handling,
-     * etc.  (so async work is never lost)
+     * @param finisher The runnable to remove.
+     */
+    public static void removeFinisher(Runnable finisher) {
+        synchronized (sLock) {
+            sFinishers.remove(finisher);
+        }
+    }
+
+    /**
+     * Trigger queued work to be processed immediately. The queued work is processed on a separate
+     * thread asynchronous. While doing that run and process all finishers on this thread. The
+     * finishers can be implemented in a way to check weather the queued work is finished.
+     *
+     * Is called from the Activity base class's onPause(), after BroadcastReceiver's onReceive,
+     * after Service command handling, etc. (so async work is never lost)
      */
     public static void waitToFinish() {
-        Runnable toFinish;
-        while ((toFinish = sPendingWorkFinishers.poll()) != null) {
-            toFinish.run();
+        Handler handler = getHandler();
+
+        synchronized (sLock) {
+            if (handler.hasMessages(QueuedWorkHandler.MSG_RUN)) {
+                // Force the delayed work to be processed now
+                handler.removeMessages(QueuedWorkHandler.MSG_RUN);
+                handler.sendEmptyMessage(QueuedWorkHandler.MSG_RUN);
+            }
+
+            // We should not delay any work as this might delay the finishers
+            sCanDelay = false;
+        }
+
+        try {
+            while (true) {
+                Runnable finisher;
+
+                synchronized (sLock) {
+                    finisher = sFinishers.poll();
+                }
+
+                if (finisher == null) {
+                    break;
+                }
+
+                finisher.run();
+            }
+        } finally {
+            sCanDelay = true;
         }
     }
-    
+
     /**
-     * Returns true if there is pending work to be done.  Note that the
-     * result is out of data as soon as you receive it, so be careful how you
-     * use it.
+     * Queue a work-runnable for processing asynchronously.
+     *
+     * @param work The new runnable to process
+     * @param shouldDelay If the message should be delayed
+     */
+    public static void queue(Runnable work, boolean shouldDelay) {
+        Handler handler = getHandler();
+
+        synchronized (sLock) {
+            sWork.add(work);
+
+            if (shouldDelay && sCanDelay) {
+                handler.sendEmptyMessageDelayed(QueuedWorkHandler.MSG_RUN, DELAY);
+            } else {
+                handler.sendEmptyMessage(QueuedWorkHandler.MSG_RUN);
+            }
+        }
+    }
+
+    /**
+     * @return True iff there is any {@link #queue async work queued}.
      */
     public static boolean hasPendingWork() {
-        return !sPendingWorkFinishers.isEmpty();
+        synchronized (sLock) {
+            return !sWork.isEmpty();
+        }
     }
-    
+
+    private static class QueuedWorkHandler extends Handler {
+        static final int MSG_RUN = 1;
+
+        QueuedWorkHandler(Looper looper) {
+            super(looper);
+        }
+
+        public void handleMessage(Message msg) {
+            if (msg.what == MSG_RUN) {
+                LinkedList<Runnable> work;
+
+                synchronized (sWork) {
+                    work = (LinkedList<Runnable>) sWork.clone();
+                    sWork.clear();
+
+                    // Remove all msg-s as all work will be processed now
+                    removeMessages(MSG_RUN);
+                }
+
+                work.forEach(Runnable::run);
+            }
+        }
+    }
 }
diff --git a/core/java/android/app/RemoteAction.aidl b/core/java/android/app/RemoteAction.aidl
new file mode 100644
index 0000000..2f9f1cc
--- /dev/null
+++ b/core/java/android/app/RemoteAction.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2016, 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.app;
+
+parcelable RemoteAction;
diff --git a/core/java/android/app/RemoteAction.java b/core/java/android/app/RemoteAction.java
new file mode 100644
index 0000000..a37680f
--- /dev/null
+++ b/core/java/android/app/RemoteAction.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2016 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.app;
+
+import android.annotation.NonNull;
+import android.graphics.drawable.Icon;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.PrintWriter;
+
+/**
+ * Represents a remote action that can be called from another process.  The action can have an
+ * associated visualization including metadata like an icon or title.
+ */
+public final class RemoteAction implements Parcelable {
+
+    /**
+     * Interface definition for a callback to be invoked when an action is invoked.
+     */
+    public interface OnActionListener {
+        /**
+         * Called when the associated action is invoked.
+         *
+         * @param action The action that was invoked.
+         */
+        void onAction(RemoteAction action);
+    }
+
+    private static final String TAG = "RemoteAction";
+
+    private static final int MESSAGE_ACTION_INVOKED = 1;
+
+    private final Icon mIcon;
+    private final CharSequence mTitle;
+    private final CharSequence mContentDescription;
+    private OnActionListener mActionCallback;
+    private final Messenger mMessenger;
+
+    RemoteAction(Parcel in) {
+        mIcon = Icon.CREATOR.createFromParcel(in);
+        mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+        mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+        mMessenger = in.readParcelable(Messenger.class.getClassLoader());
+    }
+
+    public RemoteAction(@NonNull Icon icon, @NonNull CharSequence title,
+            @NonNull CharSequence contentDescription, @NonNull OnActionListener callback) {
+        if (icon == null || title == null || contentDescription == null || callback == null) {
+            throw new IllegalArgumentException("Expected icon, title, content description and " +
+                    "action callback");
+        }
+        mIcon = icon;
+        mTitle = title;
+        mContentDescription = contentDescription;
+        mActionCallback = callback;
+        mMessenger = new Messenger(new Handler() {
+            @Override
+            public void handleMessage(Message msg) {
+                switch (msg.what) {
+                    case MESSAGE_ACTION_INVOKED:
+                        mActionCallback.onAction(RemoteAction.this);
+                        break;
+                }
+            }
+        });
+    }
+
+    /**
+     * Return an icon representing the action.
+     */
+    public @NonNull Icon getIcon() {
+        return mIcon;
+    }
+
+    /**
+     * Return an title representing the action.
+     */
+    public @NonNull CharSequence getTitle() {
+        return mTitle;
+    }
+
+    /**
+     * Return a content description representing the action.
+     */
+    public @NonNull CharSequence getContentDescription() {
+        return mContentDescription;
+    }
+
+    /**
+     * Sends a message that the action was invoked.
+     * @hide
+     */
+    public void sendActionInvoked() {
+        Message m = Message.obtain();
+        m.what = MESSAGE_ACTION_INVOKED;
+        try {
+            mMessenger.send(m);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Could not send action-invoked", e);
+        }
+    }
+
+    @Override
+    public RemoteAction clone() {
+        return new RemoteAction(mIcon, mTitle, mContentDescription, mActionCallback);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        mIcon.writeToParcel(out, 0);
+        TextUtils.writeToParcel(mTitle, out, flags);
+        TextUtils.writeToParcel(mContentDescription, out, flags);
+        out.writeParcelable(mMessenger, flags);
+    }
+
+    public void dump(String prefix, PrintWriter pw) {
+        pw.print(prefix);
+        pw.print("title=" + mTitle);
+        pw.print(" contentDescription=" + mContentDescription);
+        pw.print(" icon=" + mIcon);
+        pw.println();
+    }
+
+    public static final Parcelable.Creator<RemoteAction> CREATOR =
+            new Parcelable.Creator<RemoteAction>() {
+                public RemoteAction createFromParcel(Parcel in) {
+                    return new RemoteAction(in);
+                }
+                public RemoteAction[] newArray(int size) {
+                    return new RemoteAction[size];
+                }
+            };
+}
\ No newline at end of file
diff --git a/core/java/android/app/SharedPreferencesImpl.java b/core/java/android/app/SharedPreferencesImpl.java
index f273cd8..023b4f3 100644
--- a/core/java/android/app/SharedPreferencesImpl.java
+++ b/core/java/android/app/SharedPreferencesImpl.java
@@ -54,23 +54,34 @@
 final class SharedPreferencesImpl implements SharedPreferences {
     private static final String TAG = "SharedPreferencesImpl";
     private static final boolean DEBUG = false;
+    private static final Object CONTENT = new Object();
 
     // Lock ordering rules:
-    //  - acquire SharedPreferencesImpl.this before EditorImpl.this
-    //  - acquire mWritingToDiskLock before EditorImpl.this
+    //  - acquire SharedPreferencesImpl.mLock before EditorImpl.mLock
+    //  - acquire mWritingToDiskLock before EditorImpl.mLock
 
     private final File mFile;
     private final File mBackupFile;
     private final int mMode;
-
-    private Map<String, Object> mMap;     // guarded by 'this'
-    private int mDiskWritesInFlight = 0;  // guarded by 'this'
-    private boolean mLoaded = false;      // guarded by 'this'
-    private long mStatTimestamp;          // guarded by 'this'
-    private long mStatSize;               // guarded by 'this'
-
+    private final Object mLock = new Object();
     private final Object mWritingToDiskLock = new Object();
-    private static final Object mContent = new Object();
+
+    @GuardedBy("mLock")
+    private Map<String, Object> mMap;
+
+    @GuardedBy("mLock")
+    private int mDiskWritesInFlight = 0;
+
+    @GuardedBy("mLock")
+    private boolean mLoaded = false;
+
+    @GuardedBy("mLock")
+    private long mStatTimestamp;
+
+    @GuardedBy("mLock")
+    private long mStatSize;
+
+    @GuardedBy("mLock")
     private final WeakHashMap<OnSharedPreferenceChangeListener, Object> mListeners =
             new WeakHashMap<OnSharedPreferenceChangeListener, Object>();
 
@@ -92,7 +103,7 @@
     }
 
     private void startLoadFromDisk() {
-        synchronized (this) {
+        synchronized (mLock) {
             mLoaded = false;
         }
         new Thread("SharedPreferencesImpl-load") {
@@ -103,7 +114,7 @@
     }
 
     private void loadFromDisk() {
-        synchronized (SharedPreferencesImpl.this) {
+        synchronized (mLock) {
             if (mLoaded) {
                 return;
             }
@@ -138,7 +149,7 @@
             /* ignore */
         }
 
-        synchronized (SharedPreferencesImpl.this) {
+        synchronized (mLock) {
             mLoaded = true;
             if (map != null) {
                 mMap = map;
@@ -147,7 +158,7 @@
             } else {
                 mMap = new HashMap<>();
             }
-            notifyAll();
+            mLock.notifyAll();
         }
     }
 
@@ -156,7 +167,7 @@
     }
 
     void startReloadIfChangedUnexpectedly() {
-        synchronized (this) {
+        synchronized (mLock) {
             // TODO: wait for any pending writes to disk?
             if (!hasFileChangedUnexpectedly()) {
                 return;
@@ -168,7 +179,7 @@
     // Has the file changed out from under us?  i.e. writes that
     // we didn't instigate.
     private boolean hasFileChangedUnexpectedly() {
-        synchronized (this) {
+        synchronized (mLock) {
             if (mDiskWritesInFlight > 0) {
                 // If we know we caused it, it's not unexpected.
                 if (DEBUG) Log.d(TAG, "disk write in flight, not unexpected.");
@@ -188,19 +199,19 @@
             return true;
         }
 
-        synchronized (this) {
+        synchronized (mLock) {
             return mStatTimestamp != stat.st_mtime || mStatSize != stat.st_size;
         }
     }
 
     public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
-        synchronized(this) {
-            mListeners.put(listener, mContent);
+        synchronized(mLock) {
+            mListeners.put(listener, CONTENT);
         }
     }
 
     public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
-        synchronized(this) {
+        synchronized(mLock) {
             mListeners.remove(listener);
         }
     }
@@ -214,14 +225,14 @@
         }
         while (!mLoaded) {
             try {
-                wait();
+                mLock.wait();
             } catch (InterruptedException unused) {
             }
         }
     }
 
     public Map<String, ?> getAll() {
-        synchronized (this) {
+        synchronized (mLock) {
             awaitLoadedLocked();
             //noinspection unchecked
             return new HashMap<String, Object>(mMap);
@@ -230,7 +241,7 @@
 
     @Nullable
     public String getString(String key, @Nullable String defValue) {
-        synchronized (this) {
+        synchronized (mLock) {
             awaitLoadedLocked();
             String v = (String)mMap.get(key);
             return v != null ? v : defValue;
@@ -239,7 +250,7 @@
 
     @Nullable
     public Set<String> getStringSet(String key, @Nullable Set<String> defValues) {
-        synchronized (this) {
+        synchronized (mLock) {
             awaitLoadedLocked();
             Set<String> v = (Set<String>) mMap.get(key);
             return v != null ? v : defValues;
@@ -247,28 +258,28 @@
     }
 
     public int getInt(String key, int defValue) {
-        synchronized (this) {
+        synchronized (mLock) {
             awaitLoadedLocked();
             Integer v = (Integer)mMap.get(key);
             return v != null ? v : defValue;
         }
     }
     public long getLong(String key, long defValue) {
-        synchronized (this) {
+        synchronized (mLock) {
             awaitLoadedLocked();
             Long v = (Long)mMap.get(key);
             return v != null ? v : defValue;
         }
     }
     public float getFloat(String key, float defValue) {
-        synchronized (this) {
+        synchronized (mLock) {
             awaitLoadedLocked();
             Float v = (Float)mMap.get(key);
             return v != null ? v : defValue;
         }
     }
     public boolean getBoolean(String key, boolean defValue) {
-        synchronized (this) {
+        synchronized (mLock) {
             awaitLoadedLocked();
             Boolean v = (Boolean)mMap.get(key);
             return v != null ? v : defValue;
@@ -276,7 +287,7 @@
     }
 
     public boolean contains(String key) {
-        synchronized (this) {
+        synchronized (mLock) {
             awaitLoadedLocked();
             return mMap.containsKey(key);
         }
@@ -290,7 +301,7 @@
         //      context.getSharedPreferences(..).edit().putString(..).apply()
         //
         // ... all without blocking.
-        synchronized (this) {
+        synchronized (mLock) {
             awaitLoadedLocked();
         }
 
@@ -299,70 +310,86 @@
 
     // Return value from EditorImpl#commitToMemory()
     private static class MemoryCommitResult {
-        public long memoryStateGeneration;
-        public List<String> keysModified;  // may be null
-        public Set<OnSharedPreferenceChangeListener> listeners;  // may be null
-        public Map<?, ?> mapToWriteToDisk;
-        public final CountDownLatch writtenToDiskLatch = new CountDownLatch(1);
-        public volatile boolean writeToDiskResult = false;
+        final long memoryStateGeneration;
+        @Nullable final List<String> keysModified;
+        @Nullable final Set<OnSharedPreferenceChangeListener> listeners;
+        final Map<String, Object> mapToWriteToDisk;
+        final CountDownLatch writtenToDiskLatch = new CountDownLatch(1);
 
-        public void setDiskWriteResult(boolean result) {
+        @GuardedBy("mWritingToDiskLock")
+        volatile boolean writeToDiskResult = false;
+
+        private MemoryCommitResult(long memoryStateGeneration, @Nullable List<String> keysModified,
+                @Nullable Set<OnSharedPreferenceChangeListener> listeners,
+                Map<String, Object> mapToWriteToDisk) {
+            this.memoryStateGeneration = memoryStateGeneration;
+            this.keysModified = keysModified;
+            this.listeners = listeners;
+            this.mapToWriteToDisk = mapToWriteToDisk;
+        }
+
+        void setDiskWriteResult(boolean result) {
             writeToDiskResult = result;
             writtenToDiskLatch.countDown();
         }
     }
 
     public final class EditorImpl implements Editor {
+        private final Object mLock = new Object();
+
+        @GuardedBy("mLock")
         private final Map<String, Object> mModified = Maps.newHashMap();
+
+        @GuardedBy("mLock")
         private boolean mClear = false;
 
         public Editor putString(String key, @Nullable String value) {
-            synchronized (this) {
+            synchronized (mLock) {
                 mModified.put(key, value);
                 return this;
             }
         }
         public Editor putStringSet(String key, @Nullable Set<String> values) {
-            synchronized (this) {
+            synchronized (mLock) {
                 mModified.put(key,
                         (values == null) ? null : new HashSet<String>(values));
                 return this;
             }
         }
         public Editor putInt(String key, int value) {
-            synchronized (this) {
+            synchronized (mLock) {
                 mModified.put(key, value);
                 return this;
             }
         }
         public Editor putLong(String key, long value) {
-            synchronized (this) {
+            synchronized (mLock) {
                 mModified.put(key, value);
                 return this;
             }
         }
         public Editor putFloat(String key, float value) {
-            synchronized (this) {
+            synchronized (mLock) {
                 mModified.put(key, value);
                 return this;
             }
         }
         public Editor putBoolean(String key, boolean value) {
-            synchronized (this) {
+            synchronized (mLock) {
                 mModified.put(key, value);
                 return this;
             }
         }
 
         public Editor remove(String key) {
-            synchronized (this) {
+            synchronized (mLock) {
                 mModified.put(key, this);
                 return this;
             }
         }
 
         public Editor clear() {
-            synchronized (this) {
+            synchronized (mLock) {
                 mClear = true;
                 return this;
             }
@@ -379,12 +406,12 @@
                     }
                 };
 
-            QueuedWork.add(awaitCommit);
+            QueuedWork.addFinisher(awaitCommit);
 
             Runnable postWriteRunnable = new Runnable() {
                     public void run() {
                         awaitCommit.run();
-                        QueuedWork.remove(awaitCommit);
+                        QueuedWork.removeFinisher(awaitCommit);
                     }
                 };
 
@@ -399,8 +426,12 @@
 
         // Returns true if any changes were made
         private MemoryCommitResult commitToMemory() {
-            MemoryCommitResult mcr = new MemoryCommitResult();
-            synchronized (SharedPreferencesImpl.this) {
+            long memoryStateGeneration;
+            List<String> keysModified = null;
+            Set<OnSharedPreferenceChangeListener> listeners = null;
+            Map<String, Object> mapToWriteToDisk;
+
+            synchronized (SharedPreferencesImpl.this.mLock) {
                 // We optimistically don't make a deep copy until
                 // a memory commit comes in when we're already
                 // writing to disk.
@@ -411,17 +442,16 @@
                     // noinspection unchecked
                     mMap = new HashMap<String, Object>(mMap);
                 }
-                mcr.mapToWriteToDisk = mMap;
+                mapToWriteToDisk = mMap;
                 mDiskWritesInFlight++;
 
                 boolean hasListeners = mListeners.size() > 0;
                 if (hasListeners) {
-                    mcr.keysModified = new ArrayList<String>();
-                    mcr.listeners =
-                            new HashSet<OnSharedPreferenceChangeListener>(mListeners.keySet());
+                    keysModified = new ArrayList<String>();
+                    listeners = new HashSet<OnSharedPreferenceChangeListener>(mListeners.keySet());
                 }
 
-                synchronized (this) {
+                synchronized (mLock) {
                     boolean changesMade = false;
 
                     if (mClear) {
@@ -455,7 +485,7 @@
 
                         changesMade = true;
                         if (hasListeners) {
-                            mcr.keysModified.add(k);
+                            keysModified.add(k);
                         }
                     }
 
@@ -465,10 +495,11 @@
                         mCurrentMemoryStateGeneration++;
                     }
 
-                    mcr.memoryStateGeneration = mCurrentMemoryStateGeneration;
+                    memoryStateGeneration = mCurrentMemoryStateGeneration;
                 }
             }
-            return mcr;
+            return new MemoryCommitResult(memoryStateGeneration, keysModified, listeners,
+                    mapToWriteToDisk);
         }
 
         public boolean commit() {
@@ -534,7 +565,7 @@
                     synchronized (mWritingToDiskLock) {
                         writeToFile(mcr, isFromSyncCommit);
                     }
-                    synchronized (SharedPreferencesImpl.this) {
+                    synchronized (mLock) {
                         mDiskWritesInFlight--;
                     }
                     if (postWriteRunnable != null) {
@@ -547,7 +578,7 @@
         // the current thread.
         if (isFromSyncCommit) {
             boolean wasEmpty = false;
-            synchronized (SharedPreferencesImpl.this) {
+            synchronized (mLock) {
                 wasEmpty = mDiskWritesInFlight == 1;
             }
             if (wasEmpty) {
@@ -557,10 +588,10 @@
         }
 
         if (DEBUG) {
-            Log.d(TAG, "added " + mcr.memoryStateGeneration + " -> " + mFile.getName());
+            Log.d(TAG, "queued " + mcr.memoryStateGeneration + " -> " + mFile.getName());
         }
 
-        QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable);
+        QueuedWork.queue(writeToDiskRunnable, !isFromSyncCommit);
     }
 
     private static FileOutputStream createFileOutputStream(File file) {
@@ -597,7 +628,7 @@
                 if (isFromSyncCommit) {
                     needsWrite = true;
                 } else {
-                    synchronized (this) {
+                    synchronized (mLock) {
                         // No need to persist intermediate states. Just wait for the latest state to
                         // be persisted.
                         if (mCurrentMemoryStateGeneration == mcr.memoryStateGeneration) {
@@ -647,7 +678,7 @@
             ContextImpl.setFilePermissionsFromMode(mFile.getPath(), mMode, 0);
             try {
                 final StructStat stat = Os.stat(mFile.getPath());
-                synchronized (this) {
+                synchronized (mLock) {
                     mStatTimestamp = stat.st_mtime;
                     mStatSize = stat.st_size;
                 }
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index e1bd93e..6bddfba 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -96,6 +96,7 @@
 import android.os.IPowerManager;
 import android.os.IRecoverySystem;
 import android.os.IUserManager;
+import android.os.IncidentManager;
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RecoverySystem;
@@ -774,6 +775,13 @@
                 return new ContextHubManager(ctx.getOuterContext(),
                   ctx.mMainThread.getHandler().getLooper());
             }});
+
+        registerService(Context.INCIDENT_SERVICE, IncidentManager.class,
+                new CachedServiceFetcher<IncidentManager>() {
+            @Override
+            public IncidentManager createService(ContextImpl ctx) throws ServiceNotFoundException {
+                return new IncidentManager(ctx);
+            }});
     }
 
     /**
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 07030d9..b641e63 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -44,6 +44,7 @@
 import android.net.ProxyInfo;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.Parcelable;
 import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteCallback;
@@ -152,6 +153,7 @@
      * <li>{@link #EXTRA_PROVISIONING_MAIN_COLOR}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_SKIP_USER_CONSENT}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_DISCLAIMERS}, optional</li>
      * </ul>
      *
      * <p>When managed provisioning has completed, broadcasts are sent to the application specified
@@ -233,6 +235,7 @@
      * <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_LOGO_URI}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_MAIN_COLOR}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_DISCLAIMERS}, optional</li>
      * </ul>
      *
      * <p>When device owner provisioning has completed, an intent of the type
@@ -804,7 +807,7 @@
      * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})</li>
      * </ul>
      *
-     * <p> It is the responsability of the caller to provide an image with a reasonable
+     * <p> It is the responsibility of the caller to provide an image with a reasonable
      * pixed density for the device.
      *
      * <p> If a content: URI is passed, the intent should have the flag
@@ -818,6 +821,58 @@
             "android.app.extra.PROVISIONING_LOGO_URI";
 
     /**
+     * A {@link Bundle}[] extra consisting of list of disclaimer headers and disclaimer contents.
+     * Each {@link Bundle} must have both {@link #EXTRA_PROVISIONING_DISCLAIMER_HEADER}
+     * as disclaimer header, and {@link #EXTRA_PROVISIONING_DISCLAIMER_CONTENT} as disclaimer
+     * content.
+     *
+     * <p> The extra typically contains one disclaimer from the company of mobile device
+     * management application (MDM), and one disclaimer from the organization.
+     *
+     * <p> Call {@link Bundle#putParcelableArray(String, Parcelable[])} to put the {@link Bundle}[]
+     *
+     * <p> Maximum 3 key-value pairs can be specified. The rest will be ignored.
+     *
+     * <p> Use in an intent with action {@link #ACTION_PROVISION_MANAGED_PROFILE} or
+     * {@link #ACTION_PROVISION_MANAGED_DEVICE}
+     */
+    public static final String EXTRA_PROVISIONING_DISCLAIMERS =
+            "android.app.extra.PROVISIONING_DISCLAIMERS";
+
+    /**
+     * A String extra of localized disclaimer header.
+     *
+     * <p> The extra is typically the company name of mobile device management application (MDM)
+     * or the organization name.
+     *
+     * <p> Use in Bundle {@link #EXTRA_PROVISIONING_DISCLAIMERS}
+     */
+    public static final String EXTRA_PROVISIONING_DISCLAIMER_HEADER =
+            "android.app.extra.PROVISIONING_DISCLAIMER_HEADER";
+
+    /**
+     * A {@link Uri} extra pointing to disclaimer content.
+     *
+     * <h5>The following URI schemes are accepted:</h5>
+     * <ul>
+     * <li>content ({@link android.content.ContentResolver#SCHEME_CONTENT})</li>
+     * <li>android.resource ({@link android.content.ContentResolver#SCHEME_ANDROID_RESOURCE})</li>
+     * </ul>
+     *
+     * <p> Styled text is supported in the disclaimer content. The content is parsed by
+     * {@link android.text.Html#fromHtml(String)} and displayed in a
+     * {@link android.widget.TextView}.
+     *
+     * <p> If a <code>content:</code> URI is passed, URI is passed, the intent should have the flag
+     * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION} and the uri should be added to the
+     * {@link android.content.ClipData} of the intent too.
+     *
+     * <p> Use in Bundle {@link #EXTRA_PROVISIONING_DISCLAIMERS}
+     */
+    public static final String EXTRA_PROVISIONING_DISCLAIMER_CONTENT =
+            "android.app.extra.PROVISIONING_DISCLAIMER_CONTENT";
+
+    /**
      * A boolean extra indicating if user setup should be skipped, for when provisioning is started
      * during setup-wizard.
      *
@@ -1254,8 +1309,8 @@
     /**
      * Result code for {@link checkProvisioningPreCondition}.
      *
-     * <p>Returned for {@link #ACTION_PROVISION_MANAGED_PROFILE} when the device has a device owner
-     * and the user is a system user on a split system user device.
+     * <p>Returned for {@link #ACTION_PROVISION_MANAGED_PROFILE} when the device the user is a
+     * system user on a split system user device.
      *
      * @hide
      */
@@ -7058,7 +7113,7 @@
      * <li>The managed profile is a profile of the user where the device owner is set.
      *     See {@link UserManager#getUserProfiles()}
      * <li>Both users are affiliated.
-     *         STOPSHIP(b/32326223) Add reference to setAffiliationIds here once public.
+     *     See {@link #setAffiliationIds}.
      * </ul>
      */
     public @NonNull List<UserHandle> getBindDeviceAdminTargetUsers(@NonNull ComponentName admin) {
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index 342c285..31b2359 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -34,4 +34,6 @@
     boolean isAppInactive(String packageName, int userId);
     void whitelistAppTemporarily(String packageName, long duration, int userId);
     void onCarrierPrivilegedAppsChanged();
+    void reportChooserSelection(String packageName, int userId, String contentType,
+            in String[] annotations, String action);
 }
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index a0da258..ce8b05a 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -86,6 +86,12 @@
         public static final int SHORTCUT_INVOCATION = 8;
 
         /**
+         * An event type denoting that a package was selected by the user for ChooserActivity.
+         * @hide
+         */
+        public static final int CHOOSER_ACTION = 9;
+
+        /**
          * {@hide}
          */
         public String mPackage;
@@ -119,6 +125,27 @@
         public String mShortcutId;
 
         /**
+         * Action type passed to ChooserActivity
+         * Only present for {@link #CHOOSER_ACTION} event types.
+         * {@hide}
+         */
+        public String mAction;
+
+        /**
+         * Content type passed to ChooserActivity.
+         * Only present for {@link #CHOOSER_ACTION} event types.
+         * {@hide}
+         */
+        public String mContentType;
+
+        /**
+         * Content annotations passed to ChooserActivity.
+         * Only present for {@link #CHOOSER_ACTION} event types.
+         * {@hide}
+         */
+        public String[] mContentAnnotations;
+
+        /**
          * The package name of the source of this event.
          */
         public String getPackageName() {
@@ -307,6 +334,11 @@
             case Event.SHORTCUT_INVOCATION:
                 p.writeString(event.mShortcutId);
                 break;
+            case Event.CHOOSER_ACTION:
+                p.writeString(event.mAction);
+                p.writeString(event.mContentType);
+                p.writeStringArray(event.mContentAnnotations);
+                break;
         }
     }
 
@@ -333,6 +365,9 @@
         // Fill out the event-dependant fields.
         eventOut.mConfiguration = null;
         eventOut.mShortcutId = null;
+        eventOut.mAction = null;
+        eventOut.mContentType = null;
+        eventOut.mContentAnnotations = null;
 
         switch (eventOut.mEventType) {
             case Event.CONFIGURATION_CHANGE:
@@ -342,6 +377,11 @@
             case Event.SHORTCUT_INVOCATION:
                 eventOut.mShortcutId = p.readString();
                 break;
+            case Event.CHOOSER_ACTION:
+                eventOut.mAction = p.readString();
+                eventOut.mContentType = p.readString();
+                eventOut.mContentAnnotations = p.createStringArray();
+                break;
         }
     }
 
diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java
index 2937ccc..57f18f1 100644
--- a/core/java/android/app/usage/UsageStats.java
+++ b/core/java/android/app/usage/UsageStats.java
@@ -16,8 +16,10 @@
 
 package android.app.usage;
 
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.ArrayMap;
 
 /**
  * Contains usage statistics for an app package for a specific
@@ -64,6 +66,11 @@
     /**
      * {@hide}
      */
+    public ArrayMap<String, ArrayMap<String, Integer>> mChooserCounts;
+
+    /**
+     * {@hide}
+     */
     public UsageStats() {
     }
 
@@ -75,6 +82,7 @@
         mTotalTimeInForeground = stats.mTotalTimeInForeground;
         mLaunchCount = stats.mLaunchCount;
         mLastEvent = stats.mLastEvent;
+        mChooserCounts = stats.mChooserCounts;
     }
 
     public String getPackageName() {
@@ -142,6 +150,26 @@
         mEndTimeStamp = Math.max(mEndTimeStamp, right.mEndTimeStamp);
         mTotalTimeInForeground += right.mTotalTimeInForeground;
         mLaunchCount += right.mLaunchCount;
+        if (mChooserCounts == null) {
+            mChooserCounts = right.mChooserCounts;
+        } else if (right.mChooserCounts != null) {
+            final int chooserCountsSize = right.mChooserCounts.size();
+            for (int i = 0; i < chooserCountsSize; i++) {
+                String action = right.mChooserCounts.keyAt(i);
+                ArrayMap<String, Integer> counts = right.mChooserCounts.valueAt(i);
+                if (!mChooserCounts.containsKey(action) || mChooserCounts.get(action) == null) {
+                    mChooserCounts.put(action, counts);
+                    continue;
+                }
+                final int annotationSize = counts.size();
+                for (int j = 0; j < annotationSize; j++) {
+                    String key = counts.keyAt(j);
+                    int rightValue = counts.valueAt(j);
+                    int leftValue = mChooserCounts.get(action).getOrDefault(key, 0);
+                    mChooserCounts.get(action).put(key, leftValue + rightValue);
+                }
+            }
+        }
     }
 
     @Override
@@ -158,6 +186,21 @@
         dest.writeLong(mTotalTimeInForeground);
         dest.writeInt(mLaunchCount);
         dest.writeInt(mLastEvent);
+        Bundle allCounts = new Bundle();
+        if (mChooserCounts != null) {
+            final int chooserCountSize = mChooserCounts.size();
+            for (int i = 0; i < chooserCountSize; i++) {
+                String action = mChooserCounts.keyAt(i);
+                ArrayMap<String, Integer> counts = mChooserCounts.valueAt(i);
+                Bundle currentCounts = new Bundle();
+                final int annotationSize = counts.size();
+                for (int j = 0; j < annotationSize; j++) {
+                    currentCounts.putInt(counts.keyAt(j), counts.valueAt(j));
+                }
+                allCounts.putBundle(action, currentCounts);
+            }
+        }
+        dest.writeBundle(allCounts);
     }
 
     public static final Creator<UsageStats> CREATOR = new Creator<UsageStats>() {
@@ -171,6 +214,25 @@
             stats.mTotalTimeInForeground = in.readLong();
             stats.mLaunchCount = in.readInt();
             stats.mLastEvent = in.readInt();
+            Bundle allCounts = in.readBundle();
+            if (allCounts != null) {
+                stats.mChooserCounts = new ArrayMap<>();
+                for (String action : allCounts.keySet()) {
+                    if (!stats.mChooserCounts.containsKey(action)) {
+                        ArrayMap<String, Integer> newCounts = new ArrayMap<>();
+                        stats.mChooserCounts.put(action, newCounts);
+                    }
+                    Bundle currentCounts = allCounts.getBundle(action);
+                    if (currentCounts != null) {
+                        for (String key : currentCounts.keySet()) {
+                            int value = currentCounts.getInt(key);
+                            if (value > 0) {
+                                stats.mChooserCounts.get(action).put(key, value);
+                            }
+                        }
+                    }
+                }
+            }
             return stats;
         }
 
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 2aeecfa..75a4a53 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -278,4 +278,23 @@
         } catch (RemoteException re) {
         }
     }
+
+    /**
+     * Reports a Chooser action to the UsageStatsManager.
+     *
+     * @param packageName The package name of the app that is selected.
+     * @param userId The user id of who makes the selection.
+     * @param contentType The type of the content, e.g., Image, Video, App.
+     * @param annotations The annotations of the content, e.g., Game, Selfie.
+     * @param action The action type of Intent that invokes ChooserActivity.
+     * {@link UsageEvents}
+     * @hide
+     */
+    public void reportChooserSelection(String packageName, int userId, String contentType,
+                                       String[] annotations, String action) {
+        try {
+            mService.reportChooserSelection(packageName, userId, contentType, annotations, action);
+        } catch (RemoteException re) {
+        }
+    }
 }
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index b0e27a4..6c1e2a9 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -201,6 +201,23 @@
     public static final int STATE_BLE_TURNING_OFF = 16;
 
     /**
+     * Human-readable string helper for AdapterState
+     * @hide
+     */
+    public static String nameForState(@AdapterState int state) {
+        switch(state) {
+            case STATE_OFF: return "OFF";
+            case STATE_TURNING_ON: return "TURNING_ON";
+            case STATE_ON: return "ON";
+            case STATE_TURNING_OFF: return "TURNING_OFF";
+            case STATE_BLE_TURNING_ON: return "BLE_TURNING_ON";
+            case STATE_BLE_ON: return "BLE_ON";
+            case STATE_BLE_TURNING_OFF: return "BLE_TURNING_OFF";
+            default: return "?!?!? (" + state + ")";
+        }
+    }
+
+    /**
      * Activity Action: Show a system activity that requests discoverable mode.
      * This activity will also request the user to turn on Bluetooth if it
      * is not currently enabled.
@@ -658,15 +675,8 @@
     @SystemApi
     public boolean isLeEnabled() {
        final int state = getLeState();
-       if (state == BluetoothAdapter.STATE_ON) {
-           if (DBG) Log.d (TAG, "STATE_ON");
-       } else if (state == BluetoothAdapter.STATE_BLE_ON) {
-           if (DBG) Log.d (TAG, "STATE_BLE_ON");
-       } else {
-           if (DBG) Log.d (TAG, "STATE_OFF");
-           return false;
-       }
-       return true;
+       if (DBG) Log.d(TAG, "isLeEnabled(): " + BluetoothAdapter.nameForState(state));
+       return (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_BLE_ON);
     }
 
     /**
@@ -831,10 +841,10 @@
         if (state == BluetoothAdapter.STATE_BLE_ON
             || state == BluetoothAdapter.STATE_BLE_TURNING_ON
             || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) {
-            if (VDBG) Log.d(TAG, "Consider internal state as OFF");
+            if (VDBG) Log.d(TAG, "Consider " + BluetoothAdapter.nameForState(state) + " state as OFF");
             state = BluetoothAdapter.STATE_OFF;
         }
-        if (VDBG) Log.d(TAG, "" + hashCode() + ": getState(). Returning " + state);
+        if (VDBG) Log.d(TAG, "" + hashCode() + ": getState(). Returning " + BluetoothAdapter.nameForState(state));
         return state;
     }
 
@@ -871,12 +881,12 @@
             mServiceLock.readLock().unlock();
         }
 
-        if (VDBG) Log.d(TAG,"getLeState() returning " + state);
+        if (VDBG) Log.d(TAG,"getLeState() returning " + BluetoothAdapter.nameForState(state));
         return state;
     }
 
     boolean getLeAccess() {
-        if(getLeState() == STATE_ON)
+        if (getLeState() == STATE_ON)
             return true;
 
         else if (getLeState() == STATE_BLE_ON)
@@ -914,8 +924,8 @@
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
     public boolean enable() {
-        if (isEnabled() == true) {
-            if (DBG) Log.d(TAG, "enable(): BT is already enabled..!");
+        if (isEnabled()) {
+            if (DBG) Log.d(TAG, "enable(): BT already enabled!");
             return true;
         }
         try {
@@ -1518,8 +1528,9 @@
                     }
                 }
             }
-        } catch (RemoteException e) {Log.e(TAG, "getSupportedProfiles:", e);}
-
+        } catch (RemoteException e) {
+          Log.e(TAG, "getSupportedProfiles:", e);
+        }
         return supportedProfiles;
     }
 
@@ -1870,34 +1881,6 @@
      * @hide
      */
     public Pair<byte[], byte[]> readOutOfBandData() {
-        if (getState() != STATE_ON) return null;
-        //TODO(BT
-        /*
-        try {
-            byte[] hash;
-            byte[] randomizer;
-
-            byte[] ret = null;
-            mServiceLock.readLock().lock();
-            if (mService != null) mService.readOutOfBandData();
-
-            if (ret  == null || ret.length != 32) return null;
-
-            hash = Arrays.copyOfRange(ret, 0, 16);
-            randomizer = Arrays.copyOfRange(ret, 16, 32);
-
-            if (DBG) {
-                Log.d(TAG, "readOutOfBandData:" + Arrays.toString(hash) +
-                  ":" + Arrays.toString(randomizer));
-            }
-            return new Pair<byte[], byte[]>(hash, randomizer);
-
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        */
         return null;
     }
 
@@ -2051,7 +2034,7 @@
                             if (cb != null) {
                                 cb.onBluetoothServiceUp(bluetoothService);
                             } else {
-                                Log.d(TAG, "onBluetoothServiceUp: cb is null!!!");
+                                Log.d(TAG, "onBluetoothServiceUp: cb is null!");
                             }
                         } catch (Exception e) {
                             Log.e(TAG,"",e);
@@ -2079,7 +2062,7 @@
                             if (cb != null) {
                                 cb.onBluetoothServiceDown();
                             } else {
-                                Log.d(TAG, "onBluetoothServiceDown: cb is null!!!");
+                                Log.d(TAG, "onBluetoothServiceDown: cb is null!");
                             }
                         } catch (Exception e) {
                             Log.e(TAG,"",e);
@@ -2089,7 +2072,7 @@
             }
 
             public void onBrEdrDown() {
-                if (DBG) Log.i(TAG, "onBrEdrDown:");
+                if (VDBG) Log.i(TAG, "onBrEdrDown: " + mService);
             }
     };
 
@@ -2100,7 +2083,7 @@
      */
     public boolean enableNoAutoConnect() {
         if (isEnabled() == true){
-            if (DBG) Log.d(TAG, "enableNoAutoConnect(): BT is already enabled..!");
+            if (DBG) Log.d(TAG, "enableNoAutoConnect(): BT already enabled!");
             return true;
         }
         try {
@@ -2140,22 +2123,6 @@
      */
     public boolean changeApplicationBluetoothState(boolean on,
                                                    BluetoothStateChangeCallback callback) {
-        if (callback == null) return false;
-
-        //TODO(BT)
-        /*
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.changeApplicationBluetoothState(on, new
-                    StateChangeCallbackWrapper(callback), new Binder());
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "changeBluetoothState", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        */
         return false;
     }
 
diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java
index 231dace..c3d6606 100644
--- a/core/java/android/content/BroadcastReceiver.java
+++ b/core/java/android/content/BroadcastReceiver.java
@@ -27,189 +27,25 @@
 import android.util.Slog;
 
 /**
- * Base class for code that will receive intents sent by sendBroadcast().
- *
- * <p>If you don't need to send broadcasts across applications, consider using
- * this class with {@link android.support.v4.content.LocalBroadcastManager} instead
- * of the more general facilities described below.  This will give you a much
- * more efficient implementation (no cross-process communication needed) and allow
- * you to avoid thinking about any security issues related to other applications
- * being able to receive or send your broadcasts.
+ * Base class for code that receives and handles broadcast intents sent by
+ * {@link android.content.Context#sendBroadcast(Intent)}.
  *
  * <p>You can either dynamically register an instance of this class with
  * {@link Context#registerReceiver Context.registerReceiver()}
- * or statically publish an implementation through the
+ * or statically declare an implementation with the
  * {@link android.R.styleable#AndroidManifestReceiver &lt;receiver&gt;}
  * tag in your <code>AndroidManifest.xml</code>.
- * 
- * <p><em><strong>Note:</strong></em>
- * &nbsp;&nbsp;&nbsp;If registering a receiver in your
- * {@link android.app.Activity#onResume() Activity.onResume()}
- * implementation, you should unregister it in 
- * {@link android.app.Activity#onPause() Activity.onPause()}.
- * (You won't receive intents when paused, 
- * and this will cut down on unnecessary system overhead). Do not unregister in 
- * {@link android.app.Activity#onSaveInstanceState(android.os.Bundle) Activity.onSaveInstanceState()},
- * because this won't be called if the user moves back in the history
- * stack.
- * 
- * <p>There are two major classes of broadcasts that can be received:</p>
- * <ul>
- * <li> <b>Normal broadcasts</b> (sent with {@link Context#sendBroadcast(Intent)
- * Context.sendBroadcast}) are completely asynchronous.  All receivers of the
- * broadcast are run in an undefined order, often at the same time.  This is
- * more efficient, but means that receivers cannot use the result or abort
- * APIs included here.
- * <li> <b>Ordered broadcasts</b> (sent with {@link Context#sendOrderedBroadcast(Intent, String)
- * Context.sendOrderedBroadcast}) are delivered to one receiver at a time.
- * As each receiver executes in turn, it can propagate a result to the next
- * receiver, or it can completely abort the broadcast so that it won't be passed
- * to other receivers.  The order receivers run in can be controlled with the
- * {@link android.R.styleable#AndroidManifestIntentFilter_priority
- * android:priority} attribute of the matching intent-filter; receivers with
- * the same priority will be run in an arbitrary order.
- * </ul>
- * 
- * <p>Even in the case of normal broadcasts, the system may in some
- * situations revert to delivering the broadcast one receiver at a time.  In
- * particular, for receivers that may require the creation of a process, only
- * one will be run at a time to avoid overloading the system with new processes.
- * In this situation, however, the non-ordered semantics hold: these receivers still
- * cannot return results or abort their broadcast.</p>
- * 
- * <p>Note that, although the Intent class is used for sending and receiving
- * these broadcasts, the Intent broadcast mechanism here is completely separate
- * from Intents that are used to start Activities with
- * {@link Context#startActivity Context.startActivity()}.
- * There is no way for a BroadcastReceiver
- * to see or capture Intents used with startActivity(); likewise, when
- * you broadcast an Intent, you will never find or start an Activity.
- * These two operations are semantically very different: starting an
- * Activity with an Intent is a foreground operation that modifies what the
- * user is currently interacting with; broadcasting an Intent is a background
- * operation that the user is not normally aware of.
- * 
- * <p>The BroadcastReceiver class (when launched as a component through
- * a manifest's {@link android.R.styleable#AndroidManifestReceiver &lt;receiver&gt;}
- * tag) is an important part of an
- * <a href="{@docRoot}guide/topics/fundamentals.html#lcycles">application's overall lifecycle</a>.</p>
- * 
- * <p>Topics covered here:
- * <ol>
- * <li><a href="#Security">Security</a>
- * <li><a href="#ReceiverLifecycle">Receiver Lifecycle</a>
- * <li><a href="#ProcessLifecycle">Process Lifecycle</a>
- * </ol>
  *
  * <div class="special reference">
  * <h3>Developer Guides</h3>
- * <p>For information about how to use this class to receive and resolve intents, read the
- * <a href="{@docRoot}guide/topics/intents/intents-filters.html">Intents and Intent Filters</a>
- * developer guide.</p>
- * </div>
+ * <p>For more information about using BroadcastReceiver, read the
+ * <a href="{@docRoot}guide/components/broadcasts.html">Broadcasts</a> developer guide.</p></div>
  *
- * <a name="Security"></a>
- * <h3>Security</h3>
- *
- * <p>Receivers used with the {@link Context} APIs are by their nature a
- * cross-application facility, so you must consider how other applications
- * may be able to abuse your use of them.  Some things to consider are:
- *
- * <ul>
- * <li><p>The Intent namespace is global.  Make sure that Intent action names and
- * other strings are written in a namespace you own, or else you may inadvertently
- * conflict with other applications.
- * <li><p>When you use {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)},
- * <em>any</em> application may send broadcasts to that registered receiver.  You can
- * control who can send broadcasts to it through permissions described below.
- * <li><p>When you publish a receiver in your application's manifest and specify
- * intent-filters for it, any other application can send broadcasts to it regardless
- * of the filters you specify.  To prevent others from sending to it, make it
- * unavailable to them with <code>android:exported="false"</code>.
- * <li><p>When you use {@link Context#sendBroadcast(Intent)} or related methods,
- * normally any other application can receive these broadcasts.  You can control who
- * can receive such broadcasts through permissions described below.  Alternatively,
- * starting with {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, you
- * can also safely restrict the broadcast to a single application with
- * {@link Intent#setPackage(String) Intent.setPackage}
- * </ul>
- *
- * <p>None of these issues exist when using
- * {@link android.support.v4.content.LocalBroadcastManager}, since intents
- * broadcast it never go outside of the current process.
- *
- * <p>Access permissions can be enforced by either the sender or receiver
- * of a broadcast.
- *
- * <p>To enforce a permission when sending, you supply a non-null
- * <var>permission</var> argument to
- * {@link Context#sendBroadcast(Intent, String)} or
- * {@link Context#sendOrderedBroadcast(Intent, String, BroadcastReceiver, android.os.Handler, int, String, Bundle)}.
- * Only receivers who have been granted this permission
- * (by requesting it with the
- * {@link android.R.styleable#AndroidManifestUsesPermission &lt;uses-permission&gt;}
- * tag in their <code>AndroidManifest.xml</code>) will be able to receive
- * the broadcast.
- *
- * <p>To enforce a permission when receiving, you supply a non-null
- * <var>permission</var> when registering your receiver -- either when calling
- * {@link Context#registerReceiver(BroadcastReceiver, IntentFilter, String, android.os.Handler)}
- * or in the static
- * {@link android.R.styleable#AndroidManifestReceiver &lt;receiver&gt;}
- * tag in your <code>AndroidManifest.xml</code>.  Only broadcasters who have
- * been granted this permission (by requesting it with the
- * {@link android.R.styleable#AndroidManifestUsesPermission &lt;uses-permission&gt;}
- * tag in their <code>AndroidManifest.xml</code>) will be able to send an
- * Intent to the receiver.
- *
- * <p>See the <a href="{@docRoot}guide/topics/security/security.html">Security and Permissions</a>
- * document for more information on permissions and security in general.
- *
- * <a name="ReceiverLifecycle"></a>
- * <h3>Receiver Lifecycle</h3>
- * 
- * <p>A BroadcastReceiver object is only valid for the duration of the call
- * to {@link #onReceive}.  Once your code returns from this function,
- * the system considers the object to be finished and no longer active.
- * 
- * <p>This has important repercussions to what you can do in an
- * {@link #onReceive} implementation: anything that requires asynchronous
- * operation is not available, because you will need to return from the
- * function to handle the asynchronous operation, but at that point the
- * BroadcastReceiver is no longer active and thus the system is free to kill
- * its process before the asynchronous operation completes.
- * 
- * <p>In particular, you may <i>not</i> show a dialog or bind to a service from
- * within a BroadcastReceiver.  For the former, you should instead use the
- * {@link android.app.NotificationManager} API.  For the latter, you can
- * use {@link android.content.Context#startService Context.startService()} to
- * send a command to the service.
- *
- * <a name="ProcessLifecycle"></a>
- * <h3>Process Lifecycle</h3>
- * 
- * <p>A process that is currently executing a BroadcastReceiver (that is,
- * currently running the code in its {@link #onReceive} method) is
- * considered to be a foreground process and will be kept running by the
- * system except under cases of extreme memory pressure.
- * 
- * <p>Once you return from onReceive(), the BroadcastReceiver is no longer
- * active, and its hosting process is only as important as any other application
- * components that are running in it.  This is especially important because if
- * that process was only hosting the BroadcastReceiver (a common case for
- * applications that the user has never or not recently interacted with), then
- * upon returning from onReceive() the system will consider its process
- * to be empty and aggressively kill it so that resources are available for other
- * more important processes.
- * 
- * <p>This means that for longer-running operations you will often use
- * a {@link android.app.Service} in conjunction with a BroadcastReceiver to keep
- * the containing process active for the entire time of your operation.
  */
 public abstract class BroadcastReceiver {
     private PendingResult mPendingResult;
     private boolean mDebugUnregister;
-    
+
     /**
      * State for a result that is pending for a broadcast receiver.  Returned
      * by {@link BroadcastReceiver#goAsync() goAsync()}
@@ -218,7 +54,7 @@
      * terminate; you must call {@link #finish()} once you are done with the
      * broadcast.  This allows you to process the broadcast off of the main
      * thread of your app.
-     * 
+     *
      * <p>Note on threading: the state inside of this class is not itself
      * thread-safe, however you can use it from any thread if you properly
      * sure that you do not have races.  Typically this means you will hand
@@ -232,14 +68,14 @@
         public static final int TYPE_REGISTERED = 1;
         /** @hide */
         public static final int TYPE_UNREGISTERED = 2;
-        
+
         final int mType;
         final boolean mOrderedHint;
         final boolean mInitialStickyHint;
         final IBinder mToken;
         final int mSendingUser;
         final int mFlags;
-        
+
         int mResultCode;
         String mResultData;
         Bundle mResultExtras;
@@ -259,7 +95,7 @@
             mSendingUser = userId;
             mFlags = flags;
         }
-        
+
         /**
          * Version of {@link BroadcastReceiver#setResultCode(int)
          * BroadcastReceiver.setResultCode(int)} for
@@ -331,7 +167,7 @@
             mResultData = data;
             mResultExtras = extras;
         }
-     
+
         /**
          * Version of {@link BroadcastReceiver#getAbortBroadcast()
          * BroadcastReceiver.getAbortBroadcast()} for
@@ -350,7 +186,7 @@
             checkSynchronousHint();
             mAbortBroadcast = true;
         }
-        
+
         /**
          * Version of {@link BroadcastReceiver#clearAbortBroadcast()
          * BroadcastReceiver.clearAbortBroadcast()} for
@@ -359,7 +195,7 @@
         public final void clearAbortBroadcast() {
             mAbortBroadcast = false;
         }
-        
+
         /**
          * Finish the broadcast.  The current result will be sent and the
          * next broadcast will proceed.
@@ -375,16 +211,16 @@
                     // of the list to finish the broadcast, so we don't block this
                     // thread (which may be the main thread) to have it finished.
                     //
-                    // Note that we don't need to use QueuedWork.add() with the
+                    // Note that we don't need to use QueuedWork.addFinisher() with the
                     // runnable, since we know the AM is waiting for us until the
                     // executor gets to it.
-                    QueuedWork.singleThreadExecutor().execute( new Runnable() {
+                    QueuedWork.queue(new Runnable() {
                         @Override public void run() {
                             if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                                     "Finishing broadcast after work to component " + mToken);
                             sendFinished(mgr);
                         }
-                    });
+                    }, false);
                 } else {
                     if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                             "Finishing broadcast to component " + mToken);
@@ -397,14 +233,14 @@
                 sendFinished(mgr);
             }
         }
-        
+
         /** @hide */
         public void setExtrasClassLoader(ClassLoader cl) {
             if (mResultExtras != null) {
                 mResultExtras.setClassLoader(cl);
             }
         }
-        
+
         /** @hide */
         public void sendFinished(IActivityManager am) {
             synchronized (this) {
@@ -412,7 +248,7 @@
                     throw new IllegalStateException("Broadcast already finished");
                 }
                 mFinished = true;
-            
+
                 try {
                     if (mResultExtras != null) {
                         mResultExtras.setAllowFds(false);
@@ -448,7 +284,7 @@
             Log.e("BroadcastReceiver", e.getMessage(), e);
         }
     }
-    
+
     public BroadcastReceiver() {
     }
 
@@ -468,14 +304,15 @@
      *
      * <p><b>If this BroadcastReceiver was launched through a &lt;receiver&gt; tag,
      * then the object is no longer alive after returning from this
-     * function.</b>  This means you should not perform any operations that
-     * return a result to you asynchronously -- in particular, for interacting
-     * with services, you should use
-     * {@link Context#startService(Intent)} instead of
-     * {@link Context#bindService(Intent, ServiceConnection, int)}.  If you wish
-     * to interact with a service that is already running, you can use
-     * {@link #peekService}.
-     * 
+     * function.</b> This means you should not perform any operations that
+     * return a result to you asynchronously. If you need to perform any follow up
+     * background work, schedule a {@link android.app.job.JobService} with
+     * {@link android.app.job.JobScheduler}.
+     *
+     * If you wish to interact with a service that is already running and previously
+     * bound using {@link android.content.Context#bindService(Intent, ServiceConnection, int) bindService()},
+     * you can use {@link #peekService}.
+     *
      * <p>The Intent filters used in {@link android.content.Context#registerReceiver}
      * and in application manifests are <em>not</em> guaranteed to be exclusive. They
      * are hints to the operating system about how to find suitable recipients. It is
@@ -483,7 +320,7 @@
      * resolution.  For this reason, {@link #onReceive(Context, Intent) onReceive()}
      * implementations should respond only to known actions, ignoring any unexpected
      * Intents that they may receive.
-     * 
+     *
      * @param context The Context in which the receiver is running.
      * @param intent The Intent being received.
      */
@@ -496,7 +333,7 @@
      * responsive to the broadcast (finishing it within 10s), but does allow
      * the implementation to move work related to it over to another thread
      * to avoid glitching the main UI thread due to disk IO.
-     * 
+     *
      * @return Returns a {@link PendingResult} representing the result of
      * the active broadcast.  The BroadcastRecord itself is no longer active;
      * all data and other interaction must go through {@link PendingResult}
@@ -508,15 +345,20 @@
         mPendingResult = null;
         return res;
     }
-    
+
     /**
-     * Provide a binder to an already-running service.  This method is synchronous
+     * Provide a binder to an already-bound service.  This method is synchronous
      * and will not start the target service if it is not present, so it is safe
      * to call from {@link #onReceive}.
-     * 
+     *
+     * For peekService() to return a non null {@link android.os.IBinder} interface
+     * the service must have published it before. In other words some component
+     * must have called {@link android.content.Context#bindService(Intent, ServiceConnection, int)} on it.
+     *
      * @param myContext The Context that had been passed to {@link #onReceive(Context, Intent)}
-     * @param service The Intent indicating the service you wish to use.  See {@link
-     * Context#startService(Intent)} for more information.
+     * @param service Identifies the already-bound service you wish to use. See
+     * {@link android.content.Context#bindService(Intent, ServiceConnection, int)}
+     * for more information.
      */
     public IBinder peekService(Context myContext, Intent service) {
         IActivityManager am = ActivityManager.getService();
@@ -538,13 +380,13 @@
      * Activity {@link android.app.Activity#RESULT_CANCELED} and
      * {@link android.app.Activity#RESULT_OK} constants, though the
      * actual meaning of this value is ultimately up to the broadcaster.
-     * 
+     *
      * <p class="note">This method does not work with non-ordered broadcasts such
      * as those sent with {@link Context#sendBroadcast(Intent)
      * Context.sendBroadcast}</p>
-     * 
+     *
      * @param code The new result code.
-     * 
+     *
      * @see #setResult(int, String, Bundle)
      */
     public final void setResultCode(int code) {
@@ -554,7 +396,7 @@
 
     /**
      * Retrieve the current result code, as set by the previous receiver.
-     * 
+     *
      * @return int The current result code.
      */
     public final int getResultCode() {
@@ -567,13 +409,13 @@
      * {@link Context#sendOrderedBroadcast(Intent, String)
      * Context.sendOrderedBroadcast}.  This is an arbitrary
      * string whose interpretation is up to the broadcaster.
-     * 
+     *
      * <p><strong>This method does not work with non-ordered broadcasts such
      * as those sent with {@link Context#sendBroadcast(Intent)
      * Context.sendBroadcast}</strong></p>
-     * 
+     *
      * @param data The new result data; may be null.
-     * 
+     *
      * @see #setResult(int, String, Bundle)
      */
     public final void setResultData(String data) {
@@ -584,7 +426,7 @@
     /**
      * Retrieve the current result data, as set by the previous receiver.
      * Often this is null.
-     * 
+     *
      * @return String The current result data; may be null.
      */
     public final String getResultData() {
@@ -599,13 +441,13 @@
      * holding arbitrary data, whose interpretation is up to the
      * broadcaster.  Can be set to null.  Calling this method completely
      * replaces the current map (if any).
-     * 
+     *
      * <p><strong>This method does not work with non-ordered broadcasts such
      * as those sent with {@link Context#sendBroadcast(Intent)
      * Context.sendBroadcast}</strong></p>
-     * 
+     *
      * @param extras The new extra data map; may be null.
-     * 
+     *
      * @see #setResult(int, String, Bundle)
      */
     public final void setResultExtras(Bundle extras) {
@@ -617,11 +459,11 @@
      * Retrieve the current result extra data, as set by the previous receiver.
      * Any changes you make to the returned Map will be propagated to the next
      * receiver.
-     * 
+     *
      * @param makeMap If true then a new empty Map will be made for you if the
      *                current Map is null; if false you should be prepared to
      *                receive a null Map.
-     * 
+     *
      * @return Map The current extras map.
      */
     public final Bundle getResultExtras(boolean makeMap) {
@@ -640,11 +482,11 @@
      * {@link Context#sendOrderedBroadcast(Intent, String)
      * Context.sendOrderedBroadcast}.  All current result data is replaced
      * by the value given to this method.
-     * 
+     *
      * <p><strong>This method does not work with non-ordered broadcasts such
      * as those sent with {@link Context#sendBroadcast(Intent)
      * Context.sendBroadcast}</strong></p>
-     * 
+     *
      * @param code The new result code.  Often uses the
      * Activity {@link android.app.Activity#RESULT_CANCELED} and
      * {@link android.app.Activity#RESULT_OK} constants, though the
@@ -662,11 +504,11 @@
         mPendingResult.mResultData = data;
         mPendingResult.mResultExtras = extras;
     }
- 
+
     /**
      * Returns the flag indicating whether or not this receiver should
      * abort the current broadcast.
-     * 
+     *
      * @return True if the broadcast should be aborted.
      */
     public final boolean getAbortBroadcast() {
@@ -679,10 +521,10 @@
      * {@link Context#sendOrderedBroadcast(Intent, String)
      * Context.sendOrderedBroadcast}.  This will prevent
      * any other broadcast receivers from receiving the broadcast. It will still
-     * call {@link #onReceive} of the BroadcastReceiver that the caller of 
+     * call {@link #onReceive} of the BroadcastReceiver that the caller of
      * {@link Context#sendOrderedBroadcast(Intent, String)
      * Context.sendOrderedBroadcast} passed in.
-     * 
+     *
      * <p><strong>This method does not work with non-ordered broadcasts such
      * as those sent with {@link Context#sendBroadcast(Intent)
      * Context.sendBroadcast}</strong></p>
@@ -691,7 +533,7 @@
         checkSynchronousHint();
         mPendingResult.mAbortBroadcast = true;
     }
-    
+
     /**
      * Clears the flag indicating that this receiver should abort the current
      * broadcast.
@@ -701,7 +543,7 @@
             mPendingResult.mAbortBroadcast = false;
         }
     }
-    
+
     /**
      * Returns true if the receiver is currently processing an ordered
      * broadcast.
@@ -709,7 +551,7 @@
     public final boolean isOrderedBroadcast() {
         return mPendingResult != null ? mPendingResult.mOrderedHint : false;
     }
-    
+
     /**
      * Returns true if the receiver is currently processing the initial
      * value of a sticky broadcast -- that is, the value that was last
@@ -719,7 +561,7 @@
     public final boolean isInitialStickyBroadcast() {
         return mPendingResult != null ? mPendingResult.mInitialStickyHint : false;
     }
-    
+
     /**
      * For internal use, sets the hint about whether this BroadcastReceiver is
      * running in ordered mode.
@@ -727,21 +569,21 @@
     public final void setOrderedHint(boolean isOrdered) {
         // Accidentally left in the SDK.
     }
-    
+
     /**
      * For internal use to set the result data that is active. @hide
      */
     public final void setPendingResult(PendingResult result) {
         mPendingResult = result;
     }
-    
+
     /**
      * For internal use to set the result data that is active. @hide
      */
     public final PendingResult getPendingResult() {
         return mPendingResult;
     }
-    
+
     /** @hide */
     public int getSendingUserId() {
         return mPendingResult.mSendingUser;
@@ -761,19 +603,19 @@
     public final void setDebugUnregister(boolean debug) {
         mDebugUnregister = debug;
     }
-    
+
     /**
      * Return the last value given to {@link #setDebugUnregister}.
      */
     public final boolean getDebugUnregister() {
         return mDebugUnregister;
     }
-    
+
     void checkSynchronousHint() {
         if (mPendingResult == null) {
             throw new IllegalStateException("Call while result is not pending");
         }
-        
+
         // Note that we don't assert when receiving the initial sticky value,
         // since that may have come from an ordered broadcast.  We'll catch
         // them later when the real broadcast happens again.
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index b9783aa..11da8dd 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2747,7 +2747,8 @@
             //@hide: SOUND_TRIGGER_SERVICE,
             SHORTCUT_SERVICE,
             //@hide: CONTEXTHUB_SERVICE,
-            SYSTEM_HEALTH_SERVICE
+            SYSTEM_HEALTH_SERVICE,
+            //@hide: INCIDENT_SERVICE
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ServiceName {}
@@ -3705,6 +3706,12 @@
     public static final String DEVICE_IDENTIFIERS_SERVICE = "device_identifiers";
 
     /**
+     * Service to report a system health "incident"
+     * @hide
+     */
+    public static final String INCIDENT_SERVICE = "incident";
+
+    /**
      * Determine whether the given permission is allowed for a particular
      * process and user ID running in the system.
      *
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index cda8176..16af5e1 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3226,18 +3226,54 @@
     public static final String
             ACTION_DYNAMIC_SENSOR_CHANGED = "android.intent.action.DYNAMIC_SENSOR_CHANGED";
 
-    /** {@hide} */
+    /**
+     * Deprecated - use {@link #ACTION_FACTORY_RESET} instead.
+     *
+     * {@hide}
+     */
+    @Deprecated
     public static final String ACTION_MASTER_CLEAR = "android.intent.action.MASTER_CLEAR";
 
     /**
-     * Boolean intent extra to be used with {@link ACTION_MASTER_CLEAR} in order to force a factory
-     * reset even if {@link android.os.UserManager.DISALLOW_FACTORY_RESET} is set.
+     * Boolean intent extra to be used with {@link #ACTION_MASTER_CLEAR} in order to force a factory
+     * reset even if {@link android.os.UserManager#DISALLOW_FACTORY_RESET} is set.
+     *
+     * <p>Deprecated - use {@link #EXTRA_FORCE_FACTORY_RESET} instead.
+     *
      * @hide
      */
+    @Deprecated
     public static final String EXTRA_FORCE_MASTER_CLEAR =
             "android.intent.extra.FORCE_MASTER_CLEAR";
 
     /**
+     * A broadcast action to trigger a factory reset.
+     *
+     * <p> The sender must hold the {@link android.Manifest.permission#MASTER_CLEAR} permission.
+     *
+     * <p>Not for use by third-party applications.
+     *
+     * @see #EXTRA_FORCE_MASTER_CLEAR
+     *
+     * {@hide}
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_FACTORY_RESET = "android.intent.action.FACTORY_RESET";
+
+    /**
+     * Boolean intent extra to be used with {@link #ACTION_MASTER_CLEAR} in order to force a factory
+     * reset even if {@link android.os.UserManager#DISALLOW_FACTORY_RESET} is set.
+     *
+     * <p>Not for use by third-party applications.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_FORCE_FACTORY_RESET =
+            "android.intent.extra.FORCE_FACTORY_RESET";
+
+    /**
      * Broadcast action: report that a settings element is being restored from backup.  The intent
      * contains three extras: EXTRA_SETTING_NAME is a string naming the restored setting,
      * EXTRA_SETTING_NEW_VALUE is the value being restored, and EXTRA_SETTING_PREVIOUS_VALUE
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index 2c97ec4..7036f87 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -531,7 +531,7 @@
      * @hide
      */
     public final boolean getAutoVerify() {
-        return ((mVerifyState & STATE_VERIFY_AUTO) == 1);
+        return ((mVerifyState & STATE_VERIFY_AUTO) == STATE_VERIFY_AUTO);
     }
 
     /**
diff --git a/core/java/android/content/pm/IPinItemRequest.aidl b/core/java/android/content/pm/IPinItemRequest.aidl
new file mode 100644
index 0000000..efe2835
--- /dev/null
+++ b/core/java/android/content/pm/IPinItemRequest.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2016 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.content.pm;
+
+import android.os.Bundle;
+
+/**
+ * {@hide}
+ */
+interface IPinItemRequest {
+    boolean isValid();
+    boolean accept(in Bundle options);
+}
diff --git a/core/java/android/content/pm/IShortcutService.aidl b/core/java/android/content/pm/IShortcutService.aidl
index 1bf2ab0..91df8e8 100644
--- a/core/java/android/content/pm/IShortcutService.aidl
+++ b/core/java/android/content/pm/IShortcutService.aidl
@@ -15,6 +15,7 @@
  */
 package android.content.pm;
 
+import android.content.IntentSender;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ShortcutInfo;
 
@@ -41,6 +42,9 @@
 
     boolean updateShortcuts(String packageName, in ParceledListSlice shortcuts, int userId);
 
+    boolean requestPinShortcut(String packageName, in ShortcutInfo shortcut,
+            in IntentSender resultIntent, int userId);
+
     void disableShortcuts(String packageName, in List shortcutIds, CharSequence disabledMessage,
             int disabledMessageResId, int userId);
 
@@ -63,4 +67,6 @@
     byte[] getBackupPayload(int user);
 
     void applyRestore(in byte[] payload, int user);
+
+    boolean isRequestPinShortcutSupported(int user);
 }
\ No newline at end of file
diff --git a/core/java/android/content/pm/IntentFilterVerificationInfo.java b/core/java/android/content/pm/IntentFilterVerificationInfo.java
index f47ad1f..f12abf3 100644
--- a/core/java/android/content/pm/IntentFilterVerificationInfo.java
+++ b/core/java/android/content/pm/IntentFilterVerificationInfo.java
@@ -181,7 +181,7 @@
     }
 
     public String getStatusString() {
-        return getStatusStringFromValue(mMainStatus);
+        return getStatusStringFromValue(((long)mMainStatus) << 32);
     }
 
     public static String getStatusStringFromValue(long val) {
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 7fc8044..5f4bc00 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -19,6 +19,8 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.TestApi;
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
@@ -32,11 +34,14 @@
 import android.graphics.Rect;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
@@ -69,6 +74,36 @@
     static final String TAG = "LauncherApps";
     static final boolean DEBUG = false;
 
+    /**
+     * Activity Action: For the default launcher to show the confirmation dialog to create
+     * a pinned shortcut.
+     *
+     * <p>See the {@link ShortcutManager} javadoc for details.
+     *
+     * <p>
+     * Use {@link #getPinItemRequest(Intent)} to get a {@link PinItemRequest} object,
+     * and call {@link PinItemRequest#accept(Bundle)}
+     * if the user accepts.  If the user doesn't accept, no further action is required.
+     *
+     * @see #EXTRA_PIN_ITEM_REQUEST
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_CONFIRM_PIN_ITEM =
+            "android.content.pm.action.CONFIRM_PIN_ITEM";
+
+    /**
+     * An extra for {@link #ACTION_CONFIRM_PIN_ITEM} containing a
+     * {@link ShortcutInfo} of the shortcut the publisher app asked to pin.
+     *
+     * <p>A helper function {@link #getPinItemRequest(Intent)} can be used
+     * instead of using this constant directly.
+     *
+     * @see #ACTION_CONFIRM_PIN_ITEM
+     */
+    public static final String EXTRA_PIN_ITEM_REQUEST =
+            "android.content.pm.extra.PIN_ITEM_REQUEST";
+
+
     private Context mContext;
     private ILauncherApps mService;
     private PackageManager mPm;
@@ -655,23 +690,41 @@
                 }
             }
         } else if (shortcut.hasIconResource()) {
-            try {
-                final int resId = shortcut.getIconResourceId();
-                if (resId == 0) {
-                    return null; // Shouldn't happen but just in case.
+            return loadDrawableResourceFromPackage(shortcut.getPackage(),
+                    shortcut.getIconResourceId(), shortcut.getUserHandle(), density);
+        } else if (shortcut.getIcon() != null) {
+            // This happens if a shortcut is pending-approval.
+            final Icon icon = shortcut.getIcon();
+            switch (icon.getType()) {
+                case Icon.TYPE_RESOURCE: {
+                    return loadDrawableResourceFromPackage(shortcut.getPackage(),
+                            icon.getResId(), shortcut.getUserHandle(), density);
                 }
-                final ApplicationInfo ai = getApplicationInfo(shortcut.getPackage(),
-                        /* flags =*/ 0, shortcut.getUserHandle());
-                final Resources res = mContext.getPackageManager().getResourcesForApplication(ai);
-                return res.getDrawableForDensity(resId, density);
-            } catch (NameNotFoundException | Resources.NotFoundException e) {
-                return null;
+                case Icon.TYPE_BITMAP: {
+                    return icon.loadDrawable(mContext);
+                }
+                default:
+                    return null; // Shouldn't happen though.
             }
         } else {
             return null; // Has no icon.
         }
     }
 
+    private Drawable loadDrawableResourceFromPackage(String packageName, int resId,
+            UserHandle user, int density) {
+        try {
+            if (resId == 0) {
+                return null; // Shouldn't happen but just in case.
+            }
+            final ApplicationInfo ai = getApplicationInfo(packageName, /* flags =*/ 0, user);
+            final Resources res = mContext.getPackageManager().getResourcesForApplication(ai);
+            return res.getDrawableForDensity(resId, density);
+        } catch (NameNotFoundException | Resources.NotFoundException e) {
+            return null;
+        }
+    }
+
     /**
      * Returns the shortcut icon with badging appropriate for the profile.
      *
@@ -1064,4 +1117,121 @@
             obtainMessage(MSG_SHORTCUT_CHANGED, info).sendToTarget();
         }
     }
+
+    /**
+     * A helper method to extract a {@link PinItemRequest} set to
+     * the {@link #EXTRA_PIN_ITEM_REQUEST} extra.
+     */
+    public PinItemRequest getPinItemRequest(Intent intent) {
+        return intent.getParcelableExtra(EXTRA_PIN_ITEM_REQUEST);
+    }
+
+    /**
+     * Represents a "pin shortcut" request made by an app, which is sent with
+     * an {@link #ACTION_CONFIRM_PIN_ITEM} intent to the default launcher app.
+     *
+     * @see #EXTRA_PIN_ITEM_REQUEST
+     * @see #getPinItemRequest(Intent)
+     */
+    public static final class PinItemRequest implements Parcelable {
+
+        /** This is a request to pin shortcut. */
+        public static final int REQUEST_TYPE_SHORTCUT = 1;
+
+        @IntDef(value = {REQUEST_TYPE_SHORTCUT})
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface RequestType {}
+
+        private final int mRequestType;
+        private final ShortcutInfo mShortcutInfo;
+        private final IPinItemRequest mInner;
+
+        /**
+         * @hide
+         */
+        public PinItemRequest(@RequestType int requestType, ShortcutInfo shortcutInfo,
+                IPinItemRequest inner) {
+            mRequestType = requestType;
+            mShortcutInfo = shortcutInfo;
+            mInner = inner;
+        }
+
+        /**
+         * Represents the type of a request.  For now {@link #REQUEST_TYPE_SHORTCUT} is the only
+         * valid type.
+         */
+        @RequestType
+        public int getRequestType() {
+            return mRequestType;
+        }
+
+        /**
+         * {@link ShortcutInfo} sent by the requesting app.  Always non-null for a
+         * {@link #REQUEST_TYPE_SHORTCUT} request.
+         */
+        @Nullable
+        public ShortcutInfo getShortcutInfo() {
+            return mShortcutInfo;
+        }
+
+        /**
+         * Return {@code TRUE} if a request is valid -- i.e. {@link #accept(Bundle)} has not been
+         * called, and it has not been canceled.
+         */
+        public boolean isValid() {
+            try {
+                return mInner.isValid();
+            } catch (RemoteException e) {
+                return false;
+            }
+        }
+
+        /**
+         * Called by the receiving launcher app when the user accepts the request.
+         */
+        public boolean accept(@Nullable Bundle options) {
+            try {
+                return mInner.accept(options);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Same as as {@link #accept(Bundle)} with no options.
+         */
+        public boolean accept() {
+            return accept(/* options= */ null);
+        }
+
+        private PinItemRequest(Parcel source) {
+            final ClassLoader cl = getClass().getClassLoader();
+
+            mRequestType = source.readInt();
+            mShortcutInfo = source.readParcelable(cl);
+            mInner = IPinItemRequest.Stub.asInterface(source.readStrongBinder());
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(mRequestType);
+            dest.writeParcelable(mShortcutInfo, flags);
+            dest.writeStrongBinder(mInner.asBinder());
+        }
+
+        public static final Creator<PinItemRequest> CREATOR =
+                new Creator<PinItemRequest>() {
+                    public PinItemRequest createFromParcel(Parcel source) {
+                        return new PinItemRequest(source);
+                    }
+                    public PinItemRequest[] newArray(int size) {
+                        return new PinItemRequest[size];
+                    }
+                };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+    }
 }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 279fb9e..32bf66a 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -357,6 +357,7 @@
         public final int[] splitRevisionCodes;
 
         public final boolean coreApp;
+        public final boolean debuggable;
         public final boolean multiArch;
         public final boolean use32bitAbi;
         public final boolean extractNativeLibs;
@@ -374,6 +375,7 @@
             this.baseRevisionCode = baseApk.revisionCode;
             this.splitRevisionCodes = splitRevisionCodes;
             this.coreApp = baseApk.coreApp;
+            this.debuggable = baseApk.debuggable;
             this.multiArch = baseApk.multiArch;
             this.use32bitAbi = baseApk.use32bitAbi;
             this.extractNativeLibs = baseApk.extractNativeLibs;
@@ -403,6 +405,7 @@
         public final Signature[] signatures;
         public final Certificate[][] certificates;
         public final boolean coreApp;
+        public final boolean debuggable;
         public final boolean multiArch;
         public final boolean use32bitAbi;
         public final boolean extractNativeLibs;
@@ -410,7 +413,8 @@
         public ApkLite(String codePath, String packageName, String splitName, int versionCode,
                 int revisionCode, int installLocation, List<VerifierInfo> verifiers,
                 Signature[] signatures, Certificate[][] certificates, boolean coreApp,
-                boolean multiArch, boolean use32bitAbi, boolean extractNativeLibs) {
+                boolean debuggable, boolean multiArch, boolean use32bitAbi,
+                boolean extractNativeLibs) {
             this.codePath = codePath;
             this.packageName = packageName;
             this.splitName = splitName;
@@ -421,6 +425,7 @@
             this.signatures = signatures;
             this.certificates = certificates;
             this.coreApp = coreApp;
+            this.debuggable = debuggable;
             this.multiArch = multiArch;
             this.use32bitAbi = use32bitAbi;
             this.extractNativeLibs = extractNativeLibs;
@@ -1621,6 +1626,7 @@
         int versionCode = 0;
         int revisionCode = 0;
         boolean coreApp = false;
+        boolean debuggable = false;
         boolean multiArch = false;
         boolean use32bitAbi = false;
         boolean extractNativeLibs = true;
@@ -1660,6 +1666,9 @@
             if (parser.getDepth() == searchDepth && "application".equals(parser.getName())) {
                 for (int i = 0; i < attrs.getAttributeCount(); ++i) {
                     final String attr = attrs.getAttributeName(i);
+                    if ("debuggable".equals(attr)) {
+                        debuggable = attrs.getAttributeBooleanValue(i, false);
+                    }
                     if ("multiArch".equals(attr)) {
                         multiArch = attrs.getAttributeBooleanValue(i, false);
                     }
@@ -1675,7 +1684,7 @@
 
         return new ApkLite(codePath, packageSplit.first, packageSplit.second, versionCode,
                 revisionCode, installLocation, verifiers, signatures, certificates, coreApp,
-                multiArch, use32bitAbi, extractNativeLibs);
+                debuggable, multiArch, use32bitAbi, extractNativeLibs);
     }
 
     /**
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 56f914e..a4d8916 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -129,6 +129,10 @@
             | CLONE_REMOVE_RES_NAMES;
 
     /** @hide */
+    public static final int CLONE_REMOVE_FOR_LAUNCHER_APPROVAL = CLONE_REMOVE_INTENT
+            | CLONE_REMOVE_RES_NAMES;
+
+    /** @hide */
     @IntDef(flag = true,
             value = {
                     CLONE_REMOVE_ICON,
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index f7c4d59..c8f00b8 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -16,21 +16,31 @@
 package android.content.pm;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.TestApi;
 import android.annotation.UserIdInt;
 import android.app.Activity;
 import android.app.usage.UsageStatsManager;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentSender;
+import android.os.Binder;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
 
 import java.util.List;
 
 /**
+ * <p><strong>TODO Update the overview to how to use the O new features.</strong></p>
+ *
  * The ShortcutManager manages an app's <em>shortcuts</em>. Shortcuts provide users
  * with quick access to activities other than an app's main activity in the currently-active
  * launcher.  For example,
@@ -618,7 +628,7 @@
      *
      * @throws IllegalStateException when the user is locked.
      */
-    public boolean updateShortcuts(List<ShortcutInfo> shortcutInfoList) {
+    public boolean updateShortcuts(@NonNull List<ShortcutInfo> shortcutInfoList) {
         try {
             return mService.updateShortcuts(mContext.getPackageName(),
                     new ParceledListSlice(shortcutInfoList), injectMyUserId());
@@ -815,6 +825,61 @@
     }
 
     /**
+     * Return {@code TRUE} if the default launcher supports
+     * {@link #requestPinShortcut(ShortcutInfo, IntentSender)}.
+     */
+    public boolean isRequestPinShortcutSupported() {
+        try {
+            return mService.isRequestPinShortcutSupported(injectMyUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Request to create a pinned shortcut.  The default launcher will receive this request and
+     * ask the user for approval.  If the user approves it, the shortcut will be created and
+     * {@code resultIntent} will be sent.  Otherwise, no responses will be sent to the caller.
+     *
+     * <p>When a request is denied by the user, the caller app will not get any response.
+     *
+     * <p>Only apps with a foreground activity or a foreground service can call it.  Otherwise
+     * it'll throw {@link IllegalStateException}.
+     *
+     * <p>When an app calls this API when a previous request is still waiting for a response,
+     * the previous request will be canceled.
+     *
+     * @param shortcut New shortcut to pin.  If an app wants to pin an existing (either dynamic
+     *     or manifest) shortcut, then it only needs to have an ID, and other fields don't have to
+     *     be set, in which case, the target shortcut must be enabled.
+     *     If it's a new shortcut, all the mandatory fields, such as a short label, must be
+     *     set.
+     * @param resultIntent If not null, this intent will be sent when the shortcut is pinned.
+     *    Use {@link android.app.PendingIntent#getIntentSender()} to create a {@link IntentSender}.
+     *
+     * @return {@code TRUE} if the launcher supports this feature.  Note the API will return without
+     *    waiting for the user to respond, so getting {@code TRUE} from this API does *not* mean
+     *    the shortcut is pinned.  {@code FALSE} if the launcher doesn't support this feature.
+     *
+     * @see #isRequestPinShortcutSupported()
+     * @see IntentSender
+     * @see android.app.PendingIntent#getIntentSender()
+     *
+     * @throws IllegalArgumentException if a shortcut with the same ID exists and is disabled.
+     * @throws IllegalStateException The caller doesn't have a foreground activity or a foreground
+     * service.
+     */
+    public boolean requestPinShortcut(@NonNull ShortcutInfo shortcut,
+            @Nullable IntentSender resultIntent) {
+        try {
+            return mService.requestPinShortcut(mContext.getPackageName(), shortcut,
+                    resultIntent, injectMyUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Called internally when an app is considered to have come to the foreground
      * even when technically it's not.  This method resets the throttling for this package.
      * For example, when the user sends an "inline reply" on a notification, the system UI will
diff --git a/core/java/android/hardware/CameraStatus.java b/core/java/android/hardware/CameraStatus.java
new file mode 100644
index 0000000..bc97e38
--- /dev/null
+++ b/core/java/android/hardware/CameraStatus.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 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.hardware;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Status information about a camera.
+ *
+ * Contains the name of the camera device, and its current status, one of the
+ * ICameraServiceListener.STATUS_ values.
+ *
+ * @hide
+ */
+public class CameraStatus implements Parcelable {
+    public String cameraId;
+    public int status;
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(cameraId);
+        out.writeInt(status);
+    }
+
+    public void readFromParcel(Parcel in) {
+        cameraId = in.readString();
+        status = in.readInt();
+    }
+
+    public static final Parcelable.Creator<CameraStatus> CREATOR =
+            new Parcelable.Creator<CameraStatus>() {
+        @Override
+        public CameraStatus createFromParcel(Parcel in) {
+            CameraStatus status = new CameraStatus();
+            status.readFromParcel(in);
+
+            return status;
+        }
+
+        @Override
+        public CameraStatus[] newArray(int size) {
+            return new CameraStatus[size];
+        }
+    };
+};
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 145b1d0..e8e989f 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -23,6 +23,7 @@
 import android.hardware.ICameraService;
 import android.hardware.ICameraServiceListener;
 import android.hardware.CameraInfo;
+import android.hardware.CameraStatus;
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.legacy.CameraDeviceUserShim;
 import android.hardware.camera2.legacy.LegacyMetadataMapper;
@@ -92,11 +93,7 @@
      */
     @NonNull
     public String[] getCameraIdList() throws CameraAccessException {
-        synchronized (mLock) {
-            // ID list creation handles various known failures in device enumeration, so only
-            // exceptions it'll throw are unexpected, and should be propagated upward.
-            return getOrCreateDeviceIdListLocked().toArray(new String[0]);
-        }
+        return CameraManagerGlobal.get().getCameraIdList();
     }
 
     /**
@@ -218,18 +215,10 @@
         CameraCharacteristics characteristics = null;
 
         synchronized (mLock) {
-            if (!getOrCreateDeviceIdListLocked().contains(cameraId)) {
-                throw new IllegalArgumentException(String.format("Camera id %s does not match any" +
-                        " currently connected camera device", cameraId));
-            }
-
-            int id = Integer.parseInt(cameraId);
-
             /*
              * Get the camera characteristics from the camera service directly if it supports it,
              * otherwise get them from the legacy shim instead.
              */
-
             ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
             if (cameraService == null) {
                 throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
@@ -239,6 +228,8 @@
                 if (!supportsCamera2ApiLocked(cameraId)) {
                     // Legacy backwards compatibility path; build static info from the camera
                     // parameters
+                    int id = Integer.parseInt(cameraId);
+
                     String parameters = cameraService.getLegacyParameters(id);
 
                     CameraInfo info = cameraService.getCameraInfo(id);
@@ -246,7 +237,7 @@
                     characteristics = LegacyMetadataMapper.createCharacteristics(parameters, info);
                 } else {
                     // Normal path: Get the camera characteristics directly from the camera service
-                    CameraMetadataNative info = cameraService.getCameraCharacteristics(id);
+                    CameraMetadataNative info = cameraService.getCameraCharacteristics(cameraId);
 
                     characteristics = new CameraCharacteristics(info);
                 }
@@ -303,14 +294,6 @@
 
             ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks();
 
-            int id;
-            try {
-                id = Integer.parseInt(cameraId);
-            } catch (NumberFormatException e) {
-                throw new IllegalArgumentException("Expected cameraId to be numeric, but it was: "
-                        + cameraId);
-            }
-
             try {
                 if (supportsCamera2ApiLocked(cameraId)) {
                     // Use cameraservice's cameradeviceclient implementation for HAL3.2+ devices
@@ -320,10 +303,18 @@
                             ICameraService.ERROR_DISCONNECTED,
                             "Camera service is currently unavailable");
                     }
-                    cameraUser = cameraService.connectDevice(callbacks, id,
+                    cameraUser = cameraService.connectDevice(callbacks, cameraId,
                             mContext.getOpPackageName(), uid);
                 } else {
                     // Use legacy camera implementation for HAL1 devices
+                    int id;
+                    try {
+                        id = Integer.parseInt(cameraId);
+                    } catch (NumberFormatException e) {
+                        throw new IllegalArgumentException("Expected cameraId to be numeric, but it was: "
+                                + cameraId);
+                    }
+
                     Log.i(TAG, "Using legacy camera HAL.");
                     cameraUser = CameraDeviceUserShim.connectBinderShim(callbacks, id);
                 }
@@ -672,69 +663,6 @@
     }
 
     /**
-     * Return or create the list of currently connected camera devices.
-     *
-     * <p>In case of errors connecting to the camera service, will return an empty list.</p>
-     */
-    private ArrayList<String> getOrCreateDeviceIdListLocked() throws CameraAccessException {
-        if (mDeviceIdList == null) {
-            int numCameras = 0;
-            ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
-            ArrayList<String> deviceIdList = new ArrayList<>();
-
-            // If no camera service, then no devices
-            if (cameraService == null) {
-                return deviceIdList;
-            }
-
-            try {
-                numCameras = cameraService.getNumberOfCameras(CAMERA_TYPE_ALL);
-            } catch(ServiceSpecificException e) {
-                throwAsPublicException(e);
-            } catch (RemoteException e) {
-                // camera service just died - if no camera service, then no devices
-                return deviceIdList;
-            }
-
-            for (int i = 0; i < numCameras; ++i) {
-                // Non-removable cameras use integers starting at 0 for their
-                // identifiers
-                boolean isDeviceSupported = false;
-                try {
-                    CameraMetadataNative info = cameraService.getCameraCharacteristics(i);
-                    if (!info.isEmpty()) {
-                        isDeviceSupported = true;
-                    } else {
-                        throw new AssertionError("Expected to get non-empty characteristics");
-                    }
-                } catch(ServiceSpecificException e) {
-                    // DISCONNECTED means that the HAL reported an low-level error getting the
-                    // device info; ILLEGAL_ARGUMENT means that this devices is not supported.
-                    // Skip listing the device.  Other errors,
-                    // propagate exception onward
-                    if (e.errorCode != ICameraService.ERROR_DISCONNECTED ||
-                            e.errorCode != ICameraService.ERROR_ILLEGAL_ARGUMENT) {
-                        throwAsPublicException(e);
-                    }
-                } catch(RemoteException e) {
-                    // Camera service died - no devices to list
-                    deviceIdList.clear();
-                    return deviceIdList;
-                }
-
-                if (isDeviceSupported) {
-                    deviceIdList.add(String.valueOf(i));
-                } else {
-                    Log.w(TAG, "Error querying camera device " + i + " for listing.");
-                }
-
-            }
-            mDeviceIdList = deviceIdList;
-        }
-        return mDeviceIdList;
-    }
-
-    /**
      * Queries the camera service if it supports the camera2 api directly, or needs a shim.
      *
      * @param cameraId a non-{@code null} camera identifier
@@ -752,8 +680,6 @@
      * @return {@code true} if connecting will work for that device version.
      */
     private boolean supportsCameraApiLocked(String cameraId, int apiVersion) {
-        int id = Integer.parseInt(cameraId);
-
         /*
          * Possible return values:
          * - NO_ERROR => CameraX API is supported
@@ -767,7 +693,7 @@
             // If no camera service, no support
             if (cameraService == null) return false;
 
-            return cameraService.supportsCameraApi(id, apiVersion);
+            return cameraService.supportsCameraApi(cameraId, apiVersion);
         } catch (RemoteException e) {
             // Camera service is now down, no support for any API level
         }
@@ -880,7 +806,10 @@
             }
 
             try {
-                cameraService.addListener(this);
+                CameraStatus[] cameraStatuses = cameraService.addListener(this);
+                for (CameraStatus c : cameraStatuses) {
+                    onStatusChangedLocked(c.status, c.cameraId);
+                }
                 mCameraService = cameraService;
             } catch(ServiceSpecificException e) {
                 // Unexpected failure
@@ -890,6 +819,36 @@
             }
         }
 
+        /**
+         * Get a list of all camera IDs that are at least PRESENT; ignore devices that are
+         * NOT_PRESENT or ENUMERATING, since they cannot be used by anyone.
+         */
+        public String[] getCameraIdList() {
+            String[] cameraIds = null;
+            synchronized(mLock) {
+                // Try to make sure we have an up-to-date list of camera devices.
+                connectCameraServiceLocked();
+
+                int idCount = 0;
+                for (int i = 0; i < mDeviceStatus.size(); i++) {
+                    int status = mDeviceStatus.valueAt(i);
+                    if (status == ICameraServiceListener.STATUS_NOT_PRESENT ||
+                            status == ICameraServiceListener.STATUS_ENUMERATING) continue;
+                    idCount++;
+                }
+                cameraIds = new String[idCount];
+                idCount = 0;
+                for (int i = 0; i < mDeviceStatus.size(); i++) {
+                    int status = mDeviceStatus.valueAt(i);
+                    if (status == ICameraServiceListener.STATUS_NOT_PRESENT ||
+                            status == ICameraServiceListener.STATUS_ENUMERATING) continue;
+                    cameraIds[idCount] = mDeviceStatus.keyAt(i);
+                    idCount++;
+                }
+            }
+            return cameraIds;
+        }
+
         public void setTorchMode(String cameraId, boolean enabled) throws CameraAccessException {
             synchronized(mLock) {
 
@@ -1173,9 +1132,9 @@
          * Callback from camera service notifying the process about camera availability changes
          */
         @Override
-        public void onStatusChanged(int status, int cameraId) throws RemoteException {
+        public void onStatusChanged(int status, String cameraId) throws RemoteException {
             synchronized(mLock) {
-                onStatusChangedLocked(status, String.valueOf(cameraId));
+                onStatusChangedLocked(status, cameraId);
             }
         }
 
diff --git a/core/java/android/hardware/location/GeofenceHardware.java b/core/java/android/hardware/location/GeofenceHardware.java
index f537a77..66dd9fc 100644
--- a/core/java/android/hardware/location/GeofenceHardware.java
+++ b/core/java/android/hardware/location/GeofenceHardware.java
@@ -167,6 +167,7 @@
             mMonitorCallbacks = new HashMap<GeofenceHardwareMonitorCallback,
                     GeofenceHardwareMonitorCallbackWrapper>();
 
+    /** @hide */
     public GeofenceHardware(IGeofenceHardware service) {
         mService = service;
     }
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 0073dde..28e392e 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -87,6 +87,13 @@
      * sent as an extra; it should be consulted to see what kind of
      * connectivity event occurred.
      * <p/>
+     * Apps targeting Android 7.0 (API level 24) and higher do not receive this
+     * broadcast if they declare the broadcast receiver in their manifest. Apps
+     * will still receive broadcasts if they register their
+     * {@link android.content.BroadcastReceiver} with
+     * {@link android.content.Context#registerReceiver Context.registerReceiver()}
+     * and that context is still valid.
+     * <p/>
      * If this is a connection that was the result of failing over from a
      * disconnected network, then the FAILOVER_CONNECTION boolean extra is
      * set to true.
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index f081e93..fe9563d 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -52,6 +52,17 @@
     public native static void attachRaFilter(FileDescriptor fd, int packetType) throws SocketException;
 
     /**
+     * Attaches a socket filter that accepts L2-L4 signaling traffic required for IP connectivity.
+     *
+     * This includes: all ARP, ICMPv6 RS/RA/NS/NA messages, and DHCPv4 exchanges.
+     *
+     * @param fd the socket's {@link FileDescriptor}.
+     * @param packetType the hardware address type, one of ARPHRD_*.
+     */
+    public native static void attachControlPacketFilter(FileDescriptor fd, int packetType)
+            throws SocketException;
+
+    /**
      * Configures a socket for receiving ICMPv6 router solicitations and sending advertisements.
      * @param fd the socket's {@link FileDescriptor}.
      * @param ifIndex the interface index.
diff --git a/core/java/android/net/metrics/ConnectStats.java b/core/java/android/net/metrics/ConnectStats.java
new file mode 100644
index 0000000..3114ff8
--- /dev/null
+++ b/core/java/android/net/metrics/ConnectStats.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2016 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.net.metrics;
+
+import android.system.OsConstants;
+import android.util.IntArray;
+import android.util.SparseIntArray;
+import com.android.internal.util.TokenBucket;
+import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.ConnectStatistics;
+import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.Pair;
+
+/**
+ * A class that aggregates connect() statistics and helps build
+ * IpConnectivityLogClass.ConnectStatistics instances.
+ *
+ * {@hide}
+ */
+public class ConnectStats {
+    private final static int EALREADY     = OsConstants.EALREADY;
+    private final static int EINPROGRESS  = OsConstants.EINPROGRESS;
+
+    /** How many events resulted in a given errno. */
+    private final SparseIntArray mErrnos = new SparseIntArray();
+    /** Latencies of blocking connects. TODO: add non-blocking connects latencies. */
+    private final IntArray mLatencies = new IntArray();
+    /** TokenBucket for rate limiting latency recording. */
+    private final TokenBucket mLatencyTb;
+    /** Maximum number of latency values recorded. */
+    private final int mMaxLatencyRecords;
+    /** Total count of successful connects. */
+    private int mConnectCount = 0;
+    /** Total count of successful connects with IPv6 socket address. */
+    private int mIpv6ConnectCount = 0;
+
+    public ConnectStats(TokenBucket tb, int maxLatencyRecords) {
+        mLatencyTb = tb;
+        mMaxLatencyRecords = maxLatencyRecords;
+    }
+
+    public ConnectStatistics toProto() {
+        ConnectStatistics stats = new ConnectStatistics();
+        stats.connectCount = mConnectCount;
+        stats.ipv6AddrCount = mIpv6ConnectCount;
+        stats.latenciesMs = mLatencies.toArray();
+        stats.errnosCounters = toPairArrays(mErrnos);
+        return stats;
+    }
+
+    public void addEvent(int errno, int latencyMs, String ipAddr) {
+        if (isSuccess(errno)) {
+            countConnect(ipAddr);
+            countLatency(errno, latencyMs);
+        } else {
+            countError(errno);
+        }
+    }
+
+    private void countConnect(String ipAddr) {
+        mConnectCount++;
+        if (isIPv6(ipAddr)) mIpv6ConnectCount++;
+    }
+
+    private void countLatency(int errno, int ms) {
+        if (isNonBlocking(errno)) {
+            // Ignore connect() on non-blocking sockets
+            return;
+        }
+        if (!mLatencyTb.get()) {
+            // Rate limited
+            return;
+        }
+        if (mLatencies.size() >= mMaxLatencyRecords) {
+            // Hard limit the total number of latency measurements.
+            return;
+        }
+        mLatencies.add(ms);
+    }
+
+    private void countError(int errno) {
+        final int newcount = mErrnos.get(errno, 0) + 1;
+        mErrnos.put(errno, newcount);
+    }
+
+    private static boolean isSuccess(int errno) {
+        return (errno == 0) || isNonBlocking(errno);
+    }
+
+    private static boolean isNonBlocking(int errno) {
+        // On non-blocking TCP sockets, connect() immediately returns EINPROGRESS.
+        // On non-blocking TCP sockets that are connecting, connect() immediately returns EALREADY.
+        return (errno == EINPROGRESS) || (errno == EALREADY);
+    }
+
+    private static boolean isIPv6(String ipAddr) {
+        return ipAddr.contains(":");
+    }
+
+    private static Pair[] toPairArrays(SparseIntArray counts) {
+        final int s = counts.size();
+        Pair[] pairs = new Pair[s];
+        for (int i = 0; i < s; i++) {
+            Pair p = new Pair();
+            p.key = counts.keyAt(i);
+            p.value = counts.valueAt(i);
+            pairs[i] = p;
+        }
+        return pairs;
+    }
+}
diff --git a/core/java/android/os/DropBoxManager.aidl b/core/java/android/os/DropBoxManager.aidl
index 6474ec2..241e93b 100644
--- a/core/java/android/os/DropBoxManager.aidl
+++ b/core/java/android/os/DropBoxManager.aidl
@@ -16,4 +16,4 @@
 
 package android.os;
 
-parcelable DropBoxManager.Entry;
+parcelable DropBoxManager.Entry cpp_header "android/os/DropBoxManager.h";
diff --git a/core/java/android/os/IIncidentManager.aidl b/core/java/android/os/IIncidentManager.aidl
new file mode 100644
index 0000000..1a76648
--- /dev/null
+++ b/core/java/android/os/IIncidentManager.aidl
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.os.IIncidentReportCompletedListener;
+import android.os.IIncidentReportStatusListener;
+import android.os.IncidentReportArgs;
+
+/**
+  * Binder interface to report system health incidents.
+  * {@hide}
+  */
+oneway interface IIncidentManager {
+
+    /**
+     * Takes a report with the given args, reporting status to the optional listener.
+     *
+     * When the report is completed, the system report listener will be notified.
+     */
+    void reportIncident(in IncidentReportArgs args);
+
+    /**
+     * Takes a report with the given args, reporting status to the optional listener.
+     *
+     * When the report is completed, the system report listener will be notified.
+     */
+    void reportIncidentToStream(in IncidentReportArgs args,
+            @nullable IIncidentReportStatusListener listener,
+            FileDescriptor stream);
+
+    /**
+     * Tell the incident daemon that the android system server is up and running.
+     */
+    void systemRunning();
+}
diff --git a/core/java/android/os/IIncidentReportCompletedListener.aidl b/core/java/android/os/IIncidentReportCompletedListener.aidl
new file mode 100644
index 0000000..2d66bf6
--- /dev/null
+++ b/core/java/android/os/IIncidentReportCompletedListener.aidl
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+  * Listener for incident report status
+  *
+  * {@hide}
+  */
+oneway interface IIncidentReportCompletedListener {
+    /**
+     * Called when there has been an incident report.
+     *
+     * The system service implementing this method should delete or move the file
+     * after it is finished with it.
+     */
+    void onIncidentReport(String filename);
+}
diff --git a/core/java/android/os/IIncidentReportStatusListener.aidl b/core/java/android/os/IIncidentReportStatusListener.aidl
new file mode 100644
index 0000000..7be2ac8
--- /dev/null
+++ b/core/java/android/os/IIncidentReportStatusListener.aidl
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+  * Listener for incident report status
+  *
+  * {@hide}
+  */
+oneway interface IIncidentReportStatusListener {
+    const int STATUS_STARTING = 1;
+    const int STATUS_FINISHED = 2;
+
+    void onReportStarted();
+
+    void onReportSectionStatus(int section, int status);
+
+    void onReportFinished();
+    void onReportFailed();
+}
diff --git a/core/java/android/os/IncidentManager.java b/core/java/android/os/IncidentManager.java
new file mode 100644
index 0000000..976d594
--- /dev/null
+++ b/core/java/android/os/IncidentManager.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.content.Context;
+import android.os.IIncidentManager;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.util.Slog;
+
+/**
+ * Class to take an incident report.
+ *
+ * @hide
+ */
+@SystemApi
+@TestApi
+public class IncidentManager {
+    private static final String TAG = "incident";
+
+    private Context mContext;
+
+    /**
+     * @hide
+     */
+    public IncidentManager(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Take an incident report and put it in dropbox.
+     */
+    public void reportIncident(IncidentReportArgs args) {
+        final IIncidentManager service = IIncidentManager.Stub.asInterface(
+                ServiceManager.getService("incident"));
+        if (service == null) {
+            Slog.e(TAG, "reportIncident can't find incident binder service");
+            return;
+        }
+
+        try {
+            service.reportIncident(args);
+        } catch (RemoteException ex) {
+            Slog.e(TAG, "reportIncident failed", ex);
+        }
+    }
+
+    /**
+     * Convenience method to trigger an incident report and put it in dropbox.
+     * <p>
+     * The fields that are reported will be looked up in the system setting named by
+     * the settingName parameter.  The setting must match one of these patterns:
+     *      The string "disabled": The report will not be taken.
+     *      The string "all": The report will taken with all sections.
+     *      The string "none": The report will taken with no sections, but with the header.
+     *      A comma separated list of field numbers: The report will have these fields.
+     * <p>
+     * The header parameter will be added as a header for the incident report.  Fill in a
+     * {@link android.util.proto.ProtoOutputStream ProtoOutputStream}, and then call the
+     * {@link android.util.proto.ProtoOutputStream#bytes bytes()} method to retrieve
+     * the encoded data for the header.
+     */
+    public void reportIncident(String settingName, byte[] headerProto) {
+        // Sections
+        String setting = Settings.System.getString(mContext.getContentResolver(), settingName);
+        IncidentReportArgs args;
+        try {
+            args = IncidentReportArgs.parseSetting(setting);
+        } catch (IllegalArgumentException ex) {
+            Slog.w(TAG, "Bad value for incident report setting '" + settingName + "'", ex);
+            return;
+        }
+        if (args == null) {
+            Slog.i(TAG, "Incident report requested but disabled: " + settingName);
+            return;
+        }
+
+        // Header
+        args.addHeader(headerProto);
+
+        // Look up the service
+        final IIncidentManager service = IIncidentManager.Stub.asInterface(
+                ServiceManager.getService("incident"));
+        if (service == null) {
+            Slog.e(TAG, "reportIncident can't find incident binder service");
+            return;
+        }
+
+        // Call the service
+        Slog.i(TAG, "Taking incident report: " + settingName);
+        try {
+            service.reportIncident(args);
+        } catch (RemoteException ex) {
+            Slog.e(TAG, "reportIncident failed", ex);
+        }
+    }
+}
+
diff --git a/core/java/android/os/IncidentReportArgs.aidl b/core/java/android/os/IncidentReportArgs.aidl
new file mode 100644
index 0000000..bbddf59
--- /dev/null
+++ b/core/java/android/os/IncidentReportArgs.aidl
@@ -0,0 +1,20 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+parcelable IncidentReportArgs cpp_header "android/os/IncidentReportArgs.h";
+
diff --git a/core/java/android/os/IncidentReportArgs.java b/core/java/android/os/IncidentReportArgs.java
new file mode 100644
index 0000000..ce2ae10
--- /dev/null
+++ b/core/java/android/os/IncidentReportArgs.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.content.Intent;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.IntArray;
+
+import java.util.ArrayList;
+
+/**
+ * The arguments for an incident report.
+ * {@hide}
+ */
+@SystemApi
+@TestApi
+public final class IncidentReportArgs implements Parcelable {
+
+    private final IntArray mSections = new IntArray();
+    private final ArrayList<byte[]> mHeaders = new ArrayList<byte[]>();
+    private boolean mAll;
+
+    /**
+     * Construct an incident report args with no fields.
+     */
+    public IncidentReportArgs() {
+    }
+
+    /**
+     * Construct an incdent report args from the given parcel.
+     */
+    public IncidentReportArgs(Parcel in) {
+        readFromParcel(in);
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mAll ? 1 : 0);
+
+        int N = mSections.size();
+        out.writeInt(N);
+        for (int i=0; i<N; i++) {
+            out.writeInt(mSections.get(i));
+        }
+
+        N = mHeaders.size();
+        out.writeInt(N);
+        for (int i=0; i<N; i++) {
+            out.writeByteArray(mHeaders.get(i));
+        }
+    }
+
+    public void readFromParcel(Parcel in) {
+        mAll = in.readInt() != 0;
+
+        mSections.clear();
+        int N = in.readInt();
+        for (int i=0; i<N; i++) {
+            mSections.add(in.readInt());
+        }
+
+        mHeaders.clear();
+        N = in.readInt();
+        for (int i=0; i<N; i++) {
+            mHeaders.add(in.createByteArray());
+        }
+    }
+
+    public static final Parcelable.Creator<IncidentReportArgs> CREATOR
+            = new Parcelable.Creator<IncidentReportArgs>() {
+        public IncidentReportArgs createFromParcel(Parcel in) {
+            return new IncidentReportArgs(in);
+        }
+
+        public IncidentReportArgs[] newArray(int size) {
+            return new IncidentReportArgs[size];
+        }
+    };
+
+    /**
+     * Print this report as a string.
+     */
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("Incident(");
+        if (mAll) {
+            sb.append("all");
+        } else {
+            final int N = mSections.size();
+            if (N > 0) {
+                sb.append(mSections.get(0));
+            }
+            for (int i=1; i<N; i++) {
+                sb.append(" ");
+                sb.append(mSections.get(i));
+            }
+        }
+        sb.append(", ");
+        sb.append(mHeaders.size());
+        sb.append(" headers)");
+        return sb.toString();
+    }
+
+    /**
+     * Set this incident report to include all fields.
+     */
+    public void setAll(boolean all) {
+        mAll = all;
+        if (all) {
+            mSections.clear();
+        }
+    }
+
+    /**
+     * Add this section to the incident report.
+     */
+    public void addSection(int section) {
+        if (!mAll) {
+            mSections.add(section);
+        }
+    }
+
+    /**
+     * Returns whether the incident report will include all fields.
+     */
+    public boolean isAll() {
+        return mAll;
+    }
+
+    /**
+     * Returns whether this section will be included in the incident report.
+     */
+    public boolean containsSection(int section) {
+        return mAll || mSections.indexOf(section) >= 0;
+    }
+
+    public int sectionCount() {
+        return mSections.size();
+    }
+
+    public void addHeader(byte[] header) {
+        mHeaders.add(header);
+    }
+
+    /**
+     * Parses an incident report config as described in the system setting.
+     *
+     * @see IncidentManager#reportIncident
+     */
+    public static IncidentReportArgs parseSetting(String setting)
+            throws IllegalArgumentException {
+        if (setting == null || setting.length() == 0) {
+            return null;
+        }
+        setting = setting.trim();
+        if (setting.length() == 0 || "disabled".equals(setting)) {
+            return null;
+        }
+
+        final IncidentReportArgs args = new IncidentReportArgs();
+
+        if ("all".equals(setting)) {
+            args.setAll(true);
+            return args;
+        } else if ("none".equals(setting)) {
+            return args;
+        }
+
+        final String[] splits = setting.split(",");
+        final int N = splits.length;
+        for (int i=0; i<N; i++) {
+            final String str = splits[i].trim();
+            if (str.length() == 0) {
+                continue;
+            }
+            int section;
+            try {
+                section = Integer.parseInt(str);
+            } catch (NumberFormatException ex) {
+                throw new IllegalArgumentException("Malformed setting. Bad integer at section"
+                        + " index " + i + ": section='" + str + "' setting='" + setting + "'");
+            }
+            if (section < 1) {
+                throw new IllegalArgumentException("Malformed setting. Illegal section at"
+                        + " index " + i + ": section='" + str + "' setting='" + setting + "'");
+            }
+            args.addSection(section);
+        }
+
+        return args;
+    }
+}
+
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 4eee854..9cd1a42 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -398,6 +398,10 @@
      * make easily identifyable processes even if you are using the same base
      * <var>processClass</var> to start them.
      * 
+     * When invokeWith is not null, the process will be started as a fresh app
+     * and not a zygote fork. Note that this is only allowed for uid 0 or when
+     * debugFlags contains DEBUG_ENABLE_DEBUGGER.
+     *
      * @param processClass The class to use as the process's main entry
      *                     point.
      * @param niceName A more readable name to use for the process.
@@ -410,6 +414,7 @@
      * @param abi non-null the ABI this app should be started with.
      * @param instructionSet null-ok the instruction set to use.
      * @param appDataDir null-ok the data directory of the app.
+     * @param invokeWith null-ok the command to invoke with.
      * @param zygoteArgs Additional arguments to supply to the zygote process.
      * 
      * @return An object that describes the result of the attempt to start the process.
@@ -426,10 +431,11 @@
                                   String abi,
                                   String instructionSet,
                                   String appDataDir,
+                                  String invokeWith,
                                   String[] zygoteArgs) {
         return zygoteProcess.start(processClass, niceName, uid, gid, gids,
                     debugFlags, mountExternal, targetSdkVersion, seInfo,
-                    abi, instructionSet, appDataDir, zygoteArgs);
+                    abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
     }
 
     /** @hide */
@@ -442,10 +448,11 @@
                                   String abi,
                                   String instructionSet,
                                   String appDataDir,
+                                  String invokeWith,
                                   String[] zygoteArgs) {
         return WebViewZygote.getProcess().start(processClass, niceName, uid, gid, gids,
                     debugFlags, mountExternal, targetSdkVersion, seInfo,
-                    abi, instructionSet, appDataDir, zygoteArgs);
+                    abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
     }
 
     /**
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index 6a751e8..78820b5 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -86,7 +86,7 @@
      */
     public static String get(String key) {
         if (key.length() > PROP_NAME_MAX) {
-            throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
+            throw newKeyTooLargeException(key);
         }
         if (TRACK_KEY_ACCESS) onKeyAccess(key);
         return native_get(key);
@@ -99,7 +99,7 @@
      */
     public static String get(String key, String def) {
         if (key.length() > PROP_NAME_MAX) {
-            throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
+            throw newKeyTooLargeException(key);
         }
         if (TRACK_KEY_ACCESS) onKeyAccess(key);
         return native_get(key, def);
@@ -115,7 +115,7 @@
      */
     public static int getInt(String key, int def) {
         if (key.length() > PROP_NAME_MAX) {
-            throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
+            throw newKeyTooLargeException(key);
         }
         if (TRACK_KEY_ACCESS) onKeyAccess(key);
         return native_get_int(key, def);
@@ -131,7 +131,7 @@
      */
     public static long getLong(String key, long def) {
         if (key.length() > PROP_NAME_MAX) {
-            throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
+            throw newKeyTooLargeException(key);
         }
         if (TRACK_KEY_ACCESS) onKeyAccess(key);
         return native_get_long(key, def);
@@ -152,7 +152,7 @@
      */
     public static boolean getBoolean(String key, boolean def) {
         if (key.length() > PROP_NAME_MAX) {
-            throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
+            throw newKeyTooLargeException(key);
         }
         if (TRACK_KEY_ACCESS) onKeyAccess(key);
         return native_get_boolean(key, def);
@@ -165,11 +165,10 @@
      */
     public static void set(String key, String val) {
         if (key.length() > PROP_NAME_MAX) {
-            throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
+            throw newKeyTooLargeException(key);
         }
         if (val != null && val.length() > PROP_VALUE_MAX) {
-            throw new IllegalArgumentException("val.length > " +
-                PROP_VALUE_MAX);
+            throw newValueTooLargeException(key, val);
         }
         if (TRACK_KEY_ACCESS) onKeyAccess(key);
         native_set(key, val);
@@ -197,6 +196,16 @@
         }
     }
 
+    private static IllegalArgumentException newKeyTooLargeException(String key) {
+        return new IllegalArgumentException("system property key '" + key + "' is longer than "
+                + PROP_NAME_MAX + " characters");
+    }
+
+    private static IllegalArgumentException newValueTooLargeException(String key, String value) {
+        return new IllegalArgumentException("value of system property '" + key + "' is longer than "
+                + PROP_VALUE_MAX + " characters: " + value);
+    }
+
     /*
      * Notifies listeners that a system property has changed
      */
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 2c0b2a0..0a32f0d 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -253,6 +253,20 @@
     public static final String DISALLOW_REMOVE_USER = "no_remove_user";
 
     /**
+     * Specifies if managed profiles of this user can be removed, other than by its profile owner.
+     * The default value is <code>false</code>.
+     * <p>
+     * This restriction can only be set by device owners.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_REMOVE_MANAGED_PROFILE = "no_remove_managed_profile";
+
+    /**
      * Specifies if a user is disallowed from enabling or
      * accessing debugging features. The default value is <code>false</code>.
      *
@@ -322,8 +336,8 @@
     public static final String DISALLOW_FACTORY_RESET = "no_factory_reset";
 
     /**
-     * Specifies if a user is disallowed from adding new users and
-     * profiles. This can only be set by device owners and profile owners on the primary user.
+     * Specifies if a user is disallowed from adding new users. This can only be set by device
+     * owners and profile owners on the primary user.
      * The default value is <code>false</code>.
      * <p>This restriction has no effect on secondary users and managed profiles since only the
      * primary user can add other users.
@@ -337,6 +351,20 @@
     public static final String DISALLOW_ADD_USER = "no_add_user";
 
     /**
+     * Specifies if a user is disallowed from adding managed profiles.
+     * <p>The default value for an unmanaged user is <code>false</code>.
+     * For users with a device owner set, the default is <code>true</code>
+     * <p>This restriction can only be set by device owners.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_ADD_MANAGED_PROFILE = "no_add_managed_profile";
+
+    /**
      * Specifies if a user is disallowed from disabling application
      * verification. The default value is <code>false</code>.
      *
@@ -1406,7 +1434,7 @@
 
     /**
      * Similar to {@link #createProfileForUser(String, int, int, String[])}
-     * except bypassing the checking of {@link UserManager#DISALLOW_ADD_USER}.
+     * except bypassing the checking of {@link UserManager#DISALLOW_ADD_MANAGED_PROFILE}.
      * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
      *
      * @see #createProfileForUser(String, int, int, String[])
diff --git a/core/java/android/os/UserManagerInternal.java b/core/java/android/os/UserManagerInternal.java
index 1447e7d..466a7e3 100644
--- a/core/java/android/os/UserManagerInternal.java
+++ b/core/java/android/os/UserManagerInternal.java
@@ -120,7 +120,8 @@
     public abstract void onEphemeralUserStop(int userId);
 
     /**
-     * Same as UserManager.createUser(), but bypasses the check for DISALLOW_ADD_USER.
+     * Same as UserManager.createUser(), but bypasses the check for
+     * {@link UserManager#DISALLOW_ADD_USER} and {@link UserManager#DISALLOW_ADD_MANAGED_PROFILE}
      *
      * <p>Called by the {@link com.android.server.devicepolicy.DevicePolicyManagerService} when
      * createAndManageUser is called by the device owner.
@@ -129,7 +130,8 @@
 
     /**
      * Same as {@link UserManager#removeUser(int userHandle)}, but bypasses the check for
-     * {@link UserManager#DISALLOW_REMOVE_USER} and does not require the
+     * {@link UserManager#DISALLOW_REMOVE_USER} and
+     * {@link UserManager#DISALLOW_REMOVE_MANAGED_PROFILE} and does not require the
      * {@link android.Manifest.permission#MANAGE_USERS} permission.
      */
     public abstract boolean removeUserEvenWhenDisallowed(int userId);
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index c45fe5a..5ac33a1 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -170,6 +170,10 @@
      * make easily identifyable processes even if you are using the same base
      * <var>processClass</var> to start them.
      *
+     * When invokeWith is not null, the process will be started as a fresh app
+     * and not a zygote fork. Note that this is only allowed for uid 0 or when
+     * debugFlags contains DEBUG_ENABLE_DEBUGGER.
+     *
      * @param processClass The class to use as the process's main entry
      *                     point.
      * @param niceName A more readable name to use for the process.
@@ -182,6 +186,7 @@
      * @param abi non-null the ABI this app should be started with.
      * @param instructionSet null-ok the instruction set to use.
      * @param appDataDir null-ok the data directory of the app.
+     * @param invokeWith null-ok the command to invoke with.
      * @param zygoteArgs Additional arguments to supply to the zygote process.
      *
      * @return An object that describes the result of the attempt to start the process.
@@ -196,11 +201,12 @@
                                                   String abi,
                                                   String instructionSet,
                                                   String appDataDir,
+                                                  String invokeWith,
                                                   String[] zygoteArgs) {
         try {
             return startViaZygote(processClass, niceName, uid, gid, gids,
                     debugFlags, mountExternal, targetSdkVersion, seInfo,
-                    abi, instructionSet, appDataDir, zygoteArgs);
+                    abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
         } catch (ZygoteStartFailedEx ex) {
             Log.e(LOG_TAG,
                     "Starting VM process through Zygote failed");
@@ -330,6 +336,7 @@
                                                       String abi,
                                                       String instructionSet,
                                                       String appDataDir,
+                                                      String invokeWith,
                                                       String[] extraArgs)
                                                       throws ZygoteStartFailedEx {
         ArrayList<String> argsForZygote = new ArrayList<String>();
@@ -407,6 +414,11 @@
             argsForZygote.add("--app-data-dir=" + appDataDir);
         }
 
+        if (invokeWith != null) {
+            argsForZygote.add("--invoke-with");
+            argsForZygote.add(invokeWith);
+        }
+
         argsForZygote.add(processClass);
 
         if (extraArgs != null) {
diff --git a/core/java/android/text/AndroidCharacter.java b/core/java/android/text/AndroidCharacter.java
index b150b6e..c5f1a01 100644
--- a/core/java/android/text/AndroidCharacter.java
+++ b/core/java/android/text/AndroidCharacter.java
@@ -17,9 +17,11 @@
 package android.text;
 
 /**
- * AndroidCharacter exposes some character properties that are not
- * easily accessed from java.lang.Character.
+ * AndroidCharacter exposes some character properties that used to be not
+ * easily accessed from java.lang.Character, but are now available in ICU.
+ * @deprecated Use various methods from {@link android.icu.lang.UCharacter}, instead.
  */
+@Deprecated
 public class AndroidCharacter
 {
     public static final int EAST_ASIAN_WIDTH_NEUTRAL = 0;
diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java
index de8aa5a..c3aac74 100644
--- a/core/java/android/text/Html.java
+++ b/core/java/android/text/Html.java
@@ -40,8 +40,6 @@
 import android.text.style.URLSpan;
 import android.text.style.UnderlineSpan;
 
-import com.android.internal.util.ArrayUtils;
-
 import org.ccil.cowan.tagsoup.HTMLSchema;
 import org.ccil.cowan.tagsoup.Parser;
 import org.xml.sax.Attributes;
@@ -342,19 +340,10 @@
     }
 
     private static String getTextDirection(Spanned text, int start, int end) {
-        final int len = end - start;
-        final byte[] levels = ArrayUtils.newUnpaddedByteArray(len);
-        final char[] buffer = TextUtils.obtain(len);
-        TextUtils.getChars(text, start, end, buffer, 0);
-
-        int paraDir = AndroidBidi.bidi(Layout.DIR_REQUEST_DEFAULT_LTR, buffer, levels, len,
-                false /* no info */);
-        switch(paraDir) {
-            case Layout.DIR_RIGHT_TO_LEFT:
-                return " dir=\"rtl\"";
-            case Layout.DIR_LEFT_TO_RIGHT:
-            default:
-                return " dir=\"ltr\"";
+        if (TextDirectionHeuristics.FIRSTSTRONG_LTR.isRtl(text, start, end - start)) {
+            return " dir=\"rtl\"";
+        } else {
+            return " dir=\"ltr\"";
         }
     }
 
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 6262fc2..58bc9a7 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -22,6 +22,7 @@
 import android.annotation.PluralsRes;
 import android.content.Context;
 import android.content.res.Resources;
+import android.icu.lang.UCharacter;
 import android.icu.util.ULocale;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -545,9 +546,10 @@
         }
 
         public char charAt(int off) {
-            return AndroidCharacter.getMirror(mSource.charAt(mEnd - 1 - off));
+            return (char) UCharacter.getMirror(mSource.charAt(mEnd - 1 - off));
         }
 
+        @SuppressWarnings("deprecation")
         public void getChars(int start, int end, char[] dest, int destoff) {
             TextUtils.getChars(mSource, start + mStart, end + mStart,
                                dest, destoff);
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index f16e714..e19b2c7 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -19,6 +19,10 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.icu.text.MeasureFormat;
+import android.icu.text.MeasureFormat.FormatWidth;
+import android.icu.util.Measure;
+import android.icu.util.MeasureUnit;
 
 import com.android.internal.R;
 
@@ -351,26 +355,54 @@
     }
 
     /**
-     * Return given duration in a human-friendly format. For example, "4
-     * minutes" or "1 second". Returns only largest meaningful unit of time,
+     * Returns the given duration in a human-friendly format. For example,
+     * "4 minutes" or "1 second". Returns only the largest meaningful unit of time,
      * from seconds up to hours.
      *
      * @hide
      */
     public static CharSequence formatDuration(long millis) {
-        final Resources res = Resources.getSystem();
+        return formatDuration(millis, LENGTH_LONG);
+    }
+
+    /**
+     * Returns the given duration in a human-friendly format. For example,
+     * "4 minutes" or "1 second". Returns only the largest meaningful unit of time,
+     * from seconds up to hours.
+     * <p>
+     * You can use abbrev to specify a preference for abbreviations (but note that some
+     * locales may not have abbreviations). Use LENGTH_LONG for the full spelling (e.g. "2 hours"),
+     * LENGTH_SHORT for the abbreviated spelling if available (e.g. "2 hr"), and LENGTH_SHORTEST for
+     * the briefest form available (e.g. "2h").
+     * @hide
+     */
+    public static CharSequence formatDuration(long millis, int abbrev) {
+        final FormatWidth width;
+        switch (abbrev) {
+            case LENGTH_LONG:
+                width = FormatWidth.WIDE;
+                break;
+            case LENGTH_SHORT:
+            case LENGTH_SHORTER:
+            case LENGTH_MEDIUM:
+                width = FormatWidth.SHORT;
+                break;
+            case LENGTH_SHORTEST:
+                width = FormatWidth.NARROW;
+                break;
+            default:
+                width = FormatWidth.WIDE;
+        }
+        final MeasureFormat formatter = MeasureFormat.getInstance(Locale.getDefault(), width);
         if (millis >= HOUR_IN_MILLIS) {
             final int hours = (int) ((millis + 1800000) / HOUR_IN_MILLIS);
-            return res.getQuantityString(
-                    com.android.internal.R.plurals.duration_hours, hours, hours);
+            return formatter.format(new Measure(hours, MeasureUnit.HOUR));
         } else if (millis >= MINUTE_IN_MILLIS) {
             final int minutes = (int) ((millis + 30000) / MINUTE_IN_MILLIS);
-            return res.getQuantityString(
-                    com.android.internal.R.plurals.duration_minutes, minutes, minutes);
+            return formatter.format(new Measure(minutes, MeasureUnit.MINUTE));
         } else {
             final int seconds = (int) ((millis + 500) / SECOND_IN_MILLIS);
-            return res.getQuantityString(
-                    com.android.internal.R.plurals.duration_seconds, seconds, seconds);
+            return formatter.format(new Measure(seconds, MeasureUnit.SECOND));
         }
     }
 
diff --git a/core/java/android/view/DisplayListCanvas.java b/core/java/android/view/DisplayListCanvas.java
index 106f172..8f9ae0e 100644
--- a/core/java/android/view/DisplayListCanvas.java
+++ b/core/java/android/view/DisplayListCanvas.java
@@ -23,6 +23,7 @@
 import android.graphics.Paint;
 import android.util.Pools.SynchronizedPool;
 
+import dalvik.annotation.optimization.CriticalNative;
 import dalvik.annotation.optimization.FastNative;
 
 /**
@@ -230,30 +231,37 @@
         }
     }
 
-    @FastNative
-    private static native long nCreateDisplayListCanvas(long node, int width, int height);
-    @FastNative
-    private static native void nResetDisplayListCanvas(long canvas, long node,
-            int width, int height);
-    @FastNative
-    private static native int nGetMaximumTextureWidth();
-    @FastNative
-    private static native int nGetMaximumTextureHeight();
-    @FastNative
-    private static native void nInsertReorderBarrier(long renderer, boolean enableReorder);
+
+    // ------------------ Fast JNI ------------------------
+
     @FastNative
     private static native void nCallDrawGLFunction(long renderer,
             long drawGLFunction, Runnable releasedCallback);
-    @FastNative
+
+
+    // ------------------ Critical JNI ------------------------
+
+    @CriticalNative
+    private static native long nCreateDisplayListCanvas(long node, int width, int height);
+    @CriticalNative
+    private static native void nResetDisplayListCanvas(long canvas, long node,
+            int width, int height);
+    @CriticalNative
+    private static native int nGetMaximumTextureWidth();
+    @CriticalNative
+    private static native int nGetMaximumTextureHeight();
+    @CriticalNative
+    private static native void nInsertReorderBarrier(long renderer, boolean enableReorder);
+    @CriticalNative
     private static native long nFinishRecording(long renderer);
-    @FastNative
+    @CriticalNative
     private static native void nDrawRenderNode(long renderer, long renderNode);
-    @FastNative
+    @CriticalNative
     private static native void nDrawLayer(long renderer, long layer);
-    @FastNative
+    @CriticalNative
     private static native void nDrawCircle(long renderer, long propCx,
             long propCy, long propRadius, long propPaint);
-    @FastNative
+    @CriticalNative
     private static native void nDrawRoundRect(long renderer, long propLeft, long propTop,
             long propRight, long propBottom, long propRx, long propRy, long propPaint);
 }
diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java
index d563f51..3f3d519 100644
--- a/core/java/android/view/FocusFinder.java
+++ b/core/java/android/view/FocusFinder.java
@@ -16,6 +16,8 @@
 
 package android.view;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.graphics.Rect;
 import android.util.ArrayMap;
 import android.util.SparseArray;
@@ -24,6 +26,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.List;
 
 /**
  * The algorithm used for finding the next focusable view in a given direction
@@ -102,6 +105,30 @@
         return next;
     }
 
+    /**
+     * Find the root of the next keyboard navigation cluster after the current one.
+     * @param root Thew view tree to look inside. Cannot be null
+     * @param currentCluster The starting point of the search. Null means the default cluster
+     * @param direction Direction to look
+     * @return The next cluster, or null if none exists
+     */
+    public View findNextKeyboardNavigationCluster(
+            @NonNull ViewGroup root, @Nullable View currentCluster, int direction) {
+        View next = null;
+
+        final ArrayList<View> clusters = mTempList;
+        try {
+            clusters.clear();
+            root.addKeyboardNavigationClusters(clusters, direction);
+            if (!clusters.isEmpty()) {
+                next = findNextKeyboardNavigationCluster(root, currentCluster, clusters, direction);
+            }
+        } finally {
+            clusters.clear();
+        }
+        return next;
+    }
+
     private View findNextUserSpecifiedFocus(ViewGroup root, View focused, int direction) {
         // check for user specified next focus
         View userSetNextFocus = focused.findUserSetNextFocus(root, direction);
@@ -170,6 +197,24 @@
         }
     }
 
+    private View findNextKeyboardNavigationCluster(ViewGroup root, View currentCluster,
+            List<View> clusters, int direction) {
+        final int count = clusters.size();
+
+        switch (direction) {
+            case View.FOCUS_FORWARD:
+            case View.FOCUS_DOWN:
+            case View.FOCUS_RIGHT:
+                return getNextKeyboardNavigationCluster(root, currentCluster, clusters, count);
+            case View.FOCUS_BACKWARD:
+            case View.FOCUS_UP:
+            case View.FOCUS_LEFT:
+                return getPreviousKeyboardNavigationCluster(root, currentCluster, clusters, count);
+            default:
+                throw new IllegalArgumentException("Unknown direction: " + direction);
+        }
+    }
+
     private View findNextFocusInRelativeDirection(ArrayList<View> focusables, ViewGroup root,
             View focused, Rect focusedRect, int direction) {
         try {
@@ -270,6 +315,45 @@
         return null;
     }
 
+    private static View getNextKeyboardNavigationCluster(ViewGroup root, View currentCluster,
+            List<View> clusters, int count) {
+        if (currentCluster == null) {
+            // The current cluster is the default one.
+            // The next cluster after the default one is the first one.
+            // Note that the caller guarantees that 'clusters' is not empty.
+            return clusters.get(0);
+        }
+
+        final int position = clusters.lastIndexOf(currentCluster);
+        if (position >= 0 && position + 1 < count) {
+            // Return the next non-default cluster if we can find it.
+            return clusters.get(position + 1);
+        }
+
+        // The current cluster is the last one. The next one is the default one, i.e. the root.
+        return root;
+    }
+
+    private static View getPreviousKeyboardNavigationCluster(ViewGroup root, View currentCluster,
+            List<View> clusters, int count) {
+        if (currentCluster == null) {
+            // The current cluster is the default one.
+            // The previous cluster before the default one is the last one.
+            // Note that the caller guarantees that 'clusters' is not empty.
+            return clusters.get(count - 1);
+        }
+
+        final int position = clusters.indexOf(currentCluster);
+        if (position > 0) {
+            // Return the previous non-default cluster if we can find it.
+            return clusters.get(position - 1);
+        }
+
+        // The current cluster is the first one. The previous one is the default one, i.e. the
+        // root.
+        return root;
+    }
+
     /**
      * Is rect1 a better candidate than rect2 for a focus search in a particular
      * direction from a source rect?  This is the core routine that determines
diff --git a/core/java/android/view/IPinnedStackListener.aidl b/core/java/android/view/IPinnedStackListener.aidl
index 3050dbb..3c348c5 100644
--- a/core/java/android/view/IPinnedStackListener.aidl
+++ b/core/java/android/view/IPinnedStackListener.aidl
@@ -16,13 +16,14 @@
 
 package android.view;
 
+import android.content.pm.ParceledListSlice;
 import android.view.IPinnedStackController;
 
 /**
-  * Listener for changes to the pinned stack made by the WindowManager.
-  *
-  * @hide
-  */
+ * Listener for changes to the pinned stack made by the WindowManager.
+ *
+ * @hide
+ */
 oneway interface IPinnedStackListener {
 
     /**
@@ -36,4 +37,24 @@
      * is first registered to allow the listener to synchronized its state with the controller.
      */
     void onBoundsChanged(boolean adjustedForIme);
+
+    /**
+     * Called when window manager decides to adjust the minimized state, or when the listener
+     * is first registered to allow the listener to synchronized its state with the controller.
+     */
+    void onMinimizedStateChanged(boolean isMinimized);
+
+    /**
+     * Called when window manager decides to adjust the snap-to-edge state, which determines whether
+     * to snap only to the corners of the screen or to the closest edge.  It is called when the
+     * listener is first registered to allow the listener to synchronized its state with the
+     * controller.
+     */
+    void onSnapToEdgeStateChanged(boolean isSnapToEdge);
+
+    /**
+     * Called when the set of actions for the current PiP activity changes, or when the listener
+     * is first registered to allow the listener to synchronized its state with the controller.
+     */
+    void onActionsChanged(in ParceledListSlice actions);
 }
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index 8eca431..fc66697 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -24,6 +24,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.AnimatedVectorDrawable;
 
+import dalvik.annotation.optimization.CriticalNative;
 import dalvik.annotation.optimization.FastNative;
 
 import libcore.util.NativeAllocationRegistry;
@@ -138,7 +139,9 @@
                 RenderNode.class.getClassLoader(), nGetNativeFinalizer(), 1024);
     }
 
+    // Note: written by native when display lists are detached
     private boolean mValid;
+
     // Do not access directly unless you are ThreadedRenderer
     final long mNativeRenderNode;
     private final View mOwningView;
@@ -792,13 +795,6 @@
         return nGetDebugSize(mNativeRenderNode);
     }
 
-    /**
-     * Called by native when the passed displaylist is removed from the draw tree
-     */
-    void onRenderNodeDetached() {
-        discardDisplayList();
-    }
-
     ///////////////////////////////////////////////////////////////////////////
     // Animations
     ///////////////////////////////////////////////////////////////////////////
@@ -828,14 +824,13 @@
     }
 
     ///////////////////////////////////////////////////////////////////////////
-    // Native methods
+    // Regular JNI methods
     ///////////////////////////////////////////////////////////////////////////
 
     // Intentionally not static because it acquires a reference to 'this'
     private native long nCreate(String name);
 
     private static native long nGetNativeFinalizer();
-    private static native void nSetDisplayList(long renderNode, long newData);
     private static native void nOutput(long renderNode);
     private static native int nGetDebugSize(long renderNode);
     private static native void nRequestPositionUpdates(long renderNode, SurfaceView callback);
@@ -845,132 +840,141 @@
     private static native void nAddAnimator(long renderNode, long animatorPtr);
     private static native void nEndAllAnimators(long renderNode);
 
+
     ///////////////////////////////////////////////////////////////////////////
-    // Fast native methods
+    // @FastNative methods
+    ///////////////////////////////////////////////////////////////////////////
+
+    @FastNative
+    private static native void nSetDisplayList(long renderNode, long newData);
+
+
+    ///////////////////////////////////////////////////////////////////////////
+    // @CriticalNative methods
     ///////////////////////////////////////////////////////////////////////////
 
     // Matrix
 
-    @FastNative
+    @CriticalNative
     private static native void nGetTransformMatrix(long renderNode, long nativeMatrix);
-    @FastNative
+    @CriticalNative
     private static native void nGetInverseTransformMatrix(long renderNode, long nativeMatrix);
-    @FastNative
+    @CriticalNative
     private static native boolean nHasIdentityMatrix(long renderNode);
 
     // Properties
 
-    @FastNative
+    @CriticalNative
     private static native boolean nOffsetTopAndBottom(long renderNode, int offset);
-    @FastNative
+    @CriticalNative
     private static native boolean nOffsetLeftAndRight(long renderNode, int offset);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetLeftTopRightBottom(long renderNode, int left, int top,
             int right, int bottom);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetBottom(long renderNode, int bottom);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetRight(long renderNode, int right);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetTop(long renderNode, int top);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetLeft(long renderNode, int left);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetCameraDistance(long renderNode, float distance);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetPivotY(long renderNode, float pivotY);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetPivotX(long renderNode, float pivotX);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetLayerType(long renderNode, int layerType);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetLayerPaint(long renderNode, long paint);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetClipToBounds(long renderNode, boolean clipToBounds);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetClipBounds(long renderNode, int left, int top,
             int right, int bottom);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetClipBoundsEmpty(long renderNode);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetProjectBackwards(long renderNode, boolean shouldProject);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetProjectionReceiver(long renderNode, boolean shouldRecieve);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetOutlineRoundRect(long renderNode, int left, int top,
             int right, int bottom, float radius, float alpha);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetOutlineConvexPath(long renderNode, long nativePath,
             float alpha);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetOutlineEmpty(long renderNode);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetOutlineNone(long renderNode);
-    @FastNative
+    @CriticalNative
     private static native boolean nHasShadow(long renderNode);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetClipToOutline(long renderNode, boolean clipToOutline);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetRevealClip(long renderNode,
             boolean shouldClip, float x, float y, float radius);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetAlpha(long renderNode, float alpha);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetHasOverlappingRendering(long renderNode,
             boolean hasOverlappingRendering);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetElevation(long renderNode, float lift);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetTranslationX(long renderNode, float translationX);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetTranslationY(long renderNode, float translationY);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetTranslationZ(long renderNode, float translationZ);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetRotation(long renderNode, float rotation);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetRotationX(long renderNode, float rotationX);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetRotationY(long renderNode, float rotationY);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetScaleX(long renderNode, float scaleX);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetScaleY(long renderNode, float scaleY);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetStaticMatrix(long renderNode, long nativeMatrix);
-    @FastNative
+    @CriticalNative
     private static native boolean nSetAnimationMatrix(long renderNode, long animationMatrix);
 
-    @FastNative
+    @CriticalNative
     private static native boolean nHasOverlappingRendering(long renderNode);
-    @FastNative
+    @CriticalNative
     private static native boolean nGetClipToOutline(long renderNode);
-    @FastNative
+    @CriticalNative
     private static native float nGetAlpha(long renderNode);
-    @FastNative
+    @CriticalNative
     private static native float nGetCameraDistance(long renderNode);
-    @FastNative
+    @CriticalNative
     private static native float nGetScaleX(long renderNode);
-    @FastNative
+    @CriticalNative
     private static native float nGetScaleY(long renderNode);
-    @FastNative
+    @CriticalNative
     private static native float nGetElevation(long renderNode);
-    @FastNative
+    @CriticalNative
     private static native float nGetTranslationX(long renderNode);
-    @FastNative
+    @CriticalNative
     private static native float nGetTranslationY(long renderNode);
-    @FastNative
+    @CriticalNative
     private static native float nGetTranslationZ(long renderNode);
-    @FastNative
+    @CriticalNative
     private static native float nGetRotation(long renderNode);
-    @FastNative
+    @CriticalNative
     private static native float nGetRotationX(long renderNode);
-    @FastNative
+    @CriticalNative
     private static native float nGetRotationY(long renderNode);
-    @FastNative
+    @CriticalNative
     private static native boolean nIsPivotExplicitlySet(long renderNode);
-    @FastNative
+    @CriticalNative
     private static native float nGetPivotX(long renderNode);
-    @FastNative
+    @CriticalNative
     private static native float nGetPivotY(long renderNode);
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 9e4bb4c..12658bd 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -128,6 +128,7 @@
 import java.lang.reflect.Modifier;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -9086,6 +9087,24 @@
     }
 
     /**
+     * Find the nearest keyboard navigation cluster in the specified direction.
+     * This does not actually give focus to that cluster.
+     *
+     * @param direction Direction to look
+     *
+     * @return The nearest keyboard navigation cluster in the specified direction, or null if none
+     *         can be found
+     */
+    public View keyboardNavigationClusterSearch(int direction) {
+        if (mParent != null) {
+            final View currentCluster = isKeyboardNavigationCluster() ? this : null;
+            return mParent.keyboardNavigationClusterSearch(currentCluster, direction);
+        } else {
+            return null;
+        }
+    }
+
+    /**
      * This method is the last chance for the focused view and its ancestors to
      * respond to an arrow key. This is called when the focused view did not
      * consume the key internally, nor could the view system find a new view in
@@ -9208,6 +9227,20 @@
     }
 
     /**
+     * Adds any keyboard navigation cluster roots that are descendants of this view (possibly
+     * including this view if it is a cluster root itself) to views.
+     *
+     * @param views Cluster roots found so far
+     * @param direction Direction to look
+     */
+    public void addKeyboardNavigationClusters(@NonNull Collection<View> views, int direction) {
+        if (!isKeyboardNavigationCluster()) {
+            return;
+        }
+        views.add(this);
+    }
+
+    /**
      * Finds the Views that contain given text. The containment is case insensitive.
      * The search is performed by either the text that the View renders or the content
      * description that describes the view for accessibility purposes and the view does
@@ -14041,11 +14074,6 @@
                     receiver.damageInParent();
                 }
             }
-
-            // Damage the entire IsolatedZVolume receiving this view's shadow.
-            if (isHardwareAccelerated() && getZ() != 0) {
-                damageShadowReceiver();
-            }
         }
     }
 
@@ -14073,23 +14101,6 @@
     }
 
     /**
-     * Damage area of the screen that can be covered by this View's shadow.
-     *
-     * This method will guarantee that any changes to shadows cast by a View
-     * are damaged on the screen for future redraw.
-     */
-    private void damageShadowReceiver() {
-        final AttachInfo ai = mAttachInfo;
-        if (ai != null) {
-            ViewParent p = getParent();
-            if (p != null && p instanceof ViewGroup) {
-                final ViewGroup vg = (ViewGroup) p;
-                vg.damageInParent();
-            }
-        }
-    }
-
-    /**
      * Quick invalidation for View property changes (alpha, translationXY, etc.). We don't want to
      * set any flags or handle all of the cases handled by the default invalidation methods.
      * Instead, we just want to schedule a traversal in ViewRootImpl with the appropriate
@@ -14119,9 +14130,6 @@
         } else {
             damageInParent();
         }
-        if (isHardwareAccelerated() && invalidateParent && getZ() != 0) {
-            damageShadowReceiver();
-        }
     }
 
     /**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index ff797d1..7340cf7 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -59,6 +59,7 @@
 import com.android.internal.util.Predicate;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
@@ -868,10 +869,13 @@
      */
     @Override
     public View focusSearch(View focused, int direction) {
-        if (isRootNamespace()) {
+        if (isRootNamespace()
+                || isKeyboardNavigationCluster()
+                && (direction == FOCUS_FORWARD || direction == FOCUS_BACKWARD)) {
             // root namespace means we should consider ourselves the top of the
             // tree for focus searching; otherwise we could be focus searching
-            // into other tabs.  see LocalActivityManager and TabHost for more info
+            // into other tabs.  see LocalActivityManager and TabHost for more info.
+            // Cluster's root works same way for the forward and backward navigation.
             return FocusFinder.getInstance().findNextFocus(this, focused, direction);
         } else if (mParent != null) {
             return mParent.focusSearch(focused, direction);
@@ -880,6 +884,23 @@
     }
 
     @Override
+    public View keyboardNavigationClusterSearch(View currentCluster, int direction) {
+        if (isKeyboardNavigationCluster()) {
+            currentCluster = this;
+        }
+        if (isRootNamespace()) {
+            // root namespace means we should consider ourselves the top of the
+            // tree for cluster searching; otherwise we could be focus searching
+            // into other tabs.  see LocalActivityManager and TabHost for more info
+            return FocusFinder.getInstance().findNextKeyboardNavigationCluster(
+                    this, currentCluster, direction);
+        } else if (mParent != null) {
+            return mParent.keyboardNavigationClusterSearch(currentCluster, direction);
+        }
+        return null;
+    }
+
+    @Override
     public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
         return false;
     }
@@ -1086,6 +1107,12 @@
 
     @Override
     public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
+        if (isKeyboardNavigationCluster()
+                && (direction == FOCUS_FORWARD || direction == FOCUS_BACKWARD) && !hasFocus()) {
+            // A cluster cannot be focus-entered from outside using forward/backward navigation.
+            return;
+        }
+
         final int focusableCount = views.size();
 
         final int descendantFocusability = getDescendantFocusability();
@@ -1118,6 +1145,32 @@
         }
     }
 
+    @Override
+    public void addKeyboardNavigationClusters(Collection<View> views, int direction) {
+        final int focusableCount = views.size();
+
+        super.addKeyboardNavigationClusters(views, direction);
+
+        if (focusableCount != views.size()) {
+            // No need to look for clusters inside a cluster.
+            return;
+        }
+
+        if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) {
+            return;
+        }
+
+        final int count = mChildrenCount;
+        final View[] children = mChildren;
+
+        for (int i = 0; i < count; i++) {
+            final View child = children[i];
+            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
+                child.addKeyboardNavigationClusters(views, direction);
+            }
+        }
+    }
+
     /**
      * Set whether this ViewGroup should ignore focus requests for itself and its children.
      * If this option is enabled and the ViewGroup or a descendant currently has focus, focus
@@ -2982,7 +3035,8 @@
         final View[] children = mChildren;
         for (int i = index; i != end; i += increment) {
             View child = children[i];
-            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
+            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
+                    && !child.isKeyboardNavigationCluster()) {
                 if (child.requestFocus(direction, previouslyFocusedRect)) {
                     return true;
                 }
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index 849c8b9..79b05cd 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -147,6 +147,19 @@
     public View focusSearch(View v, int direction);
 
     /**
+     * Find the nearest keyboard navigation cluster in the specified direction.
+     * This does not actually give focus to that cluster.
+     *
+     * @param currentCluster The starting point of the search. Null means the current cluster is not
+     *                       found yet
+     * @param direction Direction to look
+     *
+     * @return The nearest keyboard navigation cluster in the specified direction, or null if none
+     *         can be found
+     */
+    View keyboardNavigationClusterSearch(View currentCluster, int direction);
+
+    /**
      * Change the z order of the child so it's on top of all other children.
      * This ordering change may affect layout, if this container
      * uses an order-dependent layout scheme (e.g., LinearLayout). Prior
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e030e76..2cebeed 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -4324,6 +4324,87 @@
             super.onDeliverToNext(q);
         }
 
+        private boolean performFocusNavigation(KeyEvent event) {
+            int direction = 0;
+            switch (event.getKeyCode()) {
+                case KeyEvent.KEYCODE_DPAD_LEFT:
+                    if (event.hasNoModifiers()) {
+                        direction = View.FOCUS_LEFT;
+                    }
+                    break;
+                case KeyEvent.KEYCODE_DPAD_RIGHT:
+                    if (event.hasNoModifiers()) {
+                        direction = View.FOCUS_RIGHT;
+                    }
+                    break;
+                case KeyEvent.KEYCODE_DPAD_UP:
+                    if (event.hasNoModifiers()) {
+                        direction = View.FOCUS_UP;
+                    }
+                    break;
+                case KeyEvent.KEYCODE_DPAD_DOWN:
+                    if (event.hasNoModifiers()) {
+                        direction = View.FOCUS_DOWN;
+                    }
+                    break;
+                case KeyEvent.KEYCODE_TAB:
+                    if (event.hasNoModifiers()) {
+                        direction = View.FOCUS_FORWARD;
+                    } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
+                        direction = View.FOCUS_BACKWARD;
+                    }
+                    break;
+            }
+            if (direction != 0) {
+                View focused = mView.findFocus();
+                if (focused != null) {
+                    View v = focused.focusSearch(direction);
+                    if (v != null && v != focused) {
+                        // do the math the get the interesting rect
+                        // of previous focused into the coord system of
+                        // newly focused view
+                        focused.getFocusedRect(mTempRect);
+                        if (mView instanceof ViewGroup) {
+                            ((ViewGroup) mView).offsetDescendantRectToMyCoords(
+                                    focused, mTempRect);
+                            ((ViewGroup) mView).offsetRectIntoDescendantCoords(
+                                    v, mTempRect);
+                        }
+                        if (v.requestFocus(direction, mTempRect)) {
+                            playSoundEffect(SoundEffectConstants
+                                    .getContantForFocusDirection(direction));
+                            return true;
+                        }
+                    }
+
+                    // Give the focused view a last chance to handle the dpad key.
+                    if (mView.dispatchUnhandledMove(focused, direction)) {
+                        return true;
+                    }
+                } else {
+                    // find the best view to give focus to in this non-touch-mode with no-focus
+                    View v = focusSearch(null, direction);
+                    if (v != null && v.requestFocus(direction)) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+
+        private boolean performClusterNavigation(int direction) {
+            final View focused = mView.findFocus();
+            final View cluster = focused != null
+                    ? focused.keyboardNavigationClusterSearch(direction)
+                    : keyboardNavigationClusterSearch(null, direction);
+
+            if (cluster != null && cluster.requestFocus()) {
+                return true;
+            }
+
+            return false;
+        }
+
         private int processKeyEvent(QueuedInputEvent q) {
             final KeyEvent event = (KeyEvent)q.mEvent;
 
@@ -4336,11 +4417,26 @@
                 return FINISH_NOT_HANDLED;
             }
 
+            int clusterNavigationDirection = 0;
+
+            if (event.getAction() == KeyEvent.ACTION_DOWN && event.isCtrlPressed()) {
+                final int character =
+                        event.getUnicodeChar(event.getMetaState() & ~KeyEvent.META_CTRL_MASK);
+                if (character == '+') {
+                    clusterNavigationDirection = View.FOCUS_FORWARD;
+                }
+
+                if (character == '_') {
+                    clusterNavigationDirection = View.FOCUS_BACKWARD;
+                }
+            }
+
             // If the Control modifier is held, try to interpret the key as a shortcut.
             if (event.getAction() == KeyEvent.ACTION_DOWN
                     && event.isCtrlPressed()
                     && event.getRepeatCount() == 0
-                    && !KeyEvent.isModifierKey(event.getKeyCode())) {
+                    && !KeyEvent.isModifierKey(event.getKeyCode())
+                    && clusterNavigationDirection == 0) {
                 if (mView.dispatchKeyShortcutEvent(event)) {
                     return FINISH_HANDLED;
                 }
@@ -4359,68 +4455,13 @@
 
             // Handle automatic focus changes.
             if (event.getAction() == KeyEvent.ACTION_DOWN) {
-                int direction = 0;
-                switch (event.getKeyCode()) {
-                    case KeyEvent.KEYCODE_DPAD_LEFT:
-                        if (event.hasNoModifiers()) {
-                            direction = View.FOCUS_LEFT;
-                        }
-                        break;
-                    case KeyEvent.KEYCODE_DPAD_RIGHT:
-                        if (event.hasNoModifiers()) {
-                            direction = View.FOCUS_RIGHT;
-                        }
-                        break;
-                    case KeyEvent.KEYCODE_DPAD_UP:
-                        if (event.hasNoModifiers()) {
-                            direction = View.FOCUS_UP;
-                        }
-                        break;
-                    case KeyEvent.KEYCODE_DPAD_DOWN:
-                        if (event.hasNoModifiers()) {
-                            direction = View.FOCUS_DOWN;
-                        }
-                        break;
-                    case KeyEvent.KEYCODE_TAB:
-                        if (event.hasNoModifiers()) {
-                            direction = View.FOCUS_FORWARD;
-                        } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
-                            direction = View.FOCUS_BACKWARD;
-                        }
-                        break;
-                }
-                if (direction != 0) {
-                    View focused = mView.findFocus();
-                    if (focused != null) {
-                        View v = focused.focusSearch(direction);
-                        if (v != null && v != focused) {
-                            // do the math the get the interesting rect
-                            // of previous focused into the coord system of
-                            // newly focused view
-                            focused.getFocusedRect(mTempRect);
-                            if (mView instanceof ViewGroup) {
-                                ((ViewGroup) mView).offsetDescendantRectToMyCoords(
-                                        focused, mTempRect);
-                                ((ViewGroup) mView).offsetRectIntoDescendantCoords(
-                                        v, mTempRect);
-                            }
-                            if (v.requestFocus(direction, mTempRect)) {
-                                playSoundEffect(SoundEffectConstants
-                                        .getContantForFocusDirection(direction));
-                                return FINISH_HANDLED;
-                            }
-                        }
-
-                        // Give the focused view a last chance to handle the dpad key.
-                        if (mView.dispatchUnhandledMove(focused, direction)) {
-                            return FINISH_HANDLED;
-                        }
-                    } else {
-                        // find the best view to give focus to in this non-touch-mode with no-focus
-                        View v = focusSearch(null, direction);
-                        if (v != null && v.requestFocus(direction)) {
-                            return FINISH_HANDLED;
-                        }
+                if (clusterNavigationDirection != 0) {
+                    if (performClusterNavigation(clusterNavigationDirection)) {
+                        return FINISH_HANDLED;
+                    }
+                } else {
+                    if (performFocusNavigation(event)) {
+                        return FINISH_HANDLED;
                     }
                 }
             }
@@ -5842,6 +5883,19 @@
         return FocusFinder.getInstance().findNextFocus((ViewGroup) mView, focused, direction);
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public View keyboardNavigationClusterSearch(View currentCluster, int direction) {
+        checkThread();
+        if (!(mView instanceof ViewGroup)) {
+            return null;
+        }
+        return FocusFinder.getInstance().findNextKeyboardNavigationCluster(
+                (ViewGroup) mView, currentCluster, direction);
+    }
+
     public void debug() {
         mView.debug();
     }
diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java
index 7dd2fc2..e4d4e7b 100644
--- a/core/java/android/view/ViewTreeObserver.java
+++ b/core/java/android/view/ViewTreeObserver.java
@@ -385,6 +385,14 @@
             }
         }
 
+        if (observer.mOnDrawListeners != null) {
+            if (mOnDrawListeners != null) {
+                mOnDrawListeners.addAll(observer.mOnDrawListeners);
+            } else {
+                mOnDrawListeners = observer.mOnDrawListeners;
+            }
+        }
+
         if (observer.mOnTouchModeChangeListeners != null) {
             if (mOnTouchModeChangeListeners != null) {
                 mOnTouchModeChangeListeners.addAll(observer.mOnTouchModeChangeListeners);
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 8084195..13d5b85 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -350,7 +350,18 @@
                 return;
             }
             if (!mIsEnabled) {
-                throw new IllegalStateException("Accessibility off. Did you forget to check that?");
+                Looper myLooper = Looper.myLooper();
+                if (myLooper == Looper.getMainLooper()) {
+                    throw new IllegalStateException(
+                            "Accessibility off. Did you forget to check that?");
+                } else {
+                    // If we're not running on the thread with the main looper, it's possible for
+                    // the state of accessibility to change between checking isEnabled and
+                    // calling this method. So just log the error rather than throwing the
+                    // exception.
+                    Log.e(LOG_TAG, "Interrupt called with accessibility disabled");
+                    return;
+                }
             }
             userId = mUserId;
         }
diff --git a/core/java/android/widget/Chronometer.java b/core/java/android/widget/Chronometer.java
index b9224f3..444ebc5 100644
--- a/core/java/android/widget/Chronometer.java
+++ b/core/java/android/widget/Chronometer.java
@@ -17,8 +17,11 @@
 package android.widget;
 
 import android.content.Context;
-import android.content.res.Resources;
 import android.content.res.TypedArray;
+import android.icu.text.MeasureFormat;
+import android.icu.text.MeasureFormat.FormatWidth;
+import android.icu.util.Measure;
+import android.icu.util.MeasureUnit;
 import android.os.SystemClock;
 import android.text.format.DateUtils;
 import android.util.AttributeSet;
@@ -28,6 +31,7 @@
 
 import com.android.internal.R;
 
+import java.util.ArrayList;
 import java.util.Formatter;
 import java.util.IllegalFormatException;
 import java.util.Locale;
@@ -329,9 +333,6 @@
     private static final int MIN_IN_SEC = 60;
     private static final int HOUR_IN_SEC = MIN_IN_SEC*60;
     private static String formatDuration(long ms) {
-        final Resources res = Resources.getSystem();
-        final StringBuilder text = new StringBuilder();
-
         int duration = (int) (ms / DateUtils.SECOND_IN_MILLIS);
         if (duration < 0) {
             duration = -duration;
@@ -348,31 +349,19 @@
             m = duration / MIN_IN_SEC;
             duration -= m * MIN_IN_SEC;
         }
-        int s = duration;
+        final int s = duration;
 
-        try {
-            if (h > 0) {
-                text.append(res.getQuantityString(
-                        com.android.internal.R.plurals.duration_hours, h, h));
-            }
-            if (m > 0) {
-                if (text.length() > 0) {
-                    text.append(' ');
-                }
-                text.append(res.getQuantityString(
-                        com.android.internal.R.plurals.duration_minutes, m, m));
-            }
-
-            if (text.length() > 0) {
-                text.append(' ');
-            }
-            text.append(res.getQuantityString(
-                    com.android.internal.R.plurals.duration_seconds, s, s));
-        } catch (Resources.NotFoundException e) {
-            // Ignore; plurals throws an exception for an untranslated quantity for a given locale.
-            return null;
+        final ArrayList<Measure> measures = new ArrayList<Measure>();
+        if (h > 0) {
+            measures.add(new Measure(h, MeasureUnit.HOUR));
         }
-        return text.toString();
+        if (m > 0) {
+            measures.add(new Measure(m, MeasureUnit.MINUTE));
+        }
+        measures.add(new Measure(s, MeasureUnit.SECOND));
+
+        return MeasureFormat.getInstance(Locale.getDefault(), FormatWidth.WIDE)
+                    .formatMeasures((Measure[]) measures.toArray());
     }
 
     @Override
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 5a134b2..c314cae 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -19,6 +19,7 @@
 import android.animation.ObjectAnimator;
 import android.annotation.NonNull;
 import android.app.Activity;
+import android.app.usage.UsageStatsManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -403,6 +404,7 @@
                 }
             }
         }
+        updateChooserCounts(target, mContentType);
         return super.onTargetSelected(target, alwaysCheck);
     }
 
@@ -547,20 +549,46 @@
         // Do nothing. We'll send the voice stuff ourselves.
     }
 
+    void updateChooserCounts(TargetInfo info, String annotation) {
+        if (info != null) {
+            UsageStatsManager usageStatsManager =
+                    (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
+            if (usageStatsManager == null) {
+                if (DEBUG) {
+                    Log.d(TAG, "Can not start UsageStatsManager");
+                }
+                return;
+            }
+            final ResolveInfo ri = info.getResolveInfo();
+            if (ri != null && ri.activityInfo != null) {
+                usageStatsManager.reportChooserSelection(ri.activityInfo.packageName, getUserId(),
+                        annotation, null, info.getResolvedIntent().getAction());
+                if (DEBUG) {
+                    Log.d(TAG, "ResolveInfo Package is" + ri.activityInfo.packageName);
+                }
+            } else if(DEBUG) {
+                Log.d(TAG, "Can not log Chooser Counts of null ResovleInfo");
+            }
+        }
+    }
+
     void onRefinementResult(TargetInfo selectedTarget, Intent matchingIntent) {
         if (mRefinementResultReceiver != null) {
             mRefinementResultReceiver.destroy();
             mRefinementResultReceiver = null;
         }
-
         if (selectedTarget == null) {
             Log.e(TAG, "Refinement result intent did not match any known targets; canceling");
         } else if (!checkTargetSourceIntent(selectedTarget, matchingIntent)) {
             Log.e(TAG, "onRefinementResult: Selected target " + selectedTarget
                     + " cannot match refined source intent " + matchingIntent);
-        } else if (super.onTargetSelected(selectedTarget.cloneFilledIn(matchingIntent, 0), false)) {
-            finish();
-            return;
+        } else {
+            TargetInfo clonedTarget = selectedTarget.cloneFilledIn(matchingIntent, 0);
+            if (super.onTargetSelected(clonedTarget, false)) {
+                updateChooserCounts(clonedTarget, mContentType);
+                finish();
+                return;
+            }
         }
         onRefinementCanceled();
     }
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index dd8ef18..c516b5c 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -26,6 +26,7 @@
 import android.app.VoiceInteractor.Prompt;
 import android.content.pm.ComponentInfo;
 import android.os.AsyncTask;
+import android.os.RemoteException;
 import android.provider.MediaStore;
 import android.provider.Settings;
 import android.text.TextUtils;
@@ -53,7 +54,6 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.PatternMatcher;
-import android.os.RemoteException;
 import android.os.StrictMode;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -114,6 +114,7 @@
     private ComponentName[] mFilteredComponents;
 
     protected ResolverDrawerLayout mResolverDrawerLayout;
+    protected String mContentType;
 
     private boolean mRegistered;
     private final PackageMonitor mPackageMonitor = new PackageMonitor() {
@@ -270,6 +271,7 @@
         final String referrerPackage = getReferrerPackageName();
 
         mResolverComparator = new ResolverComparator(this, getTargetIntent(), referrerPackage);
+        mContentType = mResolverComparator.mContentType;
 
         if (configureContentView(mIntents, initialIntents, rList, alwaysUseOption)) {
             return;
diff --git a/core/java/com/android/internal/app/ResolverComparator.java b/core/java/com/android/internal/app/ResolverComparator.java
index 4d4c7ce..75be906 100644
--- a/core/java/com/android/internal/app/ResolverComparator.java
+++ b/core/java/com/android/internal/app/ResolverComparator.java
@@ -63,6 +63,8 @@
     private final long mSinceTime;
     private final LinkedHashMap<ComponentName, ScoredTarget> mScoredTargets = new LinkedHashMap<>();
     private final String mReferrerPackage;
+    public String mContentType;
+    private String mAction;
 
     public ResolverComparator(Context context, Intent intent, String referrerPackage) {
         mCollator = Collator.getInstance(context.getResources().getConfiguration().locale);
@@ -76,6 +78,8 @@
         mCurrentTime = System.currentTimeMillis();
         mSinceTime = mCurrentTime - USAGE_STATS_PERIOD;
         mStats = mUsm.queryAndAggregateUsageStats(mSinceTime, mCurrentTime);
+        mContentType = intent.getType();
+        mAction = intent.getAction();
     }
 
     public void compute(List<ResolvedComponentInfo> targets) {
@@ -86,6 +90,7 @@
         long mostRecentlyUsedTime = recentSinceTime + 1;
         long mostTimeSpent = 1;
         int mostLaunched = 1;
+        int mostSelected = 1;
 
         for (ResolvedComponentInfo target : targets) {
             final ScoredTarget scoredTarget
@@ -114,6 +119,25 @@
                 if (launched > mostLaunched) {
                     mostLaunched = launched;
                 }
+                // TODO(kanlig): get and combine counts of categories.
+
+                int selected = 0;
+                if (pkStats.mChooserCounts != null && mAction != null
+                        && pkStats.mChooserCounts.get(mAction) != null) {
+                    selected = pkStats.mChooserCounts.get(mAction).getOrDefault(mContentType, 0);
+                }
+                if (DEBUG) {
+                    if (mAction == null) {
+                        Log.d(TAG, "Action type is null");
+                    } else {
+                        Log.d(TAG, "Chooser Count of " + mAction + ":" +
+                                target.name.getPackageName() + " is " + Integer.toString(selected));
+                    }
+                }
+                scoredTarget.chooserCount = selected;
+                if (selected > mostSelected) {
+                    mostSelected = selected;
+                }
             }
         }
 
@@ -190,7 +214,15 @@
                         lhs.activityInfo.packageName, lhs.activityInfo.name));
                 final ScoredTarget rhsTarget = mScoredTargets.get(new ComponentName(
                         rhs.activityInfo.packageName, rhs.activityInfo.name));
-                final float diff = rhsTarget.score - lhsTarget.score;
+
+                final int chooserCountDiff = Long.compare(
+                        rhsTarget.chooserCount, lhsTarget.chooserCount);
+
+                if (chooserCountDiff != 0) {
+                    return chooserCountDiff > 0 ? 1 : -1;
+                }
+
+                final int diff = Float.compare(rhsTarget.score, lhsTarget.score);
 
                 if (diff != 0) {
                     return diff > 0 ? 1 : -1;
@@ -220,6 +252,7 @@
         public long lastTimeUsed;
         public long timeSpent;
         public long launchCount;
+        public long chooserCount;
 
         public ScoredTarget(ComponentInfo ci) {
             componentInfo = ci;
@@ -232,6 +265,7 @@
                     + " lastTimeUsed: " + lastTimeUsed
                     + " timeSpent: " + timeSpent
                     + " launchCount: " + launchCount
+                    + " chooserCount: " + chooserCount
                     + "}";
         }
     }
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index f479f4f..83b7d2f 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -76,6 +76,7 @@
         final long[] apkHandles;
         final boolean multiArch;
         final boolean extractNativeLibs;
+        final boolean debuggable;
 
         public static Handle create(File packageFile) throws IOException {
             try {
@@ -89,15 +90,17 @@
         public static Handle create(Package pkg) throws IOException {
             return create(pkg.getAllCodePaths(),
                     (pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) != 0,
-                    (pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS) != 0);
+                    (pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS) != 0,
+                    (pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
         }
 
         public static Handle create(PackageLite lite) throws IOException {
-            return create(lite.getAllCodePaths(), lite.multiArch, lite.extractNativeLibs);
+            return create(lite.getAllCodePaths(), lite.multiArch, lite.extractNativeLibs,
+                    lite.debuggable);
         }
 
         private static Handle create(List<String> codePaths, boolean multiArch,
-                boolean extractNativeLibs) throws IOException {
+                boolean extractNativeLibs, boolean debuggable) throws IOException {
             final int size = codePaths.size();
             final long[] apkHandles = new long[size];
             for (int i = 0; i < size; i++) {
@@ -112,13 +115,15 @@
                 }
             }
 
-            return new Handle(apkHandles, multiArch, extractNativeLibs);
+            return new Handle(apkHandles, multiArch, extractNativeLibs, debuggable);
         }
 
-        Handle(long[] apkHandles, boolean multiArch, boolean extractNativeLibs) {
+        Handle(long[] apkHandles, boolean multiArch, boolean extractNativeLibs,
+                boolean debuggable) {
             this.apkHandles = apkHandles;
             this.multiArch = multiArch;
             this.extractNativeLibs = extractNativeLibs;
+            this.debuggable = debuggable;
             mGuard.open("close");
         }
 
@@ -149,15 +154,17 @@
     private static native long nativeOpenApk(String path);
     private static native void nativeClose(long handle);
 
-    private static native long nativeSumNativeBinaries(long handle, String cpuAbi);
+    private static native long nativeSumNativeBinaries(long handle, String cpuAbi,
+            boolean debuggable);
 
     private native static int nativeCopyNativeBinaries(long handle, String sharedLibraryPath,
-            String abiToCopy, boolean extractNativeLibs, boolean hasNativeBridge);
+            String abiToCopy, boolean extractNativeLibs, boolean hasNativeBridge,
+            boolean debuggable);
 
     private static long sumNativeBinaries(Handle handle, String abi) {
         long sum = 0;
         for (long apkHandle : handle.apkHandles) {
-            sum += nativeSumNativeBinaries(apkHandle, abi);
+            sum += nativeSumNativeBinaries(apkHandle, abi, handle.debuggable);
         }
         return sum;
     }
@@ -173,7 +180,7 @@
     public static int copyNativeBinaries(Handle handle, File sharedLibraryDir, String abi) {
         for (long apkHandle : handle.apkHandles) {
             int res = nativeCopyNativeBinaries(apkHandle, sharedLibraryDir.getPath(), abi,
-                    handle.extractNativeLibs, HAS_NATIVE_BRIDGE);
+                    handle.extractNativeLibs, HAS_NATIVE_BRIDGE, handle.debuggable);
             if (res != INSTALL_SUCCEEDED) {
                 return res;
             }
@@ -191,7 +198,7 @@
     public static int findSupportedAbi(Handle handle, String[] supportedAbis) {
         int finalRes = NO_NATIVE_LIBRARIES;
         for (long apkHandle : handle.apkHandles) {
-            final int res = nativeFindSupportedAbi(apkHandle, supportedAbis);
+            final int res = nativeFindSupportedAbi(apkHandle, supportedAbis, handle.debuggable);
             if (res == NO_NATIVE_LIBRARIES) {
                 // No native code, keep looking through all APKs.
             } else if (res == INSTALL_FAILED_NO_MATCHING_ABIS) {
@@ -213,7 +220,8 @@
         return finalRes;
     }
 
-    private native static int nativeFindSupportedAbi(long handle, String[] supportedAbis);
+    private native static int nativeFindSupportedAbi(long handle, String[] supportedAbis,
+            boolean debuggable);
 
     // Convenience method to call removeNativeBinariesFromDirLI(File)
     public static void removeNativeBinariesLI(String nativeLibraryPath) {
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index 98d87d3..eec3cb0 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -16,8 +16,6 @@
 
 package com.android.internal.content;
 
-import static android.net.TrafficStats.MB_IN_BYTES;
-
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
@@ -38,7 +36,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 
-import libcore.io.IoUtils;
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.File;
 import java.io.FileOutputStream;
@@ -50,6 +48,11 @@
 import java.util.zip.ZipFile;
 import java.util.zip.ZipOutputStream;
 
+import libcore.io.IoUtils;
+
+import static android.net.TrafficStats.MB_IN_BYTES;
+import static android.os.storage.VolumeInfo.ID_PRIVATE_INTERNAL;
+
 /**
  * Constants used internally between the PackageManager
  * and media container service transports.
@@ -74,6 +77,8 @@
     public static final int APP_INSTALL_INTERNAL = 1;
     public static final int APP_INSTALL_EXTERNAL = 2;
 
+    private static TestableInterface sDefaultTestableInterface = null;
+
     public static IStorageManager getStorageManager() throws RemoteException {
         IBinder service = ServiceManager.getService("mount");
         if (service != null) {
@@ -338,6 +343,65 @@
     }
 
     /**
+     * A group of external dependencies used in
+     * {@link #resolveInstallVolume(Context, String, int, long)}. It can be backed by real values
+     * from the system or mocked ones for testing purposes.
+     */
+    public static abstract class TestableInterface {
+        abstract public StorageManager getStorageManager(Context context);
+        abstract public boolean getForceAllowOnExternalSetting(Context context);
+        abstract public boolean getAllow3rdPartyOnInternalConfig(Context context);
+        abstract public ApplicationInfo getExistingAppInfo(Context context, String packageName);
+        abstract public File getDataDirectory();
+
+        public boolean fitsOnInternalStorage(Context context, long sizeBytes) {
+            StorageManager storage = getStorageManager(context);
+            File target = getDataDirectory();
+            return (sizeBytes <= storage.getStorageBytesUntilLow(target));
+        }
+    }
+
+    private synchronized static TestableInterface getDefaultTestableInterface() {
+        if (sDefaultTestableInterface == null) {
+            sDefaultTestableInterface = new TestableInterface() {
+                @Override
+                public StorageManager getStorageManager(Context context) {
+                    return context.getSystemService(StorageManager.class);
+                }
+
+                @Override
+                public boolean getForceAllowOnExternalSetting(Context context) {
+                    return Settings.Global.getInt(context.getContentResolver(),
+                            Settings.Global.FORCE_ALLOW_ON_EXTERNAL, 0) != 0;
+                }
+
+                @Override
+                public boolean getAllow3rdPartyOnInternalConfig(Context context) {
+                    return context.getResources().getBoolean(
+                            com.android.internal.R.bool.config_allow3rdPartyAppOnInternal);
+                }
+
+                @Override
+                public ApplicationInfo getExistingAppInfo(Context context, String packageName) {
+                    ApplicationInfo existingInfo = null;
+                    try {
+                        existingInfo = context.getPackageManager().getApplicationInfo(packageName,
+                                PackageManager.MATCH_ANY_USER);
+                    } catch (NameNotFoundException ignored) {
+                    }
+                    return existingInfo;
+                }
+
+                @Override
+                public File getDataDirectory() {
+                    return Environment.getDataDirectory();
+                }
+            };
+        }
+        return sDefaultTestableInterface;
+    }
+
+    /**
      * Given a requested {@link PackageInfo#installLocation} and calculated
      * install size, pick the actual volume to install the app. Only considers
      * internal and private volumes, and prefers to keep an existing package on
@@ -348,25 +412,44 @@
      */
     public static String resolveInstallVolume(Context context, String packageName,
             int installLocation, long sizeBytes) throws IOException {
-        final boolean forceAllowOnExternal = Settings.Global.getInt(
-                context.getContentResolver(), Settings.Global.FORCE_ALLOW_ON_EXTERNAL, 0) != 0;
+        TestableInterface testableInterface = getDefaultTestableInterface();
+        return resolveInstallVolume(context, packageName,
+                installLocation, sizeBytes, testableInterface);
+    }
+
+    @VisibleForTesting
+    public static String resolveInstallVolume(Context context, String packageName,
+            int installLocation, long sizeBytes, TestableInterface testInterface)
+            throws IOException {
+        final boolean forceAllowOnExternal = testInterface.getForceAllowOnExternalSetting(context);
+        final boolean allow3rdPartyOnInternal =
+                testInterface.getAllow3rdPartyOnInternalConfig(context);
         // TODO: handle existing apps installed in ASEC; currently assumes
         // they'll end up back on internal storage
-        ApplicationInfo existingInfo = null;
-        try {
-            existingInfo = context.getPackageManager().getApplicationInfo(packageName,
-                    PackageManager.MATCH_ANY_USER);
-        } catch (NameNotFoundException ignored) {
+        ApplicationInfo existingInfo = testInterface.getExistingAppInfo(context, packageName);
+
+        final boolean fitsOnInternal = testInterface.fitsOnInternalStorage(context, sizeBytes);
+        final StorageManager storageManager =
+                testInterface.getStorageManager(context);
+
+        // System apps always forced to internal storage
+        if (existingInfo != null && existingInfo.isSystemApp()) {
+            if (fitsOnInternal) {
+                return StorageManager.UUID_PRIVATE_INTERNAL;
+            } else {
+                throw new IOException("Not enough space on existing volume "
+                        + existingInfo.volumeUuid + " for system app " + packageName + " upgrade");
+            }
         }
 
-        final StorageManager storageManager = context.getSystemService(StorageManager.class);
-        final boolean fitsOnInternal = fitsOnInternal(context, sizeBytes);
-
+        // Now deal with non-system apps.
         final ArraySet<String> allCandidates = new ArraySet<>();
         VolumeInfo bestCandidate = null;
         long bestCandidateAvailBytes = Long.MIN_VALUE;
         for (VolumeInfo vol : storageManager.getVolumes()) {
-            if (vol.type == VolumeInfo.TYPE_PRIVATE && vol.isMountedWritable()) {
+            boolean isInternalStorage = ID_PRIVATE_INTERNAL.equals(vol.id);
+            if (vol.type == VolumeInfo.TYPE_PRIVATE && vol.isMountedWritable()
+                    && (!isInternalStorage || allow3rdPartyOnInternal)) {
                 final long availBytes = storageManager.getStorageBytesUntilLow(new File(vol.path));
                 if (availBytes >= sizeBytes) {
                     allCandidates.add(vol.fsUuid);
@@ -378,11 +461,6 @@
             }
         }
 
-        // System apps always forced to internal storage
-        if (existingInfo != null && existingInfo.isSystemApp()) {
-            installLocation = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY;
-        }
-
         // If app expresses strong desire for internal storage, honor it
         if (!forceAllowOnExternal
                 && installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
@@ -391,6 +469,11 @@
                 throw new IOException("Cannot automatically move " + packageName + " from "
                         + existingInfo.volumeUuid + " to internal storage");
             }
+
+            if (!allow3rdPartyOnInternal) {
+                throw new IOException("Not allowed to install non-system apps on internal storage");
+            }
+
             if (fitsOnInternal) {
                 return StorageManager.UUID_PRIVATE_INTERNAL;
             } else {
@@ -411,14 +494,13 @@
             }
         }
 
-        // We're left with either preferring external or auto, so just pick
+        // We're left with new installations with either preferring external or auto, so just pick
         // volume with most space
         if (bestCandidate != null) {
             return bestCandidate.fsUuid;
-        } else if (fitsOnInternal) {
-            return StorageManager.UUID_PRIVATE_INTERNAL;
         } else {
-            throw new IOException("No special requests, but no room anywhere");
+            throw new IOException("No special requests, but no room on allowed volumes. "
+                + " allow3rdPartyOnInternal? " + allow3rdPartyOnInternal);
         }
     }
 
diff --git a/core/java/com/android/internal/os/IDropBoxManagerService.aidl b/core/java/com/android/internal/os/IDropBoxManagerService.aidl
index d067926..d16579c 100644
--- a/core/java/com/android/internal/os/IDropBoxManagerService.aidl
+++ b/core/java/com/android/internal/os/IDropBoxManagerService.aidl
@@ -17,7 +17,6 @@
 package com.android.internal.os;
 
 import android.os.DropBoxManager;
-import android.os.ParcelFileDescriptor;
 
 /**
  * "Backend" interface used by {@link android.os.DropBoxManager} to talk to the
diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java
index d968e3c..a8a5549 100644
--- a/core/java/com/android/internal/os/WebViewZygoteInit.java
+++ b/core/java/com/android/internal/os/WebViewZygoteInit.java
@@ -62,6 +62,9 @@
             ClassLoader loader = ApplicationLoaders.getDefault().createAndCacheWebViewClassLoader(
                     packagePath, libsPath);
 
+            // Add the APK to the Zygote's list of allowed files for children.
+            Zygote.nativeAllowFileAcrossFork(packagePath);
+
             // Once we have the classloader, look up the WebViewFactoryProvider implementation and
             // call preloadInZygote() on it to give it the opportunity to preload the native library
             // and perform any other initialisation work that should be shared among the children.
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index fc0ccb7..293de3d 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -153,6 +153,11 @@
             int[][] rlimits, long permittedCapabilities, long effectiveCapabilities);
 
     /**
+     * Lets children of the zygote inherit open file descriptors to this path.
+     */
+    native protected static void nativeAllowFileAcrossFork(String path);
+
+    /**
      * Zygote unmount storage space on initializing.
      * This method is called once.
      */
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 7edc938..39cb464 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -695,9 +695,11 @@
             throws ZygoteSecurityException {
         int peerUid = peer.getUid();
 
-        if (args.invokeWith != null && peerUid != 0) {
-            throw new ZygoteSecurityException("Peer is not permitted to specify "
-                    + "an explicit invoke-with wrapper command");
+        if (args.invokeWith != null && peerUid != 0 &&
+            (args.debugFlags & Zygote.DEBUG_ENABLE_DEBUGGER) == 0) {
+            throw new ZygoteSecurityException("Peer is permitted to specify an"
+                    + "explicit invoke-with wrapper command only for debuggable"
+                    + "applications.");
         }
     }
 
diff --git a/core/java/com/android/internal/util/TokenBucket.java b/core/java/com/android/internal/util/TokenBucket.java
index effb82b..a163ceb 100644
--- a/core/java/com/android/internal/util/TokenBucket.java
+++ b/core/java/com/android/internal/util/TokenBucket.java
@@ -33,6 +33,8 @@
  * The available amount of tokens is computed lazily when the bucket state is inspected.
  * Therefore it is purely synchronous and does not involve any asynchronous activity.
  * It is not synchronized in any way and not a thread-safe object.
+ *
+ * {@hide}
  */
 public class TokenBucket {
 
diff --git a/core/java/com/android/internal/widget/SwipeDismissLayout.java b/core/java/com/android/internal/widget/SwipeDismissLayout.java
index 83b49eb..b5d6268 100644
--- a/core/java/com/android/internal/widget/SwipeDismissLayout.java
+++ b/core/java/com/android/internal/widget/SwipeDismissLayout.java
@@ -339,18 +339,16 @@
         mVelocityTracker.addMovement(ev);
         mVelocityTracker.computeCurrentVelocity(1000);
         if (!mDismissed) {
-
-            if (deltaX > (getWidth() * DISMISS_MIN_DRAG_WIDTH_RATIO) &&
-                    ev.getRawX() >= mLastX) {
+            if ((deltaX > (getWidth() * DISMISS_MIN_DRAG_WIDTH_RATIO) &&
+                    ev.getRawX() >= mLastX)
+                    || mVelocityTracker.getXVelocity() >= mMinFlingVelocity) {
                 mDismissed = true;
             }
         }
         // Check if the user tried to undo this.
         if (mDismissed && mSwiping) {
-            // Check if the user's finger is actually back
-            if (deltaX < (getWidth() * DISMISS_MIN_DRAG_WIDTH_RATIO) ||
-                    // or user is flinging back left
-                    mVelocityTracker.getXVelocity() < -mMinFlingVelocity) {
+            // Check if the user's finger is actually flinging back to left
+            if (mVelocityTracker.getXVelocity() < -mMinFlingVelocity) {
                 mDismissed = false;
             }
         }
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 44b6e1a..3555057 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -188,8 +188,10 @@
     com_android_internal_util_VirtualRefBasePtr.cpp \
     com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp \
     hwbinder/EphemeralStorage.cpp \
+    fd_utils.cpp \
 
 LOCAL_C_INCLUDES += \
+    $(LOCAL_PATH)/include \
     $(JNI_H_INCLUDE) \
     $(LOCAL_PATH)/android/graphics \
     $(LOCAL_PATH)/../../libs/hwui \
@@ -283,8 +285,10 @@
 # <bionic_tls.h> in com_google_android_gles_jni_GLImpl.cpp
 LOCAL_C_INCLUDES += bionic/libc/private
 
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+
 # AndroidRuntime.h depends on nativehelper/jni.h
-LOCAL_EXPORT_C_INCLUDE_DIRS := libnativehelper/include
+LOCAL_EXPORT_C_INCLUDE_DIRS += libnativehelper/include
 
 LOCAL_MODULE:= libandroid_runtime
 
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 178e073..b656bb0 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -5,7 +5,11 @@
 #include "SkPixelRef.h"
 #include "SkImageEncoder.h"
 #include "SkImageInfo.h"
+#include "SkColor.h"
 #include "SkColorPriv.h"
+#include "SkHalf.h"
+#include "SkPM4f.h"
+#include "SkPM4fPriv.h"
 #include "GraphicsJNI.h"
 #include "SkDither.h"
 #include "SkUnPreMultiply.h"
@@ -232,6 +236,28 @@
 typedef void (*FromColorProc)(void* dst, const SkColor src[], int width,
                               int x, int y);
 
+static void FromColor_F16(void* dst, const SkColor src[], int width,
+                          int, int) {
+    uint64_t* d = (uint64_t*)dst;
+
+    for (int i = 0; i < width; i++) {
+        *d++ = SkColor4f::FromColor(*src++).premul().toF16();
+    }
+}
+
+static void FromColor_F16_Raw(void* dst, const SkColor src[], int width,
+                          int, int) {
+    uint64_t* d = (uint64_t*)dst;
+
+    for (int i = 0; i < width; i++) {
+        const float* color = SkColor4f::FromColor(*src++).vec();
+        uint16_t* scratch = reinterpret_cast<uint16_t*>(d++);
+        for (int i = 0; i < 4; ++i) {
+            scratch[i] = SkFloatToHalf(color[i]);
+        }
+    }
+}
+
 static void FromColor_D32(void* dst, const SkColor src[], int width,
                           int, int) {
     SkPMColor* d = (SkPMColor*)dst;
@@ -321,6 +347,8 @@
             return FromColor_D565;
         case kAlpha_8_SkColorType:
             return FromColor_DA8;
+        case kRGBA_F16_SkColorType:
+            return bitmap.alphaType() == kPremul_SkAlphaType ? FromColor_F16 : FromColor_F16_Raw;
         default:
             break;
     }
@@ -351,8 +379,7 @@
 
     dstBitmap.notifyPixelsChanged();
 
-    env->ReleaseIntArrayElements(srcColors, const_cast<jint*>(array),
-                                 JNI_ABORT);
+    env->ReleaseIntArrayElements(srcColors, const_cast<jint*>(array), JNI_ABORT);
     return true;
 }
 
@@ -361,6 +388,24 @@
 typedef void (*ToColorProc)(SkColor dst[], const void* src, int width,
                             SkColorTable*);
 
+static void ToColor_F16_Alpha(SkColor dst[], const void* src, int width,
+                              SkColorTable*) {
+    SkASSERT(width > 0);
+    uint64_t* s = (uint64_t*)src;
+    do {
+        *dst++ = SkPM4f::FromF16((const uint16_t*) s++).unpremul().toSkColor();
+    } while (--width != 0);
+}
+
+static void ToColor_F16_Raw(SkColor dst[], const void* src, int width,
+                            SkColorTable*) {
+    SkASSERT(width > 0);
+    uint64_t* s = (uint64_t*)src;
+    do {
+        *dst++ = Sk4f_toS32(swizzle_rb(SkHalfToFloat_finite_ftz(*s++)));
+    } while (--width != 0);
+}
+
 static void ToColor_S32_Alpha(SkColor dst[], const void* src, int width,
                               SkColorTable*) {
     SkASSERT(width > 0);
@@ -520,6 +565,17 @@
             }
         case kAlpha_8_SkColorType:
             return ToColor_SA8;
+        case kRGBA_F16_SkColorType:
+            switch (src.alphaType()) {
+                case kOpaque_SkAlphaType:
+                    return ToColor_F16_Raw;
+                case kPremul_SkAlphaType:
+                    return ToColor_F16_Alpha;
+                case kUnpremul_SkAlphaType:
+                    return ToColor_F16_Raw;
+                default:
+                    return NULL;
+            }
         default:
             break;
     }
@@ -554,7 +610,7 @@
 
     SkBitmap bitmap;
     bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType,
-            GraphicsJNI::defaultColorSpace()));
+            GraphicsJNI::colorSpaceForType(colorType)));
 
     sk_sp<Bitmap> nativeBitmap = Bitmap::allocateHeapBitmap(&bitmap, NULL);
     if (!nativeBitmap) {
@@ -562,8 +618,7 @@
     }
 
     if (jColors != NULL) {
-        GraphicsJNI::SetPixels(env, jColors, offset, stride,
-                0, 0, width, height, bitmap);
+        GraphicsJNI::SetPixels(env, jColors, offset, stride, 0, 0, width, height, bitmap);
     }
 
     return createBitmap(env, nativeBitmap.release(), getPremulBitmapCreateFlags(isMutable));
@@ -712,6 +767,9 @@
 
 static jint Bitmap_config(JNIEnv* env, jobject, jlong bitmapHandle) {
     LocalScopedBitmap bitmap(bitmapHandle);
+    if (bitmap->bitmap().isHardware()) {
+        return GraphicsJNI::hardwareLegacyBitmapConfig();
+    }
     return GraphicsJNI::colorTypeToLegacyBitmapConfig(bitmap->info().colorType());
 }
 
@@ -787,6 +845,7 @@
     const int         density = p->readInt32();
 
     if (kN32_SkColorType != colorType &&
+            kRGBA_F16_SkColorType != colorType &&
             kRGB_565_SkColorType != colorType &&
             kARGB_4444_SkColorType != colorType &&
             kIndex_8_SkColorType != colorType &&
@@ -797,8 +856,15 @@
 
     std::unique_ptr<SkBitmap> bitmap(new SkBitmap);
 
-    if (!bitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType,
-            isSRGB ? SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named) : nullptr), rowBytes)) {
+    sk_sp<SkColorSpace> colorSpace;
+    if (kRGBA_F16_SkColorType == colorType) {
+        colorSpace = SkColorSpace::MakeNamed(SkColorSpace::kSRGBLinear_Named);
+    } else {
+        colorSpace = isSRGB ? SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named) : nullptr;
+    }
+
+    if (!bitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType, colorSpace),
+            rowBytes)) {
         return NULL;
     }
 
@@ -1128,8 +1194,19 @@
                               jlong bm1Handle) {
     SkBitmap bm0;
     SkBitmap bm1;
-    reinterpret_cast<BitmapWrapper*>(bm0Handle)->getSkBitmap(&bm0);
-    reinterpret_cast<BitmapWrapper*>(bm1Handle)->getSkBitmap(&bm1);
+
+    LocalScopedBitmap bitmap0(bm0Handle);
+    LocalScopedBitmap bitmap1(bm1Handle);
+
+    // Paying the price for making Hardware Bitmap as Config:
+    // later check for colorType will pass successfully,
+    // because Hardware Config internally may be RGBA8888 or smth like that.
+    if (bitmap0->bitmap().isHardware() != bitmap1->bitmap().isHardware()) {
+        return JNI_FALSE;
+    }
+
+    bitmap0->bitmap().getSkBitmap(&bm0);
+    bitmap1->bitmap().getSkBitmap(&bm1);
     if (bm0.width() != bm1.width() ||
         bm0.height() != bm1.height() ||
         bm0.colorType() != bm1.colorType() ||
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 762a3f3..69c7054 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -283,8 +283,8 @@
 
     // Create the codec.
     NinePatchPeeker peeker;
-    std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::NewFromStream(streamDeleter.release(),
-            &peeker));
+    std::unique_ptr<SkAndroidCodec> codec(SkAndroidCodec::NewFromStream(
+            streamDeleter.release(), &peeker));
     if (!codec.get()) {
         return nullObjectReturn("SkAndroidCodec::NewFromStream returned null");
     }
@@ -395,9 +395,12 @@
     SkAlphaType alphaType = codec->computeOutputAlphaType(requireUnpremultiplied);
 
     const SkImageInfo decodeInfo = SkImageInfo::Make(size.width(), size.height(),
-            decodeColorType, alphaType, GraphicsJNI::defaultColorSpace());
+            decodeColorType, alphaType, codec->computeOutputColorSpace(decodeColorType));
 
-    SkImageInfo bitmapInfo = decodeInfo;
+    // We always decode to sRGB, but only mark the bitmap with a color space if linear
+    // blending is enabled.
+    SkImageInfo bitmapInfo = decodeInfo.makeColorSpace(
+            GraphicsJNI::colorSpaceForType(decodeColorType));
     if (decodeColorType == kGray_8_SkColorType) {
         // The legacy implementation of BitmapFactory used kAlpha8 for
         // grayscale images (before kGray8 existed).  While the codec
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index e740428..6f97c60 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -297,13 +297,16 @@
     kRGB_565_LegacyBitmapConfig     = 3,
     kARGB_4444_LegacyBitmapConfig   = 4,
     kARGB_8888_LegacyBitmapConfig   = 5,
-    kHardware_LegacyBitmapConfig    = 6,
+    kRGBA_16F_LegacyBitmapConfig    = 6,
+    kHardware_LegacyBitmapConfig    = 7,
 
     kLastEnum_LegacyBitmapConfig = kHardware_LegacyBitmapConfig
 };
 
 jint GraphicsJNI::colorTypeToLegacyBitmapConfig(SkColorType colorType) {
     switch (colorType) {
+        case kRGBA_F16_SkColorType:
+            return kRGBA_16F_LegacyBitmapConfig;
         case kN32_SkColorType:
             return kARGB_8888_LegacyBitmapConfig;
         case kARGB_4444_SkColorType:
@@ -329,6 +332,7 @@
         kRGB_565_SkColorType,
         kARGB_4444_SkColorType,
         kN32_SkColorType,
+        kRGBA_F16_SkColorType,
         kN32_SkColorType
     };
 
@@ -366,6 +370,10 @@
     return c == kHardware_LegacyBitmapConfig;
 }
 
+jint GraphicsJNI::hardwareLegacyBitmapConfig() {
+    return kHardware_LegacyBitmapConfig;
+}
+
 android::Canvas* GraphicsJNI::getNativeCanvas(JNIEnv* env, jobject canvas) {
     SkASSERT(env);
     SkASSERT(canvas);
@@ -454,6 +462,19 @@
 #endif
 }
 
+sk_sp<SkColorSpace> GraphicsJNI::linearColorSpace() {
+    return SkColorSpace::MakeNamed(SkColorSpace::kSRGBLinear_Named);
+}
+
+sk_sp<SkColorSpace> GraphicsJNI::colorSpaceForType(SkColorType type) {
+    switch (type) {
+        case kRGBA_F16_SkColorType:
+            return linearColorSpace();
+        default:
+            return defaultColorSpace();
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 bool HeapAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
     mStorage = android::Bitmap::allocateHeapBitmap(bitmap, ctable);
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index ced9939..508c9ff 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -69,6 +69,7 @@
     static SkColorType getNativeBitmapColorType(JNIEnv*, jobject jconfig);
 
     static bool isHardwareConfig(JNIEnv* env, jobject jconfig);
+    static jint hardwareLegacyBitmapConfig();
 
     static jobject createRegion(JNIEnv* env, SkRegion* region);
 
@@ -94,6 +95,8 @@
             const SkBitmap& dstBitmap);
 
     static sk_sp<SkColorSpace> defaultColorSpace();
+    static sk_sp<SkColorSpace> linearColorSpace();
+    static sk_sp<SkColorSpace> colorSpaceForType(SkColorType type);
 };
 
 class HeapAllocator : public SkBRDAllocator {
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index a31bd80..be9449b 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -185,12 +185,12 @@
 
 // SkRegion::Op and SkClipOp are numerically identical, so we can freely cast
 // from one to the other (though SkClipOp is destined to become a strict subset)
-static_assert(SkRegion::kDifference_Op == static_cast<SkRegion::Op>(kDifference_SkClipOp), "");
-static_assert(SkRegion::kIntersect_Op == static_cast<SkRegion::Op>(kIntersect_SkClipOp), "");
-static_assert(SkRegion::kUnion_Op == static_cast<SkRegion::Op>(kUnion_SkClipOp), "");
-static_assert(SkRegion::kXOR_Op == static_cast<SkRegion::Op>(kXOR_SkClipOp), "");
-static_assert(SkRegion::kReverseDifference_Op == static_cast<SkRegion::Op>(kReverseDifference_SkClipOp), "");
-static_assert(SkRegion::kReplace_Op == static_cast<SkRegion::Op>(kReplace_SkClipOp), "");
+static_assert(SkRegion::kDifference_Op == static_cast<SkRegion::Op>(SkClipOp::kDifference), "");
+static_assert(SkRegion::kIntersect_Op == static_cast<SkRegion::Op>(SkClipOp::kIntersect), "");
+static_assert(SkRegion::kUnion_Op == static_cast<SkRegion::Op>(SkClipOp::kUnion), "");
+static_assert(SkRegion::kXOR_Op == static_cast<SkRegion::Op>(SkClipOp::kXOR), "");
+static_assert(SkRegion::kReverseDifference_Op == static_cast<SkRegion::Op>(SkClipOp::kReverseDifference), "");
+static_assert(SkRegion::kReplace_Op == static_cast<SkRegion::Op>(SkClipOp::kReplace), "");
 
 static SkClipOp opHandleToClipOp(jint opHandle) {
     // The opHandle is defined in Canvas.java to be Region::Op
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 26a2cf0..3e99521 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -44,28 +44,33 @@
 
 namespace android {
 
+static const uint32_t kEtherTypeOffset = offsetof(ether_header, ether_type);
+static const uint32_t kEtherHeaderLen = sizeof(ether_header);
+static const uint32_t kIPv4Protocol = kEtherHeaderLen + offsetof(iphdr, protocol);
+static const uint32_t kIPv4FlagsOffset = kEtherHeaderLen + offsetof(iphdr, frag_off);
+static const uint32_t kIPv6NextHeader = kEtherHeaderLen + offsetof(ip6_hdr, ip6_nxt);
+static const uint32_t kIPv6PayloadStart = kEtherHeaderLen + sizeof(ip6_hdr);
+static const uint32_t kICMPv6TypeOffset = kIPv6PayloadStart + offsetof(icmp6_hdr, icmp6_type);
+static const uint32_t kUDPSrcPortIndirectOffset = kEtherHeaderLen + offsetof(udphdr, source);
+static const uint32_t kUDPDstPortIndirectOffset = kEtherHeaderLen + offsetof(udphdr, dest);
 static const uint16_t kDhcpClientPort = 68;
 
 static void android_net_utils_attachDhcpFilter(JNIEnv *env, jobject clazz, jobject javaFd)
 {
-    uint32_t ip_offset = sizeof(ether_header);
-    uint32_t proto_offset = ip_offset + offsetof(iphdr, protocol);
-    uint32_t flags_offset = ip_offset + offsetof(iphdr, frag_off);
-    uint32_t dport_indirect_offset = ip_offset + offsetof(udphdr, dest);
     struct sock_filter filter_code[] = {
         // Check the protocol is UDP.
-        BPF_STMT(BPF_LD  | BPF_B   | BPF_ABS,  proto_offset),
+        BPF_STMT(BPF_LD  | BPF_B   | BPF_ABS,  kIPv4Protocol),
         BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,    IPPROTO_UDP, 0, 6),
 
         // Check this is not a fragment.
-        BPF_STMT(BPF_LD  | BPF_H    | BPF_ABS, flags_offset),
-        BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K,   0x1fff, 4, 0),
+        BPF_STMT(BPF_LD  | BPF_H    | BPF_ABS, kIPv4FlagsOffset),
+        BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K,   IP_OFFMASK, 4, 0),
 
         // Get the IP header length.
-        BPF_STMT(BPF_LDX | BPF_B    | BPF_MSH, ip_offset),
+        BPF_STMT(BPF_LDX | BPF_B    | BPF_MSH, kEtherHeaderLen),
 
         // Check the destination port.
-        BPF_STMT(BPF_LD  | BPF_H    | BPF_IND, dport_indirect_offset),
+        BPF_STMT(BPF_LD  | BPF_H    | BPF_IND, kUDPDstPortIndirectOffset),
         BPF_JUMP(BPF_JMP | BPF_JEQ  | BPF_K,   kDhcpClientPort, 0, 1),
 
         // Accept or reject.
@@ -93,17 +98,13 @@
         return;
     }
 
-    uint32_t ipv6_offset = sizeof(ether_header);
-    uint32_t ipv6_next_header_offset = ipv6_offset + offsetof(ip6_hdr, ip6_nxt);
-    uint32_t icmp6_offset = ipv6_offset + sizeof(ip6_hdr);
-    uint32_t icmp6_type_offset = icmp6_offset + offsetof(icmp6_hdr, icmp6_type);
     struct sock_filter filter_code[] = {
         // Check IPv6 Next Header is ICMPv6.
-        BPF_STMT(BPF_LD  | BPF_B   | BPF_ABS,  ipv6_next_header_offset),
+        BPF_STMT(BPF_LD  | BPF_B   | BPF_ABS,  kIPv6NextHeader),
         BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,    IPPROTO_ICMPV6, 0, 3),
 
         // Check ICMPv6 type is Router Advertisement.
-        BPF_STMT(BPF_LD  | BPF_B   | BPF_ABS,  icmp6_type_offset),
+        BPF_STMT(BPF_LD  | BPF_B   | BPF_ABS,  kICMPv6TypeOffset),
         BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,    ND_ROUTER_ADVERT, 0, 1),
 
         // Accept or reject.
@@ -122,6 +123,81 @@
     }
 }
 
+// TODO: Move all this filter code into libnetutils.
+static void android_net_utils_attachControlPacketFilter(
+        JNIEnv *env, jobject clazz, jobject javaFd, jint hardwareAddressType) {
+    if (hardwareAddressType != ARPHRD_ETHER) {
+        jniThrowExceptionFmt(env, "java/net/SocketException",
+                "attachControlPacketFilter only supports ARPHRD_ETHER");
+        return;
+    }
+
+    // Capture all:
+    //     - ARPs
+    //     - DHCPv4 packets
+    //     - Router Advertisements & Solicitations
+    //     - Neighbor Advertisements & Solicitations
+    //
+    // tcpdump:
+    //     arp or
+    //     '(ip and udp port 68)' or
+    //     '(icmp6 and ip6[40] >= 133 and ip6[40] <= 136)'
+    struct sock_filter filter_code[] = {
+        // Load the link layer next payload field.
+        BPF_STMT(BPF_LD  | BPF_H   | BPF_ABS,  kEtherTypeOffset),
+
+        // Accept all ARP.
+        // TODO: Figure out how to better filter ARPs on noisy networks.
+        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_ARP, 16, 0),
+
+        // If IPv4:
+        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_IP, 0, 9),
+
+        // Check the protocol is UDP.
+        BPF_STMT(BPF_LD  | BPF_B   | BPF_ABS,  kIPv4Protocol),
+        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,    IPPROTO_UDP, 0, 14),
+
+        // Check this is not a fragment.
+        BPF_STMT(BPF_LD  | BPF_H    | BPF_ABS, kIPv4FlagsOffset),
+        BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K,   IP_OFFMASK, 12, 0),
+
+        // Get the IP header length.
+        BPF_STMT(BPF_LDX | BPF_B    | BPF_MSH, kEtherHeaderLen),
+
+        // Check the source port.
+        BPF_STMT(BPF_LD  | BPF_H    | BPF_IND, kUDPSrcPortIndirectOffset),
+        BPF_JUMP(BPF_JMP | BPF_JEQ  | BPF_K,   kDhcpClientPort, 8, 0),
+
+        // Check the destination port.
+        BPF_STMT(BPF_LD  | BPF_H    | BPF_IND, kUDPDstPortIndirectOffset),
+        BPF_JUMP(BPF_JMP | BPF_JEQ  | BPF_K,   kDhcpClientPort, 6, 7),
+
+        // IPv6 ...
+        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_IPV6, 0, 6),
+        // ... check IPv6 Next Header is ICMPv6 (ignore fragments), ...
+        BPF_STMT(BPF_LD  | BPF_B   | BPF_ABS,  kIPv6NextHeader),
+        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,    IPPROTO_ICMPV6, 0, 4),
+        // ... and check the ICMPv6 type is one of RS/RA/NS/NA.
+        BPF_STMT(BPF_LD  | BPF_B   | BPF_ABS,  kICMPv6TypeOffset),
+        BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K,    ND_ROUTER_SOLICIT, 0, 2),
+        BPF_JUMP(BPF_JMP | BPF_JGT | BPF_K,    ND_NEIGHBOR_ADVERT, 1, 0),
+
+        // Accept or reject.
+        BPF_STMT(BPF_RET | BPF_K,              0xffff),
+        BPF_STMT(BPF_RET | BPF_K,              0)
+    };
+    struct sock_fprog filter = {
+        sizeof(filter_code) / sizeof(filter_code[0]),
+        filter_code,
+    };
+
+    int fd = jniGetFDFromFileDescriptor(env, javaFd);
+    if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
+        jniThrowExceptionFmt(env, "java/net/SocketException",
+                "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
+    }
+}
+
 static void android_net_utils_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd,
         jint ifIndex)
 {
@@ -263,6 +339,7 @@
     { "queryUserAccess", "(II)Z", (void*)android_net_utils_queryUserAccess },
     { "attachDhcpFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDhcpFilter },
     { "attachRaFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachRaFilter },
+    { "attachControlPacketFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachControlPacketFilter },
     { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_setupRaSocket },
 };
 
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index 740b24d..2ae4a17 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -26,10 +26,11 @@
 #include <JNIHelp.h>
 #include <android/hidl/manager/1.0/IServiceManager.h>
 #include <android/hidl/base/1.0/IBase.h>
-#include <android/hidl/base/1.0/IHwBase.h>
+#include <android/hidl/base/1.0/BpBase.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <hidl/ServiceManagement.h>
 #include <hidl/Status.h>
+#include <hidl/HidlTransportSupport.h>
 #include <hwbinder/ProcessState.h>
 #include <nativehelper/ScopedLocalRef.h>
 
@@ -241,14 +242,8 @@
     using android::hidl::manager::V1_0::IServiceManager;
 
     sp<hardware::IBinder> binder = JHwBinder::GetNativeContext(env, thiz);
-
-    sp<hidl::base::V1_0::IBase> base = hidl::base::V1_0::IHwBase::asInterface(binder);
-    if (base.get() == nullptr) {
-        LOG(ERROR) << "IBinder object cannot be casted to the base interface.";
-        signalExceptionForError(env, UNKNOWN_ERROR);
-        return;
-    }
-
+    /* TODO(b/33440494) this is not right */
+    sp<hidl::base::V1_0::IBase> base = new hidl::base::V1_0::BpBase(binder);
     bool ok = hardware::defaultServiceManager()->add(
                 interfaceChain,
                 serviceName,
@@ -300,7 +295,7 @@
             serviceName,
             [&service](sp<hidl::base::V1_0::IBase> out) {
                 service = hardware::toBinder<
-                        hidl::base::V1_0::IBase, hidl::base::V1_0::IHwBase
+                        hidl::base::V1_0::IBase, hidl::base::V1_0::BpBase
                     >(out);
             });
 
diff --git a/core/jni/android_view_DisplayListCanvas.cpp b/core/jni/android_view_DisplayListCanvas.cpp
index 74c073f..2c4771c 100644
--- a/core/jni/android_view_DisplayListCanvas.cpp
+++ b/core/jni/android_view_DisplayListCanvas.cpp
@@ -87,19 +87,17 @@
     sp<InvokeRunnableMessage> mMessage;
 };
 
-// ----------------------------------------------------------------------------
-// Setup
-// ----------------------------------------------------------------------------
 
-static void android_view_DisplayListCanvas_insertReorderBarrier(JNIEnv* env, jobject clazz,
-        jlong canvasPtr, jboolean reorderEnable) {
-    Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
-    canvas->insertReorderBarrier(reorderEnable);
+// ---------------- Regular JNI -----------------------------
+
+static void
+android_app_ActivityThread_dumpGraphics(JNIEnv* env, jobject clazz, jobject javaFileDescriptor) {
+    int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor);
+    android::uirenderer::renderthread::RenderProxy::dumpGraphicsMemory(fd);
 }
 
-// ----------------------------------------------------------------------------
-// Functor
-// ----------------------------------------------------------------------------
+
+// ---------------- @FastNative -----------------------------
 
 static void android_view_DisplayListCanvas_callDrawGLFunction(JNIEnv* env, jobject clazz,
         jlong canvasPtr, jlong functorPtr, jobject releasedCallback) {
@@ -112,31 +110,62 @@
     canvas->callDrawGLFunction(functor, bridge.get());
 }
 
-// ----------------------------------------------------------------------------
-// Misc
-// ----------------------------------------------------------------------------
 
-static jint android_view_DisplayListCanvas_getMaxTextureWidth(JNIEnv* env, jobject clazz) {
+// ---------------- @CriticalNative -------------------------
+
+static jlong android_view_DisplayListCanvas_createDisplayListCanvas(jlong renderNodePtr,
+        jint width, jint height) {
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    return reinterpret_cast<jlong>(Canvas::create_recording_canvas(width, height, renderNode));
+}
+
+static void android_view_DisplayListCanvas_resetDisplayListCanvas(jlong canvasPtr,
+        jlong renderNodePtr, jint width, jint height) {
+    Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    canvas->resetRecording(width, height, renderNode);
+}
+
+static jint android_view_DisplayListCanvas_getMaxTextureWidth() {
     if (!Caches::hasInstance()) {
         android::uirenderer::renderthread::RenderProxy::staticFence();
     }
     return Caches::getInstance().maxTextureSize;
 }
 
-static jint android_view_DisplayListCanvas_getMaxTextureHeight(JNIEnv* env, jobject clazz) {
+static jint android_view_DisplayListCanvas_getMaxTextureHeight() {
     if (!Caches::hasInstance()) {
         android::uirenderer::renderthread::RenderProxy::staticFence();
     }
     return Caches::getInstance().maxTextureSize;
 }
 
-// ----------------------------------------------------------------------------
-// Drawing
-// ----------------------------------------------------------------------------
+static void android_view_DisplayListCanvas_insertReorderBarrier(jlong canvasPtr,
+        jboolean reorderEnable) {
+    Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
+    canvas->insertReorderBarrier(reorderEnable);
+}
 
-static void android_view_DisplayListCanvas_drawRoundRectProps(JNIEnv* env, jobject clazz,
-        jlong canvasPtr, jlong leftPropPtr, jlong topPropPtr, jlong rightPropPtr,
-        jlong bottomPropPtr, jlong rxPropPtr, jlong ryPropPtr, jlong paintPropPtr) {
+static jlong android_view_DisplayListCanvas_finishRecording(jlong canvasPtr) {
+    Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
+    return reinterpret_cast<jlong>(canvas->finishRecording());
+}
+
+static void android_view_DisplayListCanvas_drawRenderNode(jlong canvasPtr, jlong renderNodePtr) {
+    Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+    canvas->drawRenderNode(renderNode);
+}
+
+static void android_view_DisplayListCanvas_drawLayer(jlong canvasPtr, jlong layerPtr) {
+    Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
+    DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr);
+    canvas->drawLayer(layer);
+}
+
+static void android_view_DisplayListCanvas_drawRoundRectProps(jlong canvasPtr,
+        jlong leftPropPtr, jlong topPropPtr, jlong rightPropPtr, jlong bottomPropPtr,
+        jlong rxPropPtr, jlong ryPropPtr, jlong paintPropPtr) {
     Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
     CanvasPropertyPrimitive* leftProp = reinterpret_cast<CanvasPropertyPrimitive*>(leftPropPtr);
     CanvasPropertyPrimitive* topProp = reinterpret_cast<CanvasPropertyPrimitive*>(topPropPtr);
@@ -148,8 +177,8 @@
     canvas->drawRoundRect(leftProp, topProp, rightProp, bottomProp, rxProp, ryProp, paintProp);
 }
 
-static void android_view_DisplayListCanvas_drawCircleProps(JNIEnv* env, jobject clazz,
-        jlong canvasPtr, jlong xPropPtr, jlong yPropPtr, jlong radiusPropPtr, jlong paintPropPtr) {
+static void android_view_DisplayListCanvas_drawCircleProps(jlong canvasPtr,
+        jlong xPropPtr, jlong yPropPtr, jlong radiusPropPtr, jlong paintPropPtr) {
     Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
     CanvasPropertyPrimitive* xProp = reinterpret_cast<CanvasPropertyPrimitive*>(xPropPtr);
     CanvasPropertyPrimitive* yProp = reinterpret_cast<CanvasPropertyPrimitive*>(yPropPtr);
@@ -159,58 +188,6 @@
 }
 
 // ----------------------------------------------------------------------------
-// Display lists
-// ----------------------------------------------------------------------------
-
-static jlong android_view_DisplayListCanvas_finishRecording(JNIEnv* env,
-        jobject clazz, jlong canvasPtr) {
-    Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
-    return reinterpret_cast<jlong>(canvas->finishRecording());
-}
-
-static jlong android_view_DisplayListCanvas_createDisplayListCanvas(JNIEnv* env, jobject clazz,
-        jlong renderNodePtr, jint width, jint height) {
-    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
-    return reinterpret_cast<jlong>(Canvas::create_recording_canvas(width, height, renderNode));
-}
-
-static void android_view_DisplayListCanvas_resetDisplayListCanvas(JNIEnv* env, jobject clazz,
-        jlong canvasPtr, jlong renderNodePtr, jint width, jint height) {
-    Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
-    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
-    canvas->resetRecording(width, height, renderNode);
-}
-
-
-static void android_view_DisplayListCanvas_drawRenderNode(JNIEnv* env,
-        jobject clazz, jlong canvasPtr, jlong renderNodePtr) {
-    Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
-    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
-    canvas->drawRenderNode(renderNode);
-}
-
-// ----------------------------------------------------------------------------
-// Layers
-// ----------------------------------------------------------------------------
-
-static void android_view_DisplayListCanvas_drawLayer(JNIEnv* env, jobject clazz,
-        jlong canvasPtr, jlong layerPtr) {
-    Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
-    DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr);
-    canvas->drawLayer(layer);
-}
-
-// ----------------------------------------------------------------------------
-// Logging
-// ----------------------------------------------------------------------------
-
-static void
-android_app_ActivityThread_dumpGraphics(JNIEnv* env, jobject clazz, jobject javaFileDescriptor) {
-    int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor);
-    android::uirenderer::renderthread::RenderProxy::dumpGraphicsMemory(fd);
-}
-
-// ----------------------------------------------------------------------------
 // JNI Glue
 // ----------------------------------------------------------------------------
 
@@ -220,28 +197,25 @@
 
     // ------------ @FastNative ------------------
 
-    { "nInsertReorderBarrier","(JZ)V",         (void*) android_view_DisplayListCanvas_insertReorderBarrier },
-
     { "nCallDrawGLFunction", "(JJLjava/lang/Runnable;)V",
             (void*) android_view_DisplayListCanvas_callDrawGLFunction },
 
-    { "nDrawRoundRect",     "(JJJJJJJJ)V",     (void*) android_view_DisplayListCanvas_drawRoundRectProps },
-    { "nDrawCircle",        "(JJJJJ)V",        (void*) android_view_DisplayListCanvas_drawCircleProps },
-
-    { "nFinishRecording",   "(J)J",            (void*) android_view_DisplayListCanvas_finishRecording },
-    { "nDrawRenderNode",    "(JJ)V",           (void*) android_view_DisplayListCanvas_drawRenderNode },
-
+    // ------------ @CriticalNative --------------
     { "nCreateDisplayListCanvas", "(JII)J",     (void*) android_view_DisplayListCanvas_createDisplayListCanvas },
-    { "nResetDisplayListCanvas", "(JJII)V",     (void*) android_view_DisplayListCanvas_resetDisplayListCanvas },
-
-    { "nDrawLayer",               "(JJ)V",     (void*) android_view_DisplayListCanvas_drawLayer },
-
-    { "nGetMaximumTextureWidth",  "()I",       (void*) android_view_DisplayListCanvas_getMaxTextureWidth },
-    { "nGetMaximumTextureHeight", "()I",       (void*) android_view_DisplayListCanvas_getMaxTextureHeight },
+    { "nResetDisplayListCanvas",  "(JJII)V",    (void*) android_view_DisplayListCanvas_resetDisplayListCanvas },
+    { "nGetMaximumTextureWidth",  "()I",        (void*) android_view_DisplayListCanvas_getMaxTextureWidth },
+    { "nGetMaximumTextureHeight", "()I",        (void*) android_view_DisplayListCanvas_getMaxTextureHeight },
+    { "nInsertReorderBarrier",    "(JZ)V",      (void*) android_view_DisplayListCanvas_insertReorderBarrier },
+    { "nFinishRecording",         "(J)J",       (void*) android_view_DisplayListCanvas_finishRecording },
+    { "nDrawRenderNode",          "(JJ)V",      (void*) android_view_DisplayListCanvas_drawRenderNode },
+    { "nDrawLayer",               "(JJ)V",      (void*) android_view_DisplayListCanvas_drawLayer },
+    { "nDrawCircle",              "(JJJJJ)V",   (void*) android_view_DisplayListCanvas_drawCircleProps },
+    { "nDrawRoundRect",           "(JJJJJJJJ)V",(void*) android_view_DisplayListCanvas_drawRoundRectProps },
 };
 
 static JNINativeMethod gActivityThreadMethods[] = {
-    { "dumpGraphicsInfo",        "(Ljava/io/FileDescriptor;)V",
+        // ------------ Regular JNI ------------------
+    { "nDumpGraphicsInfo",        "(Ljava/io/FileDescriptor;)V",
                                                (void*) android_app_ActivityThread_dumpGraphics }
 };
 
diff --git a/core/jni/android_view_GraphicBuffer.cpp b/core/jni/android_view_GraphicBuffer.cpp
index 53c9ff0..bb69e27 100644
--- a/core/jni/android_view_GraphicBuffer.cpp
+++ b/core/jni/android_view_GraphicBuffer.cpp
@@ -142,6 +142,10 @@
             return kN32_SkColorType;
         case PIXEL_FORMAT_RGBX_8888:
             return kN32_SkColorType;
+        case PIXEL_FORMAT_RGBA_FP16:
+            return kRGBA_F16_SkColorType;
+        case PIXEL_FORMAT_RGBX_FP16:
+            return kRGBA_F16_SkColorType;
         case PIXEL_FORMAT_RGB_565:
             return kRGB_565_SkColorType;
         default:
@@ -197,7 +201,7 @@
     Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas);
     nativeCanvas->setBitmap(bitmap);
     nativeCanvas->clipRect(rect.left, rect.top, rect.right, rect.bottom,
-            kIntersect_SkClipOp);
+            SkClipOp::kIntersect);
 
     if (dirtyRect) {
         INVOKEV(dirtyRect, gRectClassInfo.set,
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index dd2a7a9..d75d5c1 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -51,7 +51,7 @@
     return env;
 }
 
-static jmethodID gOnRenderNodeDetached;
+static jfieldID gRenderNode_validFieldID;
 
 class RenderNodeContext : public VirtualLightRefBase {
 public:
@@ -87,7 +87,10 @@
         node->setUserContext(nullptr);
         return;
     }
-    env->CallVoidMethod(jnode, gOnRenderNodeDetached);
+
+    // Update the valid field, since native has already removed
+    // the staging DisplayList
+    env->SetBooleanField(jnode, gRenderNode_validFieldID, false);
     env->DeleteLocalRef(jnode);
 }
 
@@ -95,14 +98,12 @@
 // DisplayList view properties
 // ----------------------------------------------------------------------------
 
-static void android_view_RenderNode_output(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr) {
+static void android_view_RenderNode_output(JNIEnv* env, jobject clazz, jlong renderNodePtr) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     renderNode->output();
 }
 
-static jint android_view_RenderNode_getDebugSize(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr) {
+static jint android_view_RenderNode_getDebugSize(JNIEnv* env, jobject clazz, jlong renderNodePtr) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     return renderNode->getDebugSize();
 }
@@ -153,59 +154,53 @@
 // RenderProperties - setters
 // ----------------------------------------------------------------------------
 
-static jboolean android_view_RenderNode_setLayerType(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, jint jlayerType) {
+static jboolean android_view_RenderNode_setLayerType(jlong renderNodePtr, jint jlayerType) {
     LayerType layerType = static_cast<LayerType>(jlayerType);
     return SET_AND_DIRTY(mutateLayerProperties().setType, layerType, RenderNode::GENERIC);
 }
 
-static jboolean android_view_RenderNode_setLayerPaint(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, jlong paintPtr) {
+static jboolean android_view_RenderNode_setLayerPaint(jlong renderNodePtr, jlong paintPtr) {
     Paint* paint = reinterpret_cast<Paint*>(paintPtr);
     return SET_AND_DIRTY(mutateLayerProperties().setFromPaint, paint, RenderNode::GENERIC);
 }
 
-static jboolean android_view_RenderNode_setStaticMatrix(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, jlong matrixPtr) {
+static jboolean android_view_RenderNode_setStaticMatrix(jlong renderNodePtr, jlong matrixPtr) {
     SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr);
     return SET_AND_DIRTY(setStaticMatrix, matrix, RenderNode::GENERIC);
 }
 
-static jboolean android_view_RenderNode_setAnimationMatrix(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, jlong matrixPtr) {
+static jboolean android_view_RenderNode_setAnimationMatrix(jlong renderNodePtr, jlong matrixPtr) {
     SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr);
     return SET_AND_DIRTY(setAnimationMatrix, matrix, RenderNode::GENERIC);
 }
 
-static jboolean android_view_RenderNode_setClipToBounds(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, jboolean clipToBounds) {
+static jboolean android_view_RenderNode_setClipToBounds(jlong renderNodePtr,
+        jboolean clipToBounds) {
     return SET_AND_DIRTY(setClipToBounds, clipToBounds, RenderNode::GENERIC);
 }
 
-static jboolean android_view_RenderNode_setClipBounds(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, jint left, jint top, jint right, jint bottom) {
+static jboolean android_view_RenderNode_setClipBounds(jlong renderNodePtr,
+        jint left, jint top, jint right, jint bottom) {
     android::uirenderer::Rect clipBounds(left, top, right, bottom);
     return SET_AND_DIRTY(setClipBounds, clipBounds, RenderNode::GENERIC);
 }
 
-static jboolean android_view_RenderNode_setClipBoundsEmpty(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr) {
+static jboolean android_view_RenderNode_setClipBoundsEmpty(jlong renderNodePtr) {
     return SET_AND_DIRTY(setClipBoundsEmpty,, RenderNode::GENERIC);
 }
 
-static jboolean android_view_RenderNode_setProjectBackwards(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, jboolean shouldProject) {
+static jboolean android_view_RenderNode_setProjectBackwards(jlong renderNodePtr,
+        jboolean shouldProject) {
     return SET_AND_DIRTY(setProjectBackwards, shouldProject, RenderNode::GENERIC);
 }
 
-static jboolean android_view_RenderNode_setProjectionReceiver(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, jboolean shouldRecieve) {
+static jboolean android_view_RenderNode_setProjectionReceiver(jlong renderNodePtr,
+        jboolean shouldRecieve) {
     return SET_AND_DIRTY(setProjectionReceiver, shouldRecieve, RenderNode::GENERIC);
 }
 
-static jboolean android_view_RenderNode_setOutlineRoundRect(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, jint left, jint top,
-        jint right, jint bottom, jfloat radius, jfloat alpha) {
+static jboolean android_view_RenderNode_setOutlineRoundRect(jlong renderNodePtr,
+        jint left, jint top, jint right, jint bottom, jfloat radius, jfloat alpha) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     renderNode->mutateStagingProperties().mutableOutline().setRoundRect(left, top, right, bottom,
             radius, alpha);
@@ -213,8 +208,8 @@
     return true;
 }
 
-static jboolean android_view_RenderNode_setOutlineConvexPath(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, jlong outlinePathPtr, jfloat alpha) {
+static jboolean android_view_RenderNode_setOutlineConvexPath(jlong renderNodePtr,
+        jlong outlinePathPtr, jfloat alpha) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     SkPath* outlinePath = reinterpret_cast<SkPath*>(outlinePathPtr);
     renderNode->mutateStagingProperties().mutableOutline().setConvexPath(outlinePath, alpha);
@@ -222,38 +217,34 @@
     return true;
 }
 
-static jboolean android_view_RenderNode_setOutlineEmpty(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr) {
+static jboolean android_view_RenderNode_setOutlineEmpty(jlong renderNodePtr) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     renderNode->mutateStagingProperties().mutableOutline().setEmpty();
     renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
     return true;
 }
 
-static jboolean android_view_RenderNode_setOutlineNone(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr) {
+static jboolean android_view_RenderNode_setOutlineNone(jlong renderNodePtr) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     renderNode->mutateStagingProperties().mutableOutline().setNone();
     renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
     return true;
 }
 
-static jboolean android_view_RenderNode_hasShadow(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr) {
+static jboolean android_view_RenderNode_hasShadow(jlong renderNodePtr) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     return renderNode->stagingProperties().hasShadow();
 }
 
-static jboolean android_view_RenderNode_setClipToOutline(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, jboolean clipToOutline) {
+static jboolean android_view_RenderNode_setClipToOutline(jlong renderNodePtr,
+        jboolean clipToOutline) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     renderNode->mutateStagingProperties().mutableOutline().setShouldClip(clipToOutline);
     renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
     return true;
 }
 
-static jboolean android_view_RenderNode_setRevealClip(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, jboolean shouldClip,
+static jboolean android_view_RenderNode_setRevealClip(jlong renderNodePtr, jboolean shouldClip,
         jfloat x, jfloat y, jfloat radius) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     renderNode->mutateStagingProperties().mutableRevealClip().set(
@@ -262,100 +253,82 @@
     return true;
 }
 
-static jboolean android_view_RenderNode_setAlpha(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, float alpha) {
+static jboolean android_view_RenderNode_setAlpha(jlong renderNodePtr, float alpha) {
     return SET_AND_DIRTY(setAlpha, alpha, RenderNode::ALPHA);
 }
 
-static jboolean android_view_RenderNode_setHasOverlappingRendering(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, bool hasOverlappingRendering) {
+static jboolean android_view_RenderNode_setHasOverlappingRendering(jlong renderNodePtr,
+        bool hasOverlappingRendering) {
     return SET_AND_DIRTY(setHasOverlappingRendering, hasOverlappingRendering,
             RenderNode::GENERIC);
 }
 
-static jboolean android_view_RenderNode_setElevation(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, float elevation) {
+static jboolean android_view_RenderNode_setElevation(jlong renderNodePtr, float elevation) {
     return SET_AND_DIRTY(setElevation, elevation, RenderNode::Z);
 }
 
-static jboolean android_view_RenderNode_setTranslationX(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, float tx) {
+static jboolean android_view_RenderNode_setTranslationX(jlong renderNodePtr, float tx) {
     return SET_AND_DIRTY(setTranslationX, tx, RenderNode::TRANSLATION_X | RenderNode::X);
 }
 
-static jboolean android_view_RenderNode_setTranslationY(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, float ty) {
+static jboolean android_view_RenderNode_setTranslationY(jlong renderNodePtr, float ty) {
     return SET_AND_DIRTY(setTranslationY, ty, RenderNode::TRANSLATION_Y | RenderNode::Y);
 }
 
-static jboolean android_view_RenderNode_setTranslationZ(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, float tz) {
+static jboolean android_view_RenderNode_setTranslationZ(jlong renderNodePtr, float tz) {
     return SET_AND_DIRTY(setTranslationZ, tz, RenderNode::TRANSLATION_Z | RenderNode::Z);
 }
 
-static jboolean android_view_RenderNode_setRotation(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, float rotation) {
+static jboolean android_view_RenderNode_setRotation(jlong renderNodePtr, float rotation) {
     return SET_AND_DIRTY(setRotation, rotation, RenderNode::ROTATION);
 }
 
-static jboolean android_view_RenderNode_setRotationX(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, float rx) {
+static jboolean android_view_RenderNode_setRotationX(jlong renderNodePtr, float rx) {
     return SET_AND_DIRTY(setRotationX, rx, RenderNode::ROTATION_X);
 }
 
-static jboolean android_view_RenderNode_setRotationY(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, float ry) {
+static jboolean android_view_RenderNode_setRotationY(jlong renderNodePtr, float ry) {
     return SET_AND_DIRTY(setRotationY, ry, RenderNode::ROTATION_Y);
 }
 
-static jboolean android_view_RenderNode_setScaleX(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, float sx) {
+static jboolean android_view_RenderNode_setScaleX(jlong renderNodePtr, float sx) {
     return SET_AND_DIRTY(setScaleX, sx, RenderNode::SCALE_X);
 }
 
-static jboolean android_view_RenderNode_setScaleY(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, float sy) {
+static jboolean android_view_RenderNode_setScaleY(jlong renderNodePtr, float sy) {
     return SET_AND_DIRTY(setScaleY, sy, RenderNode::SCALE_Y);
 }
 
-static jboolean android_view_RenderNode_setPivotX(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, float px) {
+static jboolean android_view_RenderNode_setPivotX(jlong renderNodePtr, float px) {
     return SET_AND_DIRTY(setPivotX, px, RenderNode::GENERIC);
 }
 
-static jboolean android_view_RenderNode_setPivotY(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, float py) {
+static jboolean android_view_RenderNode_setPivotY(jlong renderNodePtr, float py) {
     return SET_AND_DIRTY(setPivotY, py, RenderNode::GENERIC);
 }
 
-static jboolean android_view_RenderNode_setCameraDistance(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, float distance) {
+static jboolean android_view_RenderNode_setCameraDistance(jlong renderNodePtr, float distance) {
     return SET_AND_DIRTY(setCameraDistance, distance, RenderNode::GENERIC);
 }
 
-static jboolean android_view_RenderNode_setLeft(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, int left) {
+static jboolean android_view_RenderNode_setLeft(jlong renderNodePtr, int left) {
     return SET_AND_DIRTY(setLeft, left, RenderNode::X);
 }
 
-static jboolean android_view_RenderNode_setTop(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, int top) {
+static jboolean android_view_RenderNode_setTop(jlong renderNodePtr, int top) {
     return SET_AND_DIRTY(setTop, top, RenderNode::Y);
 }
 
-static jboolean android_view_RenderNode_setRight(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, int right) {
+static jboolean android_view_RenderNode_setRight(jlong renderNodePtr, int right) {
     return SET_AND_DIRTY(setRight, right, RenderNode::X);
 }
 
-static jboolean android_view_RenderNode_setBottom(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, int bottom) {
+static jboolean android_view_RenderNode_setBottom(jlong renderNodePtr, int bottom) {
     return SET_AND_DIRTY(setBottom, bottom, RenderNode::Y);
 }
 
-static jboolean android_view_RenderNode_setLeftTopRightBottom(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, int left, int top,
-        int right, int bottom) {
+static jboolean android_view_RenderNode_setLeftTopRightBottom(jlong renderNodePtr,
+        int left, int top, int right, int bottom) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     if (renderNode->mutateStagingProperties().setLeftTopRightBottom(left, top, right, bottom)) {
         renderNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
@@ -364,13 +337,11 @@
     return false;
 }
 
-static jboolean android_view_RenderNode_offsetLeftAndRight(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, jint offset) {
+static jboolean android_view_RenderNode_offsetLeftAndRight(jlong renderNodePtr, jint offset) {
     return SET_AND_DIRTY(offsetLeftRight, offset, RenderNode::X);
 }
 
-static jboolean android_view_RenderNode_offsetTopAndBottom(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, jint offset) {
+static jboolean android_view_RenderNode_offsetTopAndBottom(jlong renderNodePtr, jint offset) {
     return SET_AND_DIRTY(offsetTopBottom, offset, RenderNode::Y);
 }
 
@@ -378,92 +349,77 @@
 // RenderProperties - getters
 // ----------------------------------------------------------------------------
 
-static jboolean android_view_RenderNode_hasOverlappingRendering(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr) {
+static jboolean android_view_RenderNode_hasOverlappingRendering(jlong renderNodePtr) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     return renderNode->stagingProperties().hasOverlappingRendering();
 }
 
-static jboolean android_view_RenderNode_getClipToOutline(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr) {
+static jboolean android_view_RenderNode_getClipToOutline(jlong renderNodePtr) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     return renderNode->stagingProperties().getOutline().getShouldClip();
 }
 
-static jfloat android_view_RenderNode_getAlpha(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr) {
+static jfloat android_view_RenderNode_getAlpha(jlong renderNodePtr) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     return renderNode->stagingProperties().getAlpha();
 }
 
-static jfloat android_view_RenderNode_getCameraDistance(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr) {
+static jfloat android_view_RenderNode_getCameraDistance(jlong renderNodePtr) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     return renderNode->stagingProperties().getCameraDistance();
 }
 
-static jfloat android_view_RenderNode_getScaleX(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr) {
+static jfloat android_view_RenderNode_getScaleX(jlong renderNodePtr) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     return renderNode->stagingProperties().getScaleX();
 }
 
-static jfloat android_view_RenderNode_getScaleY(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr) {
+static jfloat android_view_RenderNode_getScaleY(jlong renderNodePtr) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     return renderNode->stagingProperties().getScaleY();
 }
 
-static jfloat android_view_RenderNode_getElevation(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr) {
+static jfloat android_view_RenderNode_getElevation(jlong renderNodePtr) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     return renderNode->stagingProperties().getElevation();
 }
 
-static jfloat android_view_RenderNode_getTranslationX(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr) {
+static jfloat android_view_RenderNode_getTranslationX(jlong renderNodePtr) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     return renderNode->stagingProperties().getTranslationX();
 }
 
-static jfloat android_view_RenderNode_getTranslationY(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr) {
+static jfloat android_view_RenderNode_getTranslationY(jlong renderNodePtr) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     return renderNode->stagingProperties().getTranslationY();
 }
 
-static jfloat android_view_RenderNode_getTranslationZ(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr) {
+static jfloat android_view_RenderNode_getTranslationZ(jlong renderNodePtr) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     return renderNode->stagingProperties().getTranslationZ();
 }
 
-static jfloat android_view_RenderNode_getRotation(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr) {
+static jfloat android_view_RenderNode_getRotation(jlong renderNodePtr) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     return renderNode->stagingProperties().getRotation();
 }
 
-static jfloat android_view_RenderNode_getRotationX(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr) {
+static jfloat android_view_RenderNode_getRotationX(jlong renderNodePtr) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     return renderNode->stagingProperties().getRotationX();
 }
 
-static jfloat android_view_RenderNode_getRotationY(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr) {
+static jfloat android_view_RenderNode_getRotationY(jlong renderNodePtr) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     return renderNode->stagingProperties().getRotationY();
 }
 
-static jboolean android_view_RenderNode_isPivotExplicitlySet(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr) {
+static jboolean android_view_RenderNode_isPivotExplicitlySet(jlong renderNodePtr) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     return renderNode->stagingProperties().isPivotExplicitlySet();
 }
 
-static jboolean android_view_RenderNode_hasIdentityMatrix(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr) {
+static jboolean android_view_RenderNode_hasIdentityMatrix(jlong renderNodePtr) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     renderNode->mutateStagingProperties().updateMatrix();
     return !renderNode->stagingProperties().hasTransformMatrix();
@@ -473,8 +429,7 @@
 // RenderProperties - computed getters
 // ----------------------------------------------------------------------------
 
-static void android_view_RenderNode_getTransformMatrix(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, jlong outMatrixPtr) {
+static void android_view_RenderNode_getTransformMatrix(jlong renderNodePtr, jlong outMatrixPtr) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     SkMatrix* outMatrix = reinterpret_cast<SkMatrix*>(outMatrixPtr);
 
@@ -488,10 +443,10 @@
     }
 }
 
-static void android_view_RenderNode_getInverseTransformMatrix(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr, jlong outMatrixPtr) {
+static void android_view_RenderNode_getInverseTransformMatrix(jlong renderNodePtr,
+        jlong outMatrixPtr) {
     // load transform matrix
-    android_view_RenderNode_getTransformMatrix(env, clazz, renderNodePtr, outMatrixPtr);
+    android_view_RenderNode_getTransformMatrix(renderNodePtr, outMatrixPtr);
     SkMatrix* outMatrix = reinterpret_cast<SkMatrix*>(outMatrixPtr);
 
     // return it inverted
@@ -501,15 +456,13 @@
     }
 }
 
-static jfloat android_view_RenderNode_getPivotX(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr) {
+static jfloat android_view_RenderNode_getPivotX(jlong renderNodePtr) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     renderNode->mutateStagingProperties().updateMatrix();
     return renderNode->stagingProperties().getPivotX();
 }
 
-static jfloat android_view_RenderNode_getPivotY(JNIEnv* env,
-        jobject clazz, jlong renderNodePtr) {
+static jfloat android_view_RenderNode_getPivotY(jlong renderNodePtr) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     renderNode->mutateStagingProperties().updateMatrix();
     return renderNode->stagingProperties().getPivotY();
@@ -519,8 +472,8 @@
 // RenderProperties - Animations
 // ----------------------------------------------------------------------------
 
-static void android_view_RenderNode_addAnimator(JNIEnv* env, jobject clazz,
-        jlong renderNodePtr, jlong animatorPtr) {
+static void android_view_RenderNode_addAnimator(JNIEnv* env, jobject clazz, jlong renderNodePtr,
+        jlong animatorPtr) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     RenderPropertyAnimator* animator = reinterpret_cast<RenderPropertyAnimator*>(animatorPtr);
     renderNode->addAnimator(animator);
@@ -651,15 +604,22 @@
 // ----------------------------------------------------------------------------
     { "nCreate",               "(Ljava/lang/String;)J", (void*) android_view_RenderNode_create },
     { "nGetNativeFinalizer",   "()J",    (void*) android_view_RenderNode_getNativeFinalizer },
-    { "nSetDisplayList",       "(JJ)V",   (void*) android_view_RenderNode_setDisplayList },
     { "nOutput",               "(J)V",    (void*) android_view_RenderNode_output },
     { "nGetDebugSize",         "(J)I",    (void*) android_view_RenderNode_getDebugSize },
     { "nAddAnimator",              "(JJ)V", (void*) android_view_RenderNode_addAnimator },
     { "nEndAllAnimators",          "(J)V", (void*) android_view_RenderNode_endAllAnimators },
     { "nRequestPositionUpdates",   "(JLandroid/view/SurfaceView;)V", (void*) android_view_RenderNode_requestPositionUpdates },
+    { "nSetDisplayList",       "(JJ)V",   (void*) android_view_RenderNode_setDisplayList },
+
 
 // ----------------------------------------------------------------------------
-// Fast JNI via @FastNative annotation in RenderNode.java
+// Fast JNI via @CriticalNative annotation in RenderNode.java
+// ----------------------------------------------------------------------------
+    { "nSetDisplayList",       "(JJ)V",   (void*) android_view_RenderNode_setDisplayList },
+
+
+// ----------------------------------------------------------------------------
+// Critical JNI via @CriticalNative annotation in RenderNode.java
 // ----------------------------------------------------------------------------
     { "nSetLayerType",         "(JI)Z",  (void*) android_view_RenderNode_setLayerType },
     { "nSetLayerPaint",        "(JJ)Z",  (void*) android_view_RenderNode_setLayerPaint },
@@ -732,8 +692,7 @@
     gSurfaceViewPositionLostMethod = GetMethodIDOrDie(env, clazz,
             "windowPositionLost_uiRtSync", "(J)V");
     clazz = FindClassOrDie(env, "android/view/RenderNode");
-    gOnRenderNodeDetached = GetMethodIDOrDie(env, clazz,
-            "onRenderNodeDetached", "()V");
+    gRenderNode_validFieldID = GetFieldIDOrDie(env, clazz, "mValid", "Z");
     return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
 }
 
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 63997e5..a5b7671 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -168,6 +168,8 @@
     switch(format) {
         case HAL_PIXEL_FORMAT_RGBA_8888:
         case HAL_PIXEL_FORMAT_RGBX_8888:
+        case HAL_PIXEL_FORMAT_RGBA_FP16:
+        case HAL_PIXEL_FORMAT_RGBX_FP16:
         case HAL_PIXEL_FORMAT_RGB_888:
         case HAL_PIXEL_FORMAT_RGB_565:
         case HAL_PIXEL_FORMAT_Y8:
@@ -283,6 +285,8 @@
     switch (format) {
     case PIXEL_FORMAT_RGBX_8888:    return kN32_SkColorType;
     case PIXEL_FORMAT_RGBA_8888:    return kN32_SkColorType;
+    case PIXEL_FORMAT_RGBX_FP16:    return kRGBA_F16_SkColorType;
+    case PIXEL_FORMAT_RGBA_FP16:    return kRGBA_F16_SkColorType;
     case PIXEL_FORMAT_RGB_565:      return kRGB_565_SkColorType;
     default:                        return kUnknown_SkColorType;
     }
@@ -339,7 +343,7 @@
 
     if (dirtyRectPtr) {
         nativeCanvas->clipRect(dirtyRect.left, dirtyRect.top,
-                dirtyRect.right, dirtyRect.bottom, kIntersect_SkClipOp);
+                dirtyRect.right, dirtyRect.bottom, SkClipOp::kIntersect);
     }
 
     if (dirtyRectObj) {
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 5b88181..62c3d04 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -164,6 +164,16 @@
             alphaType = kPremul_SkAlphaType;
             break;
         }
+        case PIXEL_FORMAT_RGBX_FP16: {
+            colorType = kRGBA_F16_SkColorType;
+            alphaType = kOpaque_SkAlphaType;
+            break;
+        }
+        case PIXEL_FORMAT_RGBA_FP16: {
+            colorType = kRGBA_F16_SkColorType;
+            alphaType = kPremul_SkAlphaType;
+            break;
+        }
         case PIXEL_FORMAT_RGB_565: {
             colorType = kRGB_565_SkColorType;
             alphaType = kOpaque_SkAlphaType;
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index 351dce9..d3c0202 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -83,6 +83,14 @@
             colorType = kN32_SkColorType;
             alphaType = kOpaque_SkAlphaType;
             break;
+        case WINDOW_FORMAT_RGBA_FP16:
+            colorType = kRGBA_F16_SkColorType;
+            alphaType = kPremul_SkAlphaType;
+            break;
+        case WINDOW_FORMAT_RGBX_FP16:
+            colorType = kRGBA_F16_SkColorType;
+            alphaType = kOpaque_SkAlphaType;
+            break;
         case WINDOW_FORMAT_RGB_565:
             colorType = kRGB_565_SkColorType;
             alphaType = kOpaque_SkAlphaType;
@@ -167,7 +175,7 @@
     Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, canvas);
     nativeCanvas->setBitmap(bitmap);
     nativeCanvas->clipRect(rect.left, rect.top, rect.right, rect.bottom,
-            kIntersect_SkClipOp);
+            SkClipOp::kIntersect);
 
     if (dirtyRect) {
         INVOKEV(dirtyRect, gRectClassInfo.set,
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index 7e01657..f8f9efe 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -49,9 +49,6 @@
 
 #define RS_BITCODE_SUFFIX ".bc"
 
-#define GDBSERVER "gdbserver"
-#define GDBSERVER_LEN (sizeof(GDBSERVER) - 1)
-
 #define TMP_FILE_PATTERN "/tmp.XXXXXX"
 #define TMP_FILE_PATTERN_LEN (sizeof(TMP_FILE_PATTERN) - 1)
 
@@ -246,7 +243,7 @@
         return INSTALL_FAILED_INTERNAL_ERROR;
     }
 
-    *(localFileName + nativeLibPath.size()) = '/';
+    *(localTmpFileName + nativeLibPath.size()) = '/';
 
     if (strlcpy(localTmpFileName + nativeLibPath.size(), TMP_FILE_PATTERN,
                     TMP_FILE_PATTERN_LEN - nativeLibPath.size()) != TMP_FILE_PATTERN_LEN) {
@@ -313,20 +310,20 @@
  */
 class NativeLibrariesIterator {
 private:
-    NativeLibrariesIterator(ZipFileRO* zipFile, void* cookie)
-        : mZipFile(zipFile), mCookie(cookie), mLastSlash(NULL) {
+    NativeLibrariesIterator(ZipFileRO* zipFile, bool debuggable, void* cookie)
+        : mZipFile(zipFile), mDebuggable(debuggable), mCookie(cookie), mLastSlash(NULL) {
         fileName[0] = '\0';
     }
 
 public:
-    static NativeLibrariesIterator* create(ZipFileRO* zipFile) {
+    static NativeLibrariesIterator* create(ZipFileRO* zipFile, bool debuggable) {
         void* cookie = NULL;
         // Do not specify a suffix to find both .so files and gdbserver.
         if (!zipFile->startIteration(&cookie, APK_LIB, NULL /* suffix */)) {
             return NULL;
         }
 
-        return new NativeLibrariesIterator(zipFile, cookie);
+        return new NativeLibrariesIterator(zipFile, debuggable, cookie);
     }
 
     ZipEntryRO next() {
@@ -347,15 +344,8 @@
             const char* lastSlash = strrchr(fileName, '/');
             ALOG_ASSERT(lastSlash != NULL, "last slash was null somehow for %s\n", fileName);
 
-            // Exception: If we find the gdbserver binary, return it.
-            if (!strncmp(lastSlash + 1, GDBSERVER, GDBSERVER_LEN)) {
-                mLastSlash = lastSlash;
-                break;
-            }
-
-            // Make sure the filename starts with lib and ends with ".so".
-            if (strncmp(fileName + fileNameLen - LIB_SUFFIX_LEN, LIB_SUFFIX, LIB_SUFFIX_LEN)
-                || strncmp(lastSlash, LIB_PREFIX, LIB_PREFIX_LEN)) {
+            // Skip directories.
+            if (*(lastSlash + 1) == 0) {
                 continue;
             }
 
@@ -364,6 +354,14 @@
                 continue;
             }
 
+            if (!mDebuggable) {
+              // Make sure the filename starts with lib and ends with ".so".
+              if (strncmp(fileName + fileNameLen - LIB_SUFFIX_LEN, LIB_SUFFIX, LIB_SUFFIX_LEN)
+                  || strncmp(lastSlash, LIB_PREFIX, LIB_PREFIX_LEN)) {
+                  continue;
+              }
+            }
+
             mLastSlash = lastSlash;
             break;
         }
@@ -386,19 +384,21 @@
 
     char fileName[PATH_MAX];
     ZipFileRO* const mZipFile;
+    const bool mDebuggable;
     void* mCookie;
     const char* mLastSlash;
 };
 
 static install_status_t
 iterateOverNativeFiles(JNIEnv *env, jlong apkHandle, jstring javaCpuAbi,
-                       iterFunc callFunc, void* callArg) {
+                       jboolean debuggable, iterFunc callFunc, void* callArg) {
     ZipFileRO* zipFile = reinterpret_cast<ZipFileRO*>(apkHandle);
     if (zipFile == NULL) {
         return INSTALL_FAILED_INVALID_APK;
     }
 
-    std::unique_ptr<NativeLibrariesIterator> it(NativeLibrariesIterator::create(zipFile));
+    std::unique_ptr<NativeLibrariesIterator> it(
+            NativeLibrariesIterator::create(zipFile, debuggable));
     if (it.get() == NULL) {
         return INSTALL_FAILED_INVALID_APK;
     }
@@ -432,7 +432,8 @@
 }
 
 
-static int findSupportedAbi(JNIEnv *env, jlong apkHandle, jobjectArray supportedAbisArray) {
+static int findSupportedAbi(JNIEnv *env, jlong apkHandle, jobjectArray supportedAbisArray,
+        jboolean debuggable) {
     const int numAbis = env->GetArrayLength(supportedAbisArray);
     Vector<ScopedUtfChars*> supportedAbis;
 
@@ -446,7 +447,8 @@
         return INSTALL_FAILED_INVALID_APK;
     }
 
-    std::unique_ptr<NativeLibrariesIterator> it(NativeLibrariesIterator::create(zipFile));
+    std::unique_ptr<NativeLibrariesIterator> it(
+            NativeLibrariesIterator::create(zipFile, debuggable));
     if (it.get() == NULL) {
         return INSTALL_FAILED_INVALID_APK;
     }
@@ -488,29 +490,29 @@
 static jint
 com_android_internal_content_NativeLibraryHelper_copyNativeBinaries(JNIEnv *env, jclass clazz,
         jlong apkHandle, jstring javaNativeLibPath, jstring javaCpuAbi,
-        jboolean extractNativeLibs, jboolean hasNativeBridge)
+        jboolean extractNativeLibs, jboolean hasNativeBridge, jboolean debuggable)
 {
     void* args[] = { &javaNativeLibPath, &extractNativeLibs, &hasNativeBridge };
-    return (jint) iterateOverNativeFiles(env, apkHandle, javaCpuAbi,
+    return (jint) iterateOverNativeFiles(env, apkHandle, javaCpuAbi, debuggable,
             copyFileIfChanged, reinterpret_cast<void*>(args));
 }
 
 static jlong
 com_android_internal_content_NativeLibraryHelper_sumNativeBinaries(JNIEnv *env, jclass clazz,
-        jlong apkHandle, jstring javaCpuAbi)
+        jlong apkHandle, jstring javaCpuAbi, jboolean debuggable)
 {
     size_t totalSize = 0;
 
-    iterateOverNativeFiles(env, apkHandle, javaCpuAbi, sumFiles, &totalSize);
+    iterateOverNativeFiles(env, apkHandle, javaCpuAbi, debuggable, sumFiles, &totalSize);
 
     return totalSize;
 }
 
 static jint
 com_android_internal_content_NativeLibraryHelper_findSupportedAbi(JNIEnv *env, jclass clazz,
-        jlong apkHandle, jobjectArray javaCpuAbisToSearch)
+        jlong apkHandle, jobjectArray javaCpuAbisToSearch, jboolean debuggable)
 {
-    return (jint) findSupportedAbi(env, apkHandle, javaCpuAbisToSearch);
+    return (jint) findSupportedAbi(env, apkHandle, javaCpuAbisToSearch, debuggable);
 }
 
 enum bitcode_scan_result_t {
@@ -569,13 +571,13 @@
             "(J)V",
             (void *)com_android_internal_content_NativeLibraryHelper_close},
     {"nativeCopyNativeBinaries",
-            "(JLjava/lang/String;Ljava/lang/String;ZZ)I",
+            "(JLjava/lang/String;Ljava/lang/String;ZZZ)I",
             (void *)com_android_internal_content_NativeLibraryHelper_copyNativeBinaries},
     {"nativeSumNativeBinaries",
-            "(JLjava/lang/String;)J",
+            "(JLjava/lang/String;Z)J",
             (void *)com_android_internal_content_NativeLibraryHelper_sumNativeBinaries},
     {"nativeFindSupportedAbi",
-            "(J[Ljava/lang/String;)I",
+            "(J[Ljava/lang/String;Z)I",
             (void *)com_android_internal_content_NativeLibraryHelper_findSupportedAbi},
     {"hasRenderscriptBitcode", "(J)I",
             (void *)com_android_internal_content_NativeLibraryHelper_hasRenderscriptBitcode},
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index da059e3..cc7b958 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -56,7 +56,7 @@
 #include "ScopedLocalRef.h"
 #include "ScopedPrimitiveArray.h"
 #include "ScopedUtfChars.h"
-#include "fd_utils-inl.h"
+#include "fd_utils.h"
 
 #include "nativebridge/native_bridge.h"
 
@@ -709,6 +709,16 @@
   return pid;
 }
 
+static void com_android_internal_os_Zygote_nativeAllowFileAcrossFork(
+        JNIEnv* env, jclass, jstring path) {
+    ScopedUtfChars path_native(env, path);
+    const char* path_cstr = path_native.c_str();
+    if (!path_cstr) {
+        RuntimeAbort(env, __LINE__, "path_cstr == NULL");
+    }
+    FileDescriptorWhitelist::Get()->Allow(path_cstr);
+}
+
 static void com_android_internal_os_Zygote_nativeUnmountStorageOnInit(JNIEnv* env, jclass) {
     // Zygote process unmount root storage space initially before every child processes are forked.
     // Every forked child processes (include SystemServer) only mount their own root storage space
@@ -753,6 +763,8 @@
       (void *) com_android_internal_os_Zygote_nativeForkAndSpecialize },
     { "nativeForkSystemServer", "(II[II[[IJJ)I",
       (void *) com_android_internal_os_Zygote_nativeForkSystemServer },
+    { "nativeAllowFileAcrossFork", "(Ljava/lang/String;)V",
+      (void *) com_android_internal_os_Zygote_nativeAllowFileAcrossFork },
     { "nativeUnmountStorageOnInit", "()V",
       (void *) com_android_internal_os_Zygote_nativeUnmountStorageOnInit }
 };
diff --git a/core/jni/fd_utils-inl.h b/core/jni/fd_utils-inl.h
deleted file mode 100644
index e270911..0000000
--- a/core/jni/fd_utils-inl.h
+++ /dev/null
@@ -1,587 +0,0 @@
-/*
- * Copyright (C) 2016 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 <string>
-#include <unordered_map>
-#include <set>
-#include <vector>
-#include <algorithm>
-
-#include <android-base/strings.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <grp.h>
-#include <inttypes.h>
-#include <stdlib.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/un.h>
-#include <unistd.h>
-
-#include <cutils/log.h>
-#include "JNIHelp.h"
-#include "ScopedPrimitiveArray.h"
-
-// Whitelist of open paths that the zygote is allowed to keep open.
-//
-// In addition to the paths listed here, all files ending with
-// ".jar" under /system/framework" are whitelisted. See
-// FileDescriptorInfo::IsWhitelisted for the canonical definition.
-//
-// If the whitelisted path is associated with a regular file or a
-// character device, the file is reopened after a fork with the same
-// offset and mode. If the whilelisted  path is associated with a
-// AF_UNIX socket, the socket will refer to /dev/null after each
-// fork, and all operations on it will fail.
-static const char* kPathWhitelist[] = {
-  "/dev/null",
-  "/dev/socket/zygote",
-  "/dev/socket/zygote_secondary",
-  "/dev/socket/webview_zygote",
-  "/sys/kernel/debug/tracing/trace_marker",
-  "/system/framework/framework-res.apk",
-  "/dev/urandom",
-  "/dev/ion",
-  "/dev/dri/renderD129", // Fixes b/31172436
-};
-
-static const char* kFdPath = "/proc/self/fd";
-
-// Keeps track of all relevant information (flags, offset etc.) of an
-// open zygote file descriptor.
-class FileDescriptorInfo {
- public:
-  // Create a FileDescriptorInfo for a given file descriptor. Returns
-  // |NULL| if an error occurred.
-  static FileDescriptorInfo* createFromFd(int fd) {
-    struct stat f_stat;
-    // This should never happen; the zygote should always have the right set
-    // of permissions required to stat all its open files.
-    if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
-      ALOGE("Unable to stat fd %d : %s", fd, strerror(errno));
-      return NULL;
-    }
-
-    if (S_ISSOCK(f_stat.st_mode)) {
-      std::string socket_name;
-      if (!GetSocketName(fd, &socket_name)) {
-        return NULL;
-      }
-
-      if (!IsWhitelisted(socket_name)) {
-        ALOGE("Socket name not whitelisted : %s (fd=%d)", socket_name.c_str(), fd);
-        return NULL;
-      }
-
-      return new FileDescriptorInfo(fd);
-    }
-
-    // We only handle whitelisted regular files and character devices. Whitelisted
-    // character devices must provide a guarantee of sensible behaviour when
-    // reopened.
-    //
-    // S_ISDIR : Not supported. (We could if we wanted to, but it's unused).
-    // S_ISLINK : Not supported.
-    // S_ISBLK : Not supported.
-    // S_ISFIFO : Not supported. Note that the zygote uses pipes to communicate
-    // with the child process across forks but those should have been closed
-    // before we got to this point.
-    if (!S_ISCHR(f_stat.st_mode) && !S_ISREG(f_stat.st_mode)) {
-      ALOGE("Unsupported st_mode %d", f_stat.st_mode);
-      return NULL;
-    }
-
-    std::string file_path;
-    if (!Readlink(fd, &file_path)) {
-      return NULL;
-    }
-
-    if (!IsWhitelisted(file_path)) {
-      ALOGE("Not whitelisted : %s", file_path.c_str());
-      return NULL;
-    }
-
-    // File descriptor flags : currently on FD_CLOEXEC. We can set these
-    // using F_SETFD - we're single threaded at this point of execution so
-    // there won't be any races.
-    const int fd_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD));
-    if (fd_flags == -1) {
-      ALOGE("Failed fcntl(%d, F_GETFD) : %s", fd, strerror(errno));
-      return NULL;
-    }
-
-    // File status flags :
-    // - File access mode : (O_RDONLY, O_WRONLY...) we'll pass these through
-    //   to the open() call.
-    //
-    // - File creation flags : (O_CREAT, O_EXCL...) - there's not much we can
-    //   do about these, since the file has already been created. We shall ignore
-    //   them here.
-    //
-    // - Other flags : We'll have to set these via F_SETFL. On linux, F_SETFL
-    //   can only set O_APPEND, O_ASYNC, O_DIRECT, O_NOATIME, and O_NONBLOCK.
-    //   In particular, it can't set O_SYNC and O_DSYNC. We'll have to test for
-    //   their presence and pass them in to open().
-    int fs_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL));
-    if (fs_flags == -1) {
-      ALOGE("Failed fcntl(%d, F_GETFL) : %s", fd, strerror(errno));
-      return NULL;
-    }
-
-    // File offset : Ignore the offset for non seekable files.
-    const off_t offset = TEMP_FAILURE_RETRY(lseek64(fd, 0, SEEK_CUR));
-
-    // We pass the flags that open accepts to open, and use F_SETFL for
-    // the rest of them.
-    static const int kOpenFlags = (O_RDONLY | O_WRONLY | O_RDWR | O_DSYNC | O_SYNC);
-    int open_flags = fs_flags & (kOpenFlags);
-    fs_flags = fs_flags & (~(kOpenFlags));
-
-    return new FileDescriptorInfo(f_stat, file_path, fd, open_flags, fd_flags, fs_flags, offset);
-  }
-
-  // Checks whether the file descriptor associated with this object
-  // refers to the same description.
-  bool Restat() const {
-    struct stat f_stat;
-    if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
-      return false;
-    }
-
-    return f_stat.st_ino == stat.st_ino && f_stat.st_dev == stat.st_dev;
-  }
-
-  bool ReopenOrDetach() const {
-    if (is_sock) {
-      return DetachSocket();
-    }
-
-    // NOTE: This might happen if the file was unlinked after being opened.
-    // It's a common pattern in the case of temporary files and the like but
-    // we should not allow such usage from the zygote.
-    const int new_fd = TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags));
-
-    if (new_fd == -1) {
-      ALOGE("Failed open(%s, %d) : %s", file_path.c_str(), open_flags, strerror(errno));
-      return false;
-    }
-
-    if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFD, fd_flags)) == -1) {
-      close(new_fd);
-      ALOGE("Failed fcntl(%d, F_SETFD, %x) : %s", new_fd, fd_flags, strerror(errno));
-      return false;
-    }
-
-    if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFL, fs_flags)) == -1) {
-      close(new_fd);
-      ALOGE("Failed fcntl(%d, F_SETFL, %x) : %s", new_fd, fs_flags, strerror(errno));
-      return false;
-    }
-
-    if (offset != -1 && TEMP_FAILURE_RETRY(lseek64(new_fd, offset, SEEK_SET)) == -1) {
-      close(new_fd);
-      ALOGE("Failed lseek64(%d, SEEK_SET) : %s", new_fd, strerror(errno));
-      return false;
-    }
-
-    if (TEMP_FAILURE_RETRY(dup2(new_fd, fd)) == -1) {
-      close(new_fd);
-      ALOGE("Failed dup2(%d, %d) : %s", fd, new_fd, strerror(errno));
-      return false;
-    }
-
-    close(new_fd);
-
-    return true;
-  }
-
-  const int fd;
-  const struct stat stat;
-  const std::string file_path;
-  const int open_flags;
-  const int fd_flags;
-  const int fs_flags;
-  const off_t offset;
-  const bool is_sock;
-
- private:
-  FileDescriptorInfo(int fd) :
-    fd(fd),
-    stat(),
-    open_flags(0),
-    fd_flags(0),
-    fs_flags(0),
-    offset(0),
-    is_sock(true) {
-  }
-
-  FileDescriptorInfo(struct stat stat, const std::string& file_path, int fd, int open_flags,
-                     int fd_flags, int fs_flags, off_t offset) :
-    fd(fd),
-    stat(stat),
-    file_path(file_path),
-    open_flags(open_flags),
-    fd_flags(fd_flags),
-    fs_flags(fs_flags),
-    offset(offset),
-    is_sock(false) {
-  }
-
-  static bool StartsWith(const std::string& str, const std::string& prefix) {
-    return str.compare(0, prefix.size(), prefix) == 0;
-  }
-
-  static bool EndsWith(const std::string& str, const std::string& suffix) {
-    if (suffix.size() > str.size()) {
-      return false;
-    }
-
-    return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
-  }
-
-  // Returns true iff. a given path is whitelisted. A path is whitelisted
-  // if it belongs to the whitelist (see kPathWhitelist) or if it's a path
-  // under /system/framework that ends with ".jar" or if it is a system
-  // framework overlay.
-  static bool IsWhitelisted(const std::string& path) {
-    for (size_t i = 0; i < (sizeof(kPathWhitelist) / sizeof(kPathWhitelist[0])); ++i) {
-      if (kPathWhitelist[i] == path) {
-        return true;
-      }
-    }
-
-    static const std::string kFrameworksPrefix = "/system/framework/";
-    static const std::string kJarSuffix = ".jar";
-    if (StartsWith(path, kFrameworksPrefix) && EndsWith(path, kJarSuffix)) {
-      return true;
-    }
-
-    // Whitelist files needed for Runtime Resource Overlay, like these:
-    // /system/vendor/overlay/framework-res.apk
-    // /system/vendor/overlay-subdir/pg/framework-res.apk
-    // /vendor/overlay/framework-res.apk
-    // /vendor/overlay/PG/android-framework-runtime-resource-overlay.apk
-    // /data/resource-cache/system@vendor@overlay@framework-res.apk@idmap
-    // /data/resource-cache/system@vendor@overlay-subdir@pg@framework-res.apk@idmap
-    // See AssetManager.cpp for more details on overlay-subdir.
-    static const std::string kOverlayDir = "/system/vendor/overlay/";
-    static const std::string kVendorOverlayDir = "/vendor/overlay";
-    static const std::string kOverlaySubdir = "/system/vendor/overlay-subdir/";
-    static const std::string kApkSuffix = ".apk";
-
-    if ((StartsWith(path, kOverlayDir) || StartsWith(path, kOverlaySubdir)
-         || StartsWith(path, kVendorOverlayDir))
-        && EndsWith(path, kApkSuffix)
-        && path.find("/../") == std::string::npos) {
-      return true;
-    }
-
-    static const std::string kOverlayIdmapPrefix = "/data/resource-cache/";
-    static const std::string kOverlayIdmapSuffix = ".apk@idmap";
-    if (StartsWith(path, kOverlayIdmapPrefix) && EndsWith(path, kOverlayIdmapSuffix)
-        && path.find("/../") == std::string::npos) {
-      return true;
-    }
-
-    // All regular files that are placed under this path are whitelisted automatically.
-    static const std::string kZygoteWhitelistPath = "/vendor/zygote_whitelist/";
-    if (StartsWith(path, kZygoteWhitelistPath) && path.find("/../") == std::string::npos) {
-      return true;
-    }
-
-    return false;
-  }
-
-  // TODO: Call android::base::Readlink instead of copying the code here.
-  static bool Readlink(const int fd, std::string* result) {
-    char path[64];
-    snprintf(path, sizeof(path), "/proc/self/fd/%d", fd);
-
-    // Code copied from android::base::Readlink starts here :
-
-    // Annoyingly, the readlink system call returns EINVAL for a zero-sized buffer,
-    // and truncates to whatever size you do supply, so it can't be used to query.
-    // We could call lstat first, but that would introduce a race condition that
-    // we couldn't detect.
-    // ext2 and ext4 both have PAGE_SIZE limitations, so we assume that here.
-    char buf[4096];
-    ssize_t len = readlink(path, buf, sizeof(buf));
-    if (len == -1) return false;
-
-    result->assign(buf, len);
-    return true;
-  }
-
-  // Returns the locally-bound name of the socket |fd|. Returns true
-  // iff. all of the following hold :
-  //
-  // - the socket's sa_family is AF_UNIX.
-  // - the length of the path is greater than zero (i.e, not an unnamed socket).
-  // - the first byte of the path isn't zero (i.e, not a socket with an abstract
-  //   address).
-  static bool GetSocketName(const int fd, std::string* result) {
-    sockaddr_storage ss;
-    sockaddr* addr = reinterpret_cast<sockaddr*>(&ss);
-    socklen_t addr_len = sizeof(ss);
-
-    if (TEMP_FAILURE_RETRY(getsockname(fd, addr, &addr_len)) == -1) {
-      ALOGE("Failed getsockname(%d) : %s", fd, strerror(errno));
-      return false;
-    }
-
-    if (addr->sa_family != AF_UNIX) {
-      ALOGE("Unsupported socket (fd=%d) with family %d", fd, addr->sa_family);
-      return false;
-    }
-
-    const sockaddr_un* unix_addr = reinterpret_cast<const sockaddr_un*>(&ss);
-
-    size_t path_len = addr_len - offsetof(struct sockaddr_un, sun_path);
-    // This is an unnamed local socket, we do not accept it.
-    if (path_len == 0) {
-      ALOGE("Unsupported AF_UNIX socket (fd=%d) with empty path.", fd);
-      return false;
-    }
-
-    // This is a local socket with an abstract address, we do not accept it.
-    if (unix_addr->sun_path[0] == '\0') {
-      ALOGE("Unsupported AF_UNIX socket (fd=%d) with abstract address.", fd);
-      return false;
-    }
-
-    // If we're here, sun_path must refer to a null terminated filesystem
-    // pathname (man 7 unix). Remove the terminator before assigning it to an
-    // std::string.
-    if (unix_addr->sun_path[path_len - 1] ==  '\0') {
-      --path_len;
-    }
-
-    result->assign(unix_addr->sun_path, path_len);
-    return true;
-  }
-
-  bool DetachSocket() const {
-    const int dev_null_fd = open("/dev/null", O_RDWR);
-    if (dev_null_fd < 0) {
-      ALOGE("Failed to open /dev/null : %s", strerror(errno));
-      return false;
-    }
-
-    if (dup2(dev_null_fd, fd) == -1) {
-      ALOGE("Failed dup2 on socket descriptor %d : %s", fd, strerror(errno));
-      return false;
-    }
-
-    if (close(dev_null_fd) == -1) {
-      ALOGE("Failed close(%d) : %s", dev_null_fd, strerror(errno));
-      return false;
-    }
-
-    return true;
-  }
-
-  DISALLOW_COPY_AND_ASSIGN(FileDescriptorInfo);
-};
-
-// A FileDescriptorTable is a collection of FileDescriptorInfo objects
-// keyed by their FDs.
-class FileDescriptorTable {
- public:
-  // Creates a new FileDescriptorTable. This function scans
-  // /proc/self/fd for the list of open file descriptors and collects
-  // information about them. Returns NULL if an error occurs.
-  static FileDescriptorTable* Create() {
-    DIR* d = opendir(kFdPath);
-    if (d == NULL) {
-      ALOGE("Unable to open directory %s: %s", kFdPath, strerror(errno));
-      return NULL;
-    }
-    int dir_fd = dirfd(d);
-    dirent* e;
-
-    std::unordered_map<int, FileDescriptorInfo*> open_fd_map;
-    while ((e = readdir(d)) != NULL) {
-      const int fd = ParseFd(e, dir_fd);
-      if (fd == -1) {
-        continue;
-      }
-
-      FileDescriptorInfo* info = FileDescriptorInfo::createFromFd(fd);
-      if (info == NULL) {
-        if (closedir(d) == -1) {
-          ALOGE("Unable to close directory : %s", strerror(errno));
-        }
-        return NULL;
-      }
-      open_fd_map[fd] = info;
-    }
-
-    if (closedir(d) == -1) {
-      ALOGE("Unable to close directory : %s", strerror(errno));
-      return NULL;
-    }
-    return new FileDescriptorTable(open_fd_map);
-  }
-
-  bool Restat() {
-    std::set<int> open_fds;
-
-    // First get the list of open descriptors.
-    DIR* d = opendir(kFdPath);
-    if (d == NULL) {
-      ALOGE("Unable to open directory %s: %s", kFdPath, strerror(errno));
-      return false;
-    }
-
-    int dir_fd = dirfd(d);
-    dirent* e;
-    while ((e = readdir(d)) != NULL) {
-      const int fd = ParseFd(e, dir_fd);
-      if (fd == -1) {
-        continue;
-      }
-
-      open_fds.insert(fd);
-    }
-
-    if (closedir(d) == -1) {
-      ALOGE("Unable to close directory : %s", strerror(errno));
-      return false;
-    }
-
-    return RestatInternal(open_fds);
-  }
-
-  // Reopens all file descriptors that are contained in the table. Returns true
-  // if all descriptors were successfully re-opened or detached, and false if an
-  // error occurred.
-  bool ReopenOrDetach() {
-    std::unordered_map<int, FileDescriptorInfo*>::const_iterator it;
-    for (it = open_fd_map_.begin(); it != open_fd_map_.end(); ++it) {
-      const FileDescriptorInfo* info = it->second;
-      if (info == NULL || !info->ReopenOrDetach()) {
-        return false;
-      }
-    }
-
-    return true;
-  }
-
- private:
-  FileDescriptorTable(const std::unordered_map<int, FileDescriptorInfo*>& map)
-      : open_fd_map_(map) {
-  }
-
-  bool RestatInternal(std::set<int>& open_fds) {
-    bool error = false;
-
-    // Iterate through the list of file descriptors we've already recorded
-    // and check whether :
-    //
-    // (a) they continue to be open.
-    // (b) they refer to the same file.
-    std::unordered_map<int, FileDescriptorInfo*>::iterator it = open_fd_map_.begin();
-    while (it != open_fd_map_.end()) {
-      std::set<int>::const_iterator element = open_fds.find(it->first);
-      if (element == open_fds.end()) {
-        // The entry from the file descriptor table is no longer in the list
-        // of open files. We warn about this condition and remove it from
-        // the list of FDs under consideration.
-        //
-        // TODO(narayan): This will be an error in a future android release.
-        // error = true;
-        // ALOGW("Zygote closed file descriptor %d.", it->first);
-        it = open_fd_map_.erase(it);
-      } else {
-        // The entry from the file descriptor table is still open. Restat
-        // it and check whether it refers to the same file.
-        const bool same_file = it->second->Restat();
-        if (!same_file) {
-          // The file descriptor refers to a different description. We must
-          // update our entry in the table.
-          delete it->second;
-          it->second = FileDescriptorInfo::createFromFd(*element);
-          if (it->second == NULL) {
-            // The descriptor no longer no longer refers to a whitelisted file.
-            // We flag an error and remove it from the list of files we're
-            // tracking.
-            error = true;
-            it = open_fd_map_.erase(it);
-          } else {
-            // Successfully restatted the file, move on to the next open FD.
-            ++it;
-          }
-        } else {
-          // It's the same file. Nothing to do here. Move on to the next open
-          // FD.
-          ++it;
-        }
-
-        // Finally, remove the FD from the set of open_fds. We do this last because
-        // |element| will not remain valid after a call to erase.
-        open_fds.erase(element);
-      }
-    }
-
-    if (open_fds.size() > 0) {
-      // The zygote has opened new file descriptors since our last inspection.
-      // We warn about this condition and add them to our table.
-      //
-      // TODO(narayan): This will be an error in a future android release.
-      // error = true;
-      // ALOGW("Zygote opened %zd new file descriptor(s).", open_fds.size());
-
-      // TODO(narayan): This code will be removed in a future android release.
-      std::set<int>::const_iterator it;
-      for (it = open_fds.begin(); it != open_fds.end(); ++it) {
-        const int fd = (*it);
-        FileDescriptorInfo* info = FileDescriptorInfo::createFromFd(fd);
-        if (info == NULL) {
-          // A newly opened file is not on the whitelist. Flag an error and
-          // continue.
-          error = true;
-        } else {
-          // Track the newly opened file.
-          open_fd_map_[fd] = info;
-        }
-      }
-    }
-
-    return !error;
-  }
-
-  static int ParseFd(dirent* e, int dir_fd) {
-    char* end;
-    const int fd = strtol(e->d_name, &end, 10);
-    if ((*end) != '\0') {
-      return -1;
-    }
-
-    // Don't bother with the standard input/output/error, they're handled
-    // specially post-fork anyway.
-    if (fd <= STDERR_FILENO || fd == dir_fd) {
-      return -1;
-    }
-
-    return fd;
-  }
-
-  // Invariant: All values in this unordered_map are non-NULL.
-  std::unordered_map<int, FileDescriptorInfo*> open_fd_map_;
-
-  DISALLOW_COPY_AND_ASSIGN(FileDescriptorTable);
-};
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
new file mode 100644
index 0000000..969d336f3
--- /dev/null
+++ b/core/jni/fd_utils.cpp
@@ -0,0 +1,559 @@
+/*
+ * Copyright (C) 2016 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 "fd_utils.h"
+
+#include <algorithm>
+
+#include <fcntl.h>
+#include <grp.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <android-base/strings.h>
+#include <cutils/log.h>
+
+// Static whitelist of open paths that the zygote is allowed to keep open.
+static const char* kPathWhitelist[] = {
+  "/dev/null",
+  "/dev/socket/zygote",
+  "/dev/socket/zygote_secondary",
+  "/dev/socket/webview_zygote",
+  "/sys/kernel/debug/tracing/trace_marker",
+  "/system/framework/framework-res.apk",
+  "/dev/urandom",
+  "/dev/ion",
+  "/dev/dri/renderD129", // Fixes b/31172436
+};
+
+static const char kFdPath[] = "/proc/self/fd";
+
+// static
+FileDescriptorWhitelist* FileDescriptorWhitelist::Get() {
+  if (instance_ == nullptr) {
+    instance_ = new FileDescriptorWhitelist();
+  }
+  return instance_;
+}
+
+bool FileDescriptorWhitelist::IsAllowed(const std::string& path) const {
+  // Check the static whitelist path.
+  for (const auto& whitelist_path : kPathWhitelist) {
+    if (path == whitelist_path)
+      return true;
+  }
+
+  // Check any paths added to the dynamic whitelist.
+  for (const auto& whitelist_path : whitelist_) {
+    if (path == whitelist_path)
+      return true;
+  }
+
+  static const std::string kFrameworksPrefix = "/system/framework/";
+  static const std::string kJarSuffix = ".jar";
+  if (StartsWith(path, kFrameworksPrefix) && EndsWith(path, kJarSuffix)) {
+    return true;
+  }
+
+  // Whitelist files needed for Runtime Resource Overlay, like these:
+  // /system/vendor/overlay/framework-res.apk
+  // /system/vendor/overlay-subdir/pg/framework-res.apk
+  // /vendor/overlay/framework-res.apk
+  // /vendor/overlay/PG/android-framework-runtime-resource-overlay.apk
+  // /data/resource-cache/system@vendor@overlay@framework-res.apk@idmap
+  // /data/resource-cache/system@vendor@overlay-subdir@pg@framework-res.apk@idmap
+  // See AssetManager.cpp for more details on overlay-subdir.
+  static const std::string kOverlayDir = "/system/vendor/overlay/";
+  static const std::string kVendorOverlayDir = "/vendor/overlay";
+  static const std::string kOverlaySubdir = "/system/vendor/overlay-subdir/";
+  static const std::string kApkSuffix = ".apk";
+
+  if ((StartsWith(path, kOverlayDir) || StartsWith(path, kOverlaySubdir)
+       || StartsWith(path, kVendorOverlayDir))
+      && EndsWith(path, kApkSuffix)
+      && path.find("/../") == std::string::npos) {
+    return true;
+  }
+
+  static const std::string kOverlayIdmapPrefix = "/data/resource-cache/";
+  static const std::string kOverlayIdmapSuffix = ".apk@idmap";
+  if (StartsWith(path, kOverlayIdmapPrefix) && EndsWith(path, kOverlayIdmapSuffix)
+      && path.find("/../") == std::string::npos) {
+    return true;
+  }
+
+  // All regular files that are placed under this path are whitelisted automatically.
+  static const std::string kZygoteWhitelistPath = "/vendor/zygote_whitelist/";
+  if (StartsWith(path, kZygoteWhitelistPath) && path.find("/../") == std::string::npos) {
+    return true;
+  }
+
+  return false;
+}
+
+FileDescriptorWhitelist::FileDescriptorWhitelist()
+    : whitelist_() {
+}
+
+// TODO: Call android::base::StartsWith instead of copying the code here.
+// static
+bool FileDescriptorWhitelist::StartsWith(const std::string& str,
+                                         const std::string& prefix) {
+  return str.compare(0, prefix.size(), prefix) == 0;
+}
+
+// TODO: Call android::base::EndsWith instead of copying the code here.
+// static
+bool FileDescriptorWhitelist::EndsWith(const std::string& str,
+                                       const std::string& suffix) {
+  if (suffix.size() > str.size()) {
+    return false;
+  }
+
+  return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
+}
+
+FileDescriptorWhitelist* FileDescriptorWhitelist::instance_ = nullptr;
+
+// static
+FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd) {
+  struct stat f_stat;
+  // This should never happen; the zygote should always have the right set
+  // of permissions required to stat all its open files.
+  if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
+    ALOGE("Unable to stat fd %d : %s", fd, strerror(errno));
+    return NULL;
+  }
+
+  const FileDescriptorWhitelist* whitelist = FileDescriptorWhitelist::Get();
+
+  if (S_ISSOCK(f_stat.st_mode)) {
+    std::string socket_name;
+    if (!GetSocketName(fd, &socket_name)) {
+      return NULL;
+    }
+
+    if (!whitelist->IsAllowed(socket_name)) {
+      ALOGE("Socket name not whitelisted : %s (fd=%d)", socket_name.c_str(), fd);
+      return NULL;
+    }
+
+    return new FileDescriptorInfo(fd);
+  }
+
+  // We only handle whitelisted regular files and character devices. Whitelisted
+  // character devices must provide a guarantee of sensible behaviour when
+  // reopened.
+  //
+  // S_ISDIR : Not supported. (We could if we wanted to, but it's unused).
+  // S_ISLINK : Not supported.
+  // S_ISBLK : Not supported.
+  // S_ISFIFO : Not supported. Note that the zygote uses pipes to communicate
+  // with the child process across forks but those should have been closed
+  // before we got to this point.
+  if (!S_ISCHR(f_stat.st_mode) && !S_ISREG(f_stat.st_mode)) {
+    ALOGE("Unsupported st_mode %d", f_stat.st_mode);
+    return NULL;
+  }
+
+  std::string file_path;
+  if (!Readlink(fd, &file_path)) {
+    return NULL;
+  }
+
+  if (!whitelist->IsAllowed(file_path)) {
+    ALOGE("Not whitelisted : %s", file_path.c_str());
+    return NULL;
+  }
+
+  // File descriptor flags : currently on FD_CLOEXEC. We can set these
+  // using F_SETFD - we're single threaded at this point of execution so
+  // there won't be any races.
+  const int fd_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD));
+  if (fd_flags == -1) {
+    ALOGE("Failed fcntl(%d, F_GETFD) : %s", fd, strerror(errno));
+    return NULL;
+  }
+
+  // File status flags :
+  // - File access mode : (O_RDONLY, O_WRONLY...) we'll pass these through
+  //   to the open() call.
+  //
+  // - File creation flags : (O_CREAT, O_EXCL...) - there's not much we can
+  //   do about these, since the file has already been created. We shall ignore
+  //   them here.
+  //
+  // - Other flags : We'll have to set these via F_SETFL. On linux, F_SETFL
+  //   can only set O_APPEND, O_ASYNC, O_DIRECT, O_NOATIME, and O_NONBLOCK.
+  //   In particular, it can't set O_SYNC and O_DSYNC. We'll have to test for
+  //   their presence and pass them in to open().
+  int fs_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL));
+  if (fs_flags == -1) {
+    ALOGE("Failed fcntl(%d, F_GETFL) : %s", fd, strerror(errno));
+    return NULL;
+  }
+
+  // File offset : Ignore the offset for non seekable files.
+  const off_t offset = TEMP_FAILURE_RETRY(lseek64(fd, 0, SEEK_CUR));
+
+  // We pass the flags that open accepts to open, and use F_SETFL for
+  // the rest of them.
+  static const int kOpenFlags = (O_RDONLY | O_WRONLY | O_RDWR | O_DSYNC | O_SYNC);
+  int open_flags = fs_flags & (kOpenFlags);
+  fs_flags = fs_flags & (~(kOpenFlags));
+
+  return new FileDescriptorInfo(f_stat, file_path, fd, open_flags, fd_flags, fs_flags, offset);
+}
+
+bool FileDescriptorInfo::Restat() const {
+  struct stat f_stat;
+  if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
+    return false;
+  }
+
+  return f_stat.st_ino == stat.st_ino && f_stat.st_dev == stat.st_dev;
+}
+
+bool FileDescriptorInfo::ReopenOrDetach() const {
+  if (is_sock) {
+    return DetachSocket();
+  }
+
+  // NOTE: This might happen if the file was unlinked after being opened.
+  // It's a common pattern in the case of temporary files and the like but
+  // we should not allow such usage from the zygote.
+  const int new_fd = TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags));
+
+  if (new_fd == -1) {
+    ALOGE("Failed open(%s, %d) : %s", file_path.c_str(), open_flags, strerror(errno));
+    return false;
+  }
+
+  if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFD, fd_flags)) == -1) {
+    close(new_fd);
+    ALOGE("Failed fcntl(%d, F_SETFD, %x) : %s", new_fd, fd_flags, strerror(errno));
+    return false;
+  }
+
+  if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFL, fs_flags)) == -1) {
+    close(new_fd);
+    ALOGE("Failed fcntl(%d, F_SETFL, %x) : %s", new_fd, fs_flags, strerror(errno));
+    return false;
+  }
+
+  if (offset != -1 && TEMP_FAILURE_RETRY(lseek64(new_fd, offset, SEEK_SET)) == -1) {
+    close(new_fd);
+    ALOGE("Failed lseek64(%d, SEEK_SET) : %s", new_fd, strerror(errno));
+    return false;
+  }
+
+  if (TEMP_FAILURE_RETRY(dup2(new_fd, fd)) == -1) {
+    close(new_fd);
+    ALOGE("Failed dup2(%d, %d) : %s", fd, new_fd, strerror(errno));
+    return false;
+  }
+
+  close(new_fd);
+
+  return true;
+}
+
+FileDescriptorInfo::FileDescriptorInfo(int fd) :
+  fd(fd),
+  stat(),
+  open_flags(0),
+  fd_flags(0),
+  fs_flags(0),
+  offset(0),
+  is_sock(true) {
+}
+
+FileDescriptorInfo::FileDescriptorInfo(struct stat stat, const std::string& file_path,
+                                       int fd, int open_flags, int fd_flags, int fs_flags,
+                                       off_t offset) :
+  fd(fd),
+  stat(stat),
+  file_path(file_path),
+  open_flags(open_flags),
+  fd_flags(fd_flags),
+  fs_flags(fs_flags),
+  offset(offset),
+  is_sock(false) {
+}
+
+// TODO: Call android::base::Readlink instead of copying the code here.
+// static
+bool FileDescriptorInfo::Readlink(const int fd, std::string* result) {
+  char path[64];
+  snprintf(path, sizeof(path), "/proc/self/fd/%d", fd);
+
+  // Code copied from android::base::Readlink starts here :
+
+  // Annoyingly, the readlink system call returns EINVAL for a zero-sized buffer,
+  // and truncates to whatever size you do supply, so it can't be used to query.
+  // We could call lstat first, but that would introduce a race condition that
+  // we couldn't detect.
+  // ext2 and ext4 both have PAGE_SIZE limitations, so we assume that here.
+  char buf[4096];
+  ssize_t len = readlink(path, buf, sizeof(buf));
+  if (len == -1) return false;
+
+  result->assign(buf, len);
+  return true;
+}
+
+// static
+bool FileDescriptorInfo::GetSocketName(const int fd, std::string* result) {
+  sockaddr_storage ss;
+  sockaddr* addr = reinterpret_cast<sockaddr*>(&ss);
+  socklen_t addr_len = sizeof(ss);
+
+  if (TEMP_FAILURE_RETRY(getsockname(fd, addr, &addr_len)) == -1) {
+    ALOGE("Failed getsockname(%d) : %s", fd, strerror(errno));
+    return false;
+  }
+
+  if (addr->sa_family != AF_UNIX) {
+    ALOGE("Unsupported socket (fd=%d) with family %d", fd, addr->sa_family);
+    return false;
+  }
+
+  const sockaddr_un* unix_addr = reinterpret_cast<const sockaddr_un*>(&ss);
+
+  size_t path_len = addr_len - offsetof(struct sockaddr_un, sun_path);
+  // This is an unnamed local socket, we do not accept it.
+  if (path_len == 0) {
+    ALOGE("Unsupported AF_UNIX socket (fd=%d) with empty path.", fd);
+    return false;
+  }
+
+  // This is a local socket with an abstract address, we do not accept it.
+  if (unix_addr->sun_path[0] == '\0') {
+    ALOGE("Unsupported AF_UNIX socket (fd=%d) with abstract address.", fd);
+    return false;
+  }
+
+  // If we're here, sun_path must refer to a null terminated filesystem
+  // pathname (man 7 unix). Remove the terminator before assigning it to an
+  // std::string.
+  if (unix_addr->sun_path[path_len - 1] ==  '\0') {
+    --path_len;
+  }
+
+  result->assign(unix_addr->sun_path, path_len);
+  return true;
+}
+
+bool FileDescriptorInfo::DetachSocket() const {
+  const int dev_null_fd = open("/dev/null", O_RDWR);
+  if (dev_null_fd < 0) {
+    ALOGE("Failed to open /dev/null : %s", strerror(errno));
+    return false;
+  }
+
+  if (dup2(dev_null_fd, fd) == -1) {
+    ALOGE("Failed dup2 on socket descriptor %d : %s", fd, strerror(errno));
+    return false;
+  }
+
+  if (close(dev_null_fd) == -1) {
+    ALOGE("Failed close(%d) : %s", dev_null_fd, strerror(errno));
+    return false;
+  }
+
+  return true;
+}
+
+// static
+FileDescriptorTable* FileDescriptorTable::Create() {
+  DIR* d = opendir(kFdPath);
+  if (d == NULL) {
+    ALOGE("Unable to open directory %s: %s", kFdPath, strerror(errno));
+    return NULL;
+  }
+  int dir_fd = dirfd(d);
+  dirent* e;
+
+  std::unordered_map<int, FileDescriptorInfo*> open_fd_map;
+  while ((e = readdir(d)) != NULL) {
+    const int fd = ParseFd(e, dir_fd);
+    if (fd == -1) {
+      continue;
+    }
+
+    FileDescriptorInfo* info = FileDescriptorInfo::CreateFromFd(fd);
+    if (info == NULL) {
+      if (closedir(d) == -1) {
+        ALOGE("Unable to close directory : %s", strerror(errno));
+      }
+      return NULL;
+    }
+    open_fd_map[fd] = info;
+  }
+
+  if (closedir(d) == -1) {
+    ALOGE("Unable to close directory : %s", strerror(errno));
+    return NULL;
+  }
+  return new FileDescriptorTable(open_fd_map);
+}
+
+bool FileDescriptorTable::Restat() {
+  std::set<int> open_fds;
+
+  // First get the list of open descriptors.
+  DIR* d = opendir(kFdPath);
+  if (d == NULL) {
+    ALOGE("Unable to open directory %s: %s", kFdPath, strerror(errno));
+    return false;
+  }
+
+  int dir_fd = dirfd(d);
+  dirent* e;
+  while ((e = readdir(d)) != NULL) {
+    const int fd = ParseFd(e, dir_fd);
+    if (fd == -1) {
+      continue;
+    }
+
+    open_fds.insert(fd);
+  }
+
+  if (closedir(d) == -1) {
+    ALOGE("Unable to close directory : %s", strerror(errno));
+    return false;
+  }
+
+  return RestatInternal(open_fds);
+}
+
+// Reopens all file descriptors that are contained in the table. Returns true
+// if all descriptors were successfully re-opened or detached, and false if an
+// error occurred.
+bool FileDescriptorTable::ReopenOrDetach() {
+  std::unordered_map<int, FileDescriptorInfo*>::const_iterator it;
+  for (it = open_fd_map_.begin(); it != open_fd_map_.end(); ++it) {
+    const FileDescriptorInfo* info = it->second;
+    if (info == NULL || !info->ReopenOrDetach()) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+FileDescriptorTable::FileDescriptorTable(
+    const std::unordered_map<int, FileDescriptorInfo*>& map)
+    : open_fd_map_(map) {
+}
+
+bool FileDescriptorTable::RestatInternal(std::set<int>& open_fds) {
+  bool error = false;
+
+  // Iterate through the list of file descriptors we've already recorded
+  // and check whether :
+  //
+  // (a) they continue to be open.
+  // (b) they refer to the same file.
+  std::unordered_map<int, FileDescriptorInfo*>::iterator it = open_fd_map_.begin();
+  while (it != open_fd_map_.end()) {
+    std::set<int>::const_iterator element = open_fds.find(it->first);
+    if (element == open_fds.end()) {
+      // The entry from the file descriptor table is no longer in the list
+      // of open files. We warn about this condition and remove it from
+      // the list of FDs under consideration.
+      //
+      // TODO(narayan): This will be an error in a future android release.
+      // error = true;
+      // ALOGW("Zygote closed file descriptor %d.", it->first);
+      it = open_fd_map_.erase(it);
+    } else {
+      // The entry from the file descriptor table is still open. Restat
+      // it and check whether it refers to the same file.
+      const bool same_file = it->second->Restat();
+      if (!same_file) {
+        // The file descriptor refers to a different description. We must
+        // update our entry in the table.
+        delete it->second;
+        it->second = FileDescriptorInfo::CreateFromFd(*element);
+        if (it->second == NULL) {
+          // The descriptor no longer no longer refers to a whitelisted file.
+          // We flag an error and remove it from the list of files we're
+          // tracking.
+          error = true;
+          it = open_fd_map_.erase(it);
+        } else {
+          // Successfully restatted the file, move on to the next open FD.
+          ++it;
+        }
+      } else {
+        // It's the same file. Nothing to do here. Move on to the next open
+        // FD.
+        ++it;
+      }
+
+      // Finally, remove the FD from the set of open_fds. We do this last because
+      // |element| will not remain valid after a call to erase.
+      open_fds.erase(element);
+    }
+  }
+
+  if (open_fds.size() > 0) {
+    // The zygote has opened new file descriptors since our last inspection.
+    // We warn about this condition and add them to our table.
+    //
+    // TODO(narayan): This will be an error in a future android release.
+    // error = true;
+    // ALOGW("Zygote opened %zd new file descriptor(s).", open_fds.size());
+
+    // TODO(narayan): This code will be removed in a future android release.
+    std::set<int>::const_iterator it;
+    for (it = open_fds.begin(); it != open_fds.end(); ++it) {
+      const int fd = (*it);
+      FileDescriptorInfo* info = FileDescriptorInfo::CreateFromFd(fd);
+      if (info == NULL) {
+        // A newly opened file is not on the whitelist. Flag an error and
+        // continue.
+        error = true;
+      } else {
+        // Track the newly opened file.
+        open_fd_map_[fd] = info;
+      }
+    }
+  }
+
+  return !error;
+}
+
+// static
+int FileDescriptorTable::ParseFd(dirent* e, int dir_fd) {
+  char* end;
+  const int fd = strtol(e->d_name, &end, 10);
+  if ((*end) != '\0') {
+    return -1;
+  }
+
+  // Don't bother with the standard input/output/error, they're handled
+  // specially post-fork anyway.
+  if (fd <= STDERR_FILENO || fd == dir_fd) {
+    return -1;
+  }
+
+  return fd;
+}
diff --git a/core/jni/fd_utils.h b/core/jni/fd_utils.h
new file mode 100644
index 0000000..9e3afd9
--- /dev/null
+++ b/core/jni/fd_utils.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRAMEWORKS_BASE_CORE_JNI_FD_UTILS_H_
+#define FRAMEWORKS_BASE_CORE_JNI_FD_UTILS_H_
+
+#include <set>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <dirent.h>
+#include <inttypes.h>
+#include <sys/stat.h>
+
+#include <android-base/macros.h>
+
+// Whitelist of open paths that the zygote is allowed to keep open.
+//
+// In addition to the paths listed in kPathWhitelist in file_utils.cpp, and
+// paths dynamically added with Allow(), all files ending with ".jar"
+// under /system/framework" are whitelisted. See IsAllowed() for the canonical
+// definition.
+//
+// If the whitelisted path is associated with a regular file or a
+// character device, the file is reopened after a fork with the same
+// offset and mode. If the whilelisted  path is associated with a
+// AF_UNIX socket, the socket will refer to /dev/null after each
+// fork, and all operations on it will fail.
+class FileDescriptorWhitelist {
+ public:
+  // Lazily creates the global whitelist.
+  static FileDescriptorWhitelist* Get();
+
+  // Adds a path to the whitelist.
+  void Allow(const std::string& path) {
+    whitelist_.push_back(path);
+  }
+
+  // Returns true iff. a given path is whitelisted. A path is whitelisted
+  // if it belongs to the whitelist (see kPathWhitelist) or if it's a path
+  // under /system/framework that ends with ".jar" or if it is a system
+  // framework overlay.
+  bool IsAllowed(const std::string& path) const;
+
+ private:
+  FileDescriptorWhitelist();
+
+  static bool StartsWith(const std::string& str, const std::string& prefix);
+
+  static bool EndsWith(const std::string& str, const std::string& suffix);
+
+  static FileDescriptorWhitelist* instance_;
+
+  std::vector<std::string> whitelist_;
+
+  DISALLOW_COPY_AND_ASSIGN(FileDescriptorWhitelist);
+};
+
+// Keeps track of all relevant information (flags, offset etc.) of an
+// open zygote file descriptor.
+class FileDescriptorInfo {
+ public:
+  // Create a FileDescriptorInfo for a given file descriptor. Returns
+  // |NULL| if an error occurred.
+  static FileDescriptorInfo* CreateFromFd(int fd);
+
+  // Checks whether the file descriptor associated with this object
+  // refers to the same description.
+  bool Restat() const;
+
+  bool ReopenOrDetach() const;
+
+  const int fd;
+  const struct stat stat;
+  const std::string file_path;
+  const int open_flags;
+  const int fd_flags;
+  const int fs_flags;
+  const off_t offset;
+  const bool is_sock;
+
+ private:
+  FileDescriptorInfo(int fd);
+
+  FileDescriptorInfo(struct stat stat, const std::string& file_path, int fd, int open_flags,
+                     int fd_flags, int fs_flags, off_t offset);
+
+  static bool Readlink(const int fd, std::string* result);
+
+  // Returns the locally-bound name of the socket |fd|. Returns true
+  // iff. all of the following hold :
+  //
+  // - the socket's sa_family is AF_UNIX.
+  // - the length of the path is greater than zero (i.e, not an unnamed socket).
+  // - the first byte of the path isn't zero (i.e, not a socket with an abstract
+  //   address).
+  static bool GetSocketName(const int fd, std::string* result);
+
+  bool DetachSocket() const;
+
+  DISALLOW_COPY_AND_ASSIGN(FileDescriptorInfo);
+};
+
+// A FileDescriptorTable is a collection of FileDescriptorInfo objects
+// keyed by their FDs.
+class FileDescriptorTable {
+ public:
+  // Creates a new FileDescriptorTable. This function scans
+  // /proc/self/fd for the list of open file descriptors and collects
+  // information about them. Returns NULL if an error occurs.
+  static FileDescriptorTable* Create();
+
+  bool Restat();
+
+  // Reopens all file descriptors that are contained in the table. Returns true
+  // if all descriptors were successfully re-opened or detached, and false if an
+  // error occurred.
+  bool ReopenOrDetach();
+
+ private:
+  FileDescriptorTable(const std::unordered_map<int, FileDescriptorInfo*>& map);
+
+  bool RestatInternal(std::set<int>& open_fds);
+
+  static int ParseFd(dirent* e, int dir_fd);
+
+  // Invariant: All values in this unordered_map are non-NULL.
+  std::unordered_map<int, FileDescriptorInfo*> open_fd_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(FileDescriptorTable);
+};
+
+#endif  // FRAMEWORKS_BASE_CORE_JNI_FD_UTILS_H_
diff --git a/include/android_runtime/AndroidRuntime.h b/core/jni/include/android_runtime/AndroidRuntime.h
similarity index 98%
rename from include/android_runtime/AndroidRuntime.h
rename to core/jni/include/android_runtime/AndroidRuntime.h
index ed77d9a..c2189d4 100644
--- a/include/android_runtime/AndroidRuntime.h
+++ b/core/jni/include/android_runtime/AndroidRuntime.h
@@ -98,7 +98,7 @@
      * Called when the Java application exits to perform additional cleanup actions
      * before the process is terminated.
      */
-    virtual void onExit(int code) { }
+    virtual void onExit(int /*code*/) { }
 
     /** create a new thread that is visible from Java */
     static android_thread_id_t createJavaThread(const char* name, void (*start)(void *),
diff --git a/include/android_runtime/Log.h b/core/jni/include/android_runtime/Log.h
similarity index 100%
rename from include/android_runtime/Log.h
rename to core/jni/include/android_runtime/Log.h
diff --git a/include/android_runtime/android_app_NativeActivity.h b/core/jni/include/android_runtime/android_app_NativeActivity.h
similarity index 100%
rename from include/android_runtime/android_app_NativeActivity.h
rename to core/jni/include/android_runtime/android_app_NativeActivity.h
diff --git a/include/android_runtime/android_content_res_Configuration.h b/core/jni/include/android_runtime/android_content_res_Configuration.h
similarity index 100%
rename from include/android_runtime/android_content_res_Configuration.h
rename to core/jni/include/android_runtime/android_content_res_Configuration.h
diff --git a/include/android_runtime/android_graphics_SurfaceTexture.h b/core/jni/include/android_runtime/android_graphics_SurfaceTexture.h
similarity index 100%
rename from include/android_runtime/android_graphics_SurfaceTexture.h
rename to core/jni/include/android_runtime/android_graphics_SurfaceTexture.h
diff --git a/include/android_runtime/android_hardware_camera2_CameraMetadata.h b/core/jni/include/android_runtime/android_hardware_camera2_CameraMetadata.h
similarity index 100%
rename from include/android_runtime/android_hardware_camera2_CameraMetadata.h
rename to core/jni/include/android_runtime/android_hardware_camera2_CameraMetadata.h
diff --git a/include/android_runtime/android_util_AssetManager.h b/core/jni/include/android_runtime/android_util_AssetManager.h
similarity index 100%
rename from include/android_runtime/android_util_AssetManager.h
rename to core/jni/include/android_runtime/android_util_AssetManager.h
diff --git a/include/android_runtime/android_view_InputQueue.h b/core/jni/include/android_runtime/android_view_InputQueue.h
similarity index 100%
rename from include/android_runtime/android_view_InputQueue.h
rename to core/jni/include/android_runtime/android_view_InputQueue.h
diff --git a/include/android_runtime/android_view_Surface.h b/core/jni/include/android_runtime/android_view_Surface.h
similarity index 100%
rename from include/android_runtime/android_view_Surface.h
rename to core/jni/include/android_runtime/android_view_Surface.h
diff --git a/include/android_runtime/android_view_SurfaceSession.h b/core/jni/include/android_runtime/android_view_SurfaceSession.h
similarity index 100%
rename from include/android_runtime/android_view_SurfaceSession.h
rename to core/jni/include/android_runtime/android_view_SurfaceSession.h
diff --git a/core/proto/android/content/component_name.proto b/core/proto/android/content/component_name.proto
new file mode 100644
index 0000000..7908af9
--- /dev/null
+++ b/core/proto/android/content/component_name.proto
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2006 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.
+ */
+
+syntax = "proto2";
+
+option java_package = "android.content";
+option java_multiple_files = true;
+
+package android.content;
+
+/**
+ * An android.content.ComponentName object.
+ */
+message ComponentNameProto {
+    optional string package_name = 1;
+    optional string class_name = 2;
+}
+
diff --git a/core/proto/android/content/configuration.proto b/core/proto/android/content/configuration.proto
new file mode 100644
index 0000000..683e7aac
--- /dev/null
+++ b/core/proto/android/content/configuration.proto
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2006 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.
+ */
+
+syntax = "proto2";
+
+option java_package = "android.content";
+option java_multiple_files = true;
+
+package android.content;
+
+import "frameworks/base/core/proto/android/content/locale.proto";
+
+/**
+ * An android resource configuration.
+ */
+message ConfigurationProto {
+  optional float font_scale = 1;
+  optional uint32 mcc = 2;
+  optional uint32 mnc = 3;
+  repeated LocaleProto locales = 4;
+  optional uint32 screen_layout = 5;
+  optional uint32 touchscreen = 6;
+  optional uint32 keyboard_hidden = 7;
+  optional uint32 hard_keyboard_hidden = 8;
+  optional uint32 navigation = 9;
+  optional uint32 navigation_hidden = 10;
+  optional uint32 orientation = 11;
+  optional uint32 ui_mode = 12;
+  optional uint32 screen_width_dp = 13;
+  optional uint32 screen_height_dp = 14;
+  optional uint32 smallest_screen_width_dp = 15;
+  optional uint32 density_dpi = 16;
+}
+
diff --git a/core/proto/android/content/locale.proto b/core/proto/android/content/locale.proto
new file mode 100644
index 0000000..55ce68e
--- /dev/null
+++ b/core/proto/android/content/locale.proto
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+syntax = "proto2";
+
+option java_package = "android.content";
+option java_multiple_files = true;
+
+package android.content;
+
+message LocaleProto {
+  optional string language = 1;
+  optional string country = 2;
+  optional string variant = 3;
+}
+
diff --git a/core/proto/android/os/incident_proto.proto b/core/proto/android/os/incident_proto.proto
new file mode 100644
index 0000000..1708b81
--- /dev/null
+++ b/core/proto/android/os/incident_proto.proto
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+syntax = "proto2";
+
+option java_package = "android.os";
+option java_multiple_files = true;
+
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+import "frameworks/base/core/proto/android/service/fingerprint_proto.proto";
+
+package android.os;
+
+message IncidentHeaderProto {
+    enum Cause {
+        CAUSE_UNKNOWN = 0;
+        CAUSE_USER = 1;
+        CAUSE_ANR = 2;
+        CAUSE_CRASH = 3;
+    }
+
+    optional Cause cause = 1;
+}
+
+message IncidentProto {
+    // Incident header
+    repeated IncidentHeaderProto header = 1;
+
+    // Device information
+    //optional SystemProperties system_properties = 1000;
+
+    // Linux services
+    //optional Procrank procrank = 2000;
+    //optional PageTypeInfo page_type_info = 2001;
+    //optional KernelWakeSources kernel_wake_sources = 2002;
+
+    // System Services
+    optional android.service.fingerprint.FingerprintServiceDumpProto fingerprint = 3000;
+}
diff --git a/core/proto/android/service/fingerprint_proto.proto b/core/proto/android/service/fingerprint_proto.proto
new file mode 100644
index 0000000..b2c5000
--- /dev/null
+++ b/core/proto/android/service/fingerprint_proto.proto
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+syntax = "proto2";
+
+package android.service.fingerprint;
+
+option java_multiple_files = true;
+
+message FingerprintServiceDumpProto {
+    // Each log may include multiple tuples of (user_id, num_fingerprints).
+    repeated FingerprintUserStatsProto users = 1;
+}
+
+message FingerprintUserStatsProto {
+    // Should be 0, 10, 11, 12, etc. where 0 is the owner.
+    optional int32 user_id = 1;
+
+    // The number of fingerprints registered to this user.
+    optional int32 num_fingerprints = 2;
+
+    // Normal fingerprint authentications (e.g. lockscreen).
+    optional FingerprintActionStatsProto normal = 3;
+
+    // Crypto authentications (e.g. to unlock password storage, make secure
+    // purchases, etc).
+    optional FingerprintActionStatsProto crypto = 4;
+}
+
+message FingerprintActionStatsProto {
+    // Number of accepted fingerprints.
+    optional int32 accept = 1;
+
+    // Number of rejected fingerprints.
+    optional int32 reject = 2;
+
+    // Total number of acquisitions. Should be >= accept+reject due to poor
+    // image acquisition in some cases (too fast, too slow, dirty sensor, etc.)
+    optional int32 acquire = 3;
+
+    // Total number of lockouts.
+    optional int32 lockout = 4;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 40fd881..bc04062 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3393,7 +3393,10 @@
             android:permission="android.permission.MASTER_CLEAR">
             <intent-filter
                     android:priority="100" >
-                <!-- For Checkin, Settings, etc.: action=MASTER_CLEAR -->
+                <!-- For Checkin, Settings, etc.: action=FACTORY_RESET -->
+                <action android:name="android.intent.action.FACTORY_RESET" />
+                <!-- As above until all the references to the deprecated MASTER_CLEAR get updated to
+                     FACTORY_RESET. -->
                 <action android:name="android.intent.action.MASTER_CLEAR" />
 
                 <!-- MCS always uses REMOTE_INTENT: category=MASTER_CLEAR -->
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 4af7e4a..b640d4d 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Tot <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Tot <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (volgende wekker)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Totdat jy dit afskakel"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Totdat jy Moenie steur nie afskakel"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Vou in"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 9731f27..7786c94 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"እስከ <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ድረስ"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"እስከ <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (ቀጣይ ማንቂያ)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"ይህን እስኪያጠፉት ድረስ"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"አትረብሽን እስኪያጠፉ ድረስ"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"ሰብስብ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index feb9c85..8a02fad 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1754,7 +1754,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"حتى <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"حتى <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (التنبيه التالي)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"لحين تعطيل هذا الإعداد"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"حتى يتم تعطيل \"الرجاء عدم الإزعاج\""</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"تصغير"</string>
diff --git a/core/res/res/values-az-rAZ/strings.xml b/core/res/res/values-az-rAZ/strings.xml
index b3094e1..2c55b7e 100644
--- a/core/res/res/values-az-rAZ/strings.xml
+++ b/core/res/res/values-az-rAZ/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Saat <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> qədər"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> radəsinə qədər (növbəti siqnal)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Bunu söndürənə kimi"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"\"Narahat etməyin\" seçiminini deaktiv edənə kimi"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Dağıt"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 588d3ba..48fefd2 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1649,7 +1649,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (sledeći alarm)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Dok ne isključite"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Dok ne isključite režim Ne uznemiravaj"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Skupi"</string>
diff --git a/core/res/res/values-be-rBY/strings.xml b/core/res/res/values-be-rBY/strings.xml
index e5b0a5c..adeeef3 100644
--- a/core/res/res/values-be-rBY/strings.xml
+++ b/core/res/res/values-be-rBY/strings.xml
@@ -1684,7 +1684,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Да <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Да <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (наступны будзільнік)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Пакуль вы не выключыце гэта"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Пакуль вы не выключыце рэжым «Не турбаваць»"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Згарнуць"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index f0acf39..3359a18 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"До следващия будилник (<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Докато не изключите това"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Докато не изключите „Не безпокойте“"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Свиване"</string>
diff --git a/core/res/res/values-bn-rBD/strings.xml b/core/res/res/values-bn-rBD/strings.xml
index 01bcce5..7512bae 100644
--- a/core/res/res/values-bn-rBD/strings.xml
+++ b/core/res/res/values-bn-rBD/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> পর্যন্ত"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> পর্যন্ত (পরবর্তী অ্যালার্ম)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"আপনার দ্বারা এটি বন্ধ করা পর্যন্ত"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"যতক্ষণ না পর্যন্ত আপনি বিরক্ত করবেন না বন্ধ করছেন"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"সঙ্কুচিত করুন"</string>
diff --git a/core/res/res/values-bs-rBA/strings.xml b/core/res/res/values-bs-rBA/strings.xml
index 1d74ce7..c188416 100644
--- a/core/res/res/values-bs-rBA/strings.xml
+++ b/core/res/res/values-bs-rBA/strings.xml
@@ -1651,7 +1651,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (sljedeći alarm)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Dok ovo ne isključite"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Dok ne isključite opciju Ne ometaj"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Skupi"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 00e1a2f..98a96db 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Fins a les <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Fins a les <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (propera alarma)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Fins que no ho desactivis"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Fins que desactivis el mode No molesteu"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Replega"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index f622d9d..57c4d06 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1684,7 +1684,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (příští budík)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Dokud tuto funkci nevypnete"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Dokud nevypnete režim Nerušit"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Sbalit"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 723c08e..c730ce6 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Indtil <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Indtil <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (næste alarm)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Indtil du slår denne indstilling fra"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Indtil du slår \"Forstyr ikke\" fra"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Skjul"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 96307f2..167d199 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1212,7 +1212,7 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Ermöglicht der App, Installationssitzungen zu lesen. Dadurch kann sie Details aktiver Paketinstallationen abrufen."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"Installation von Paketen anfordern"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Ermöglicht der App, die Installation von Paketen anzufordern"</string>
-    <string name="permlab_requestIgnoreBatteryOptimizations" msgid="8021256345643918264">"Fragen, ob Akku-Leistungsoptimierungen ignoriert werden können"</string>
+    <string name="permlab_requestIgnoreBatteryOptimizations" msgid="8021256345643918264">"fragen, ob Akku-Leistungsoptimierungen ignoriert werden können"</string>
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="8359147856007447638">"Erlaubt einer App, nach der Berechtigung zum Ignorieren der Akku-Leistungsoptimierungen zu fragen."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Für Zoomeinstellung zweimal berühren"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Widget konnte nicht hinzugefügt werden."</string>
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Bis <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Bis <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (nächste Weckzeit)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Bis zur Deaktivierung"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Bis zur Deaktivierung von \"Nicht stören\""</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Minimieren"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index d767abf..c477aec 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Έως τις <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Μέχρι τις <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (επόμενο ξυπνητήρι)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Μέχρι να το απενεργοποιήσετε"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Μέχρι να απενεργοποιήσετε τη ρύθμιση \"Μην ενοχλείτε\""</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Σύμπτυξη"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 03795d4..50989b4 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (next alarm)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Until you turn this off"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Until you turn off Do Not Disturb"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Collapse"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 03795d4..50989b4 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (next alarm)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Until you turn this off"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Until you turn off Do Not Disturb"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Collapse"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 03795d4..50989b4 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (next alarm)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Until you turn this off"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Until you turn off Do Not Disturb"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Collapse"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index e984c65..347a331 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Hasta la(s) <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Hasta la hora <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próxima alarma)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Hasta que lo desactives"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Hasta que desactives No molestar"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Contraer"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 0e1df79..0d624c7 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Hasta las <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Hasta las <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próxima alarma)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Hasta desactivar esta opción"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Hasta que desactives la opción No molestar"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Contraer"</string>
diff --git a/core/res/res/values-et-rEE/strings.xml b/core/res/res/values-et-rEE/strings.xml
index 6429193..a6c6cc9 100644
--- a/core/res/res/values-et-rEE/strings.xml
+++ b/core/res/res/values-et-rEE/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Kuni <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Kuni <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (järgmine äratus)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Kuni lülitate selle välja"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Kuni lülitate välja valiku Mitte segada"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Ahendamine"</string>
diff --git a/core/res/res/values-eu-rES/strings.xml b/core/res/res/values-eu-rES/strings.xml
index d8cd634..c2f6d7a 100644
--- a/core/res/res/values-eu-rES/strings.xml
+++ b/core/res/res/values-eu-rES/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> arte"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> arte (hurrengo alarma)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Zuk desaktibatu arte"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"\"Ez molestatu\" desaktibatzen duzun arte"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Tolestu"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 457127a..8585bff 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"تا <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"تا <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (هشدار بعدی)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"تا وقتی آن را خاموش کنید"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"تا زمانی که «مزاحم نشوید» را خاموش کنید"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> /‏ <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"کوچک کردن"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 36c3e6c..4b463109 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Kunnes kello on <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> asti (seuraava hälytys)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Kunnes otat tämän pois päältä"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Kunnes poistat Varattu-tilan käytöstä."</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Kutista"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 9dd10fa..d7a1bd6 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Jusqu\'à <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Jusqu\'à <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (alarme suivante)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Jusqu\'à la désactivation"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Jusqu\'à ce que vous désactiviez le mode « Ne pas déranger »"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Réduire"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index e9a4681..af291d4 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Jusqu\'à <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Jusqu\'à <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (alarme suivante)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Jusqu\'à la désactivation"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Jusqu\'à ce que vous désactiviez la fonctionnalité \"Ne pas déranger\""</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Réduire"</string>
diff --git a/core/res/res/values-gl-rES/strings.xml b/core/res/res/values-gl-rES/strings.xml
index 10ab490..27acc35 100644
--- a/core/res/res/values-gl-rES/strings.xml
+++ b/core/res/res/values-gl-rES/strings.xml
@@ -1212,8 +1212,8 @@
     <string name="permdesc_readInstallSessions" msgid="2049771699626019849">"Permite que unha aplicación consulte as sesións de instalación. Desta forma, pode ver os detalles acerca das instalacións de paquetes activas."</string>
     <string name="permlab_requestInstallPackages" msgid="5782013576218172577">"solicitar instalación de paquetes"</string>
     <string name="permdesc_requestInstallPackages" msgid="5740101072486783082">"Permite a unha aplicación solicitar a instalación dos paquetes."</string>
-    <string name="permlab_requestIgnoreBatteryOptimizations" msgid="8021256345643918264">"solicitar ignorar optimizacións da batería"</string>
-    <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="8359147856007447638">"Permite que unha aplicación solicite permiso para ignorar optimizacións da batería para esa aplicación."</string>
+    <string name="permlab_requestIgnoreBatteryOptimizations" msgid="8021256345643918264">"pedir que se ignore a optimización da batería"</string>
+    <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="8359147856007447638">"Fai que unha aplicación poida solicitar permiso para ignorar as optimizacións da batería."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1311810005957319690">"Toca dúas veces para controlar o zoom"</string>
     <string name="gadget_host_error_inflating" msgid="4882004314906466162">"Non se puido engadir o widget."</string>
     <string name="ime_action_go" msgid="8320845651737369027">"Ir"</string>
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Ata as <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Ata as <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próxima alarma)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Ata que desactives isto"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Ata que desactives o modo Non molestar"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Contraer"</string>
diff --git a/core/res/res/values-gu-rIN/strings.xml b/core/res/res/values-gu-rIN/strings.xml
index e648ba5..a36079f 100644
--- a/core/res/res/values-gu-rIN/strings.xml
+++ b/core/res/res/values-gu-rIN/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> સુધી"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (આગલા એલાર્મ) સુધી"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"તમે આ બંધ ન કરો ત્યાં સુધી"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"તમે ખલેલ પાડશો નહીં બંધ ન કરો ત્યાં સુધી"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"સંકુચિત કરો"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 6cd8fd94..f58b293 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> तक"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (अगले अलार्म) तक"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"जब तक आप इसे बंद नहीं कर देते"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"जब तक कि आप परेशान ना करें को बंद नहीं कर देते"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"संक्षिप्त करें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index cd4ef80..a4fc7c4 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1649,7 +1649,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (sljedeći alarm)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Dok ne isključite"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Dok ne isključite \"Ne uznemiravaj\""</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Sažmi"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index f06a006..0823d48 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Eddig: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Eddig: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (ez a következő ébresztés)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Amíg ki nem kapcsolja ezt"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Amíg ki nem kapcsolja a „Ne zavarjanak” lehetőséget"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Összecsukás"</string>
diff --git a/core/res/res/values-hy-rAM/strings.xml b/core/res/res/values-hy-rAM/strings.xml
index 25f3213..0f88ee2 100644
--- a/core/res/res/values-hy-rAM/strings.xml
+++ b/core/res/res/values-hy-rAM/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Մինչև <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Մինչև ժ. <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>-ը (հաջորդ զարթուցիչը)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Քանի դեռ չեք անջատել"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Մինչև չանջատեք «Չանհանգստացնել» գործառույթը"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Թաքցնել"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 944583d..75907a9 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Hingga <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Hingga <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (alarm berikutnya)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Hingga Anda menonaktifkan ini"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Hingga Anda menonaktifkan status Jangan Ganggu"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Ciutkan"</string>
diff --git a/core/res/res/values-is-rIS/strings.xml b/core/res/res/values-is-rIS/strings.xml
index 9986dd8..4226a86 100644
--- a/core/res/res/values-is-rIS/strings.xml
+++ b/core/res/res/values-is-rIS/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Til <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Þangað til <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (næsta viðvörun)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Þar til þú slekkur á þessu"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Þar til þú slekkur á „Ónáðið ekki“"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Minnka"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 4496dfe..e54ed19 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Fino a <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Fino a <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (prossima sveglia)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Fino alla disattivazione"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Fino alla disattivazione di Non disturbare"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Comprimi"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 465f5e9..d0d8e06 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1684,7 +1684,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"עד <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"עד <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (ההתראה הבאה)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"עד שתכבה"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"עד שתכבה את \'נא לא להפריע\'"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"כווץ"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index ee3ff99..cf8052e 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>まで"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>(次のアラーム)まで"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"ユーザーがOFFにするまで"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"マナーモードを OFF にするまで"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"折りたたむ"</string>
diff --git a/core/res/res/values-ka-rGE/strings.xml b/core/res/res/values-ka-rGE/strings.xml
index 79fc4638..2b6ee7f 100644
--- a/core/res/res/values-ka-rGE/strings.xml
+++ b/core/res/res/values-ka-rGE/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>-მდე"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>-მდე (შემდეგი მაღვიძარა)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"სანამ ამას გამორთავდეთ"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"სანამ გამორთავთ „არ შემაწუხოთ“ ფუნქციას"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"აკეცვა"</string>
diff --git a/core/res/res/values-kk-rKZ/strings.xml b/core/res/res/values-kk-rKZ/strings.xml
index bbb34cb..ad9060c 100644
--- a/core/res/res/values-kk-rKZ/strings.xml
+++ b/core/res/res/values-kk-rKZ/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> дейін"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> дейін (келесі дабыл)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Сіз осыны өшіргенше"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Өшірмейінше мазаламау"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Тасалау"</string>
diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml
index 1566c92..e53164a 100644
--- a/core/res/res/values-km-rKH/strings.xml
+++ b/core/res/res/values-km-rKH/strings.xml
@@ -1616,7 +1616,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"រហូត​ដល់ <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"រហូតដល់ <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (ម៉ោងរោទិ៍បន្ទាប់)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"រហូត​ដល់ពេល​​អ្នក​បិទ​វា"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"រហូតទាល់តែអ្នកបិទ កុំរំខាន"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"បង្រួម"</string>
diff --git a/core/res/res/values-kn-rIN/strings.xml b/core/res/res/values-kn-rIN/strings.xml
index 5c60fbe..5342672 100644
--- a/core/res/res/values-kn-rIN/strings.xml
+++ b/core/res/res/values-kn-rIN/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ವರೆಗೆ"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ವರೆಗೆ (ಮುಂದಿನ ಅಲಾರಮ್)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"ನೀವಿದನ್ನು ಆಫ್‌ ಮಾಡುವವರೆಗೆ"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"ನೀವು ಆಫ್ ಮಾಡುವವರೆಗೂ ಅಡಚಣೆ ಮಾಡಬೇಡಿ"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"ಸಂಕುಚಿಸು"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 07491a9..11f26eb 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -248,9 +248,9 @@
     <string name="permgrouplab_location" msgid="7275582855722310164">"위치"</string>
     <string name="permgroupdesc_location" msgid="1346617465127855033">"이 기기의 위치정보에 액세스"</string>
     <string name="permgrouplab_calendar" msgid="5863508437783683902">"캘린더"</string>
-    <string name="permgroupdesc_calendar" msgid="3889615280211184106">"일정에 접근할 수 있도록"</string>
+    <string name="permgroupdesc_calendar" msgid="3889615280211184106">"캘린더에 액세스"</string>
     <string name="permgrouplab_sms" msgid="228308803364967808">"SMS"</string>
-    <string name="permgroupdesc_sms" msgid="4656988620100940350">"문자 메시지를 보내고 확인할 수 있도록"</string>
+    <string name="permgroupdesc_sms" msgid="4656988620100940350">"SMS 메시지 전송 및 보기"</string>
     <string name="permgrouplab_storage" msgid="1971118770546336966">"저장"</string>
     <string name="permgroupdesc_storage" msgid="637758554581589203">"기기 사진, 미디어, 파일 액세스"</string>
     <string name="permgrouplab_microphone" msgid="171539900250043464">"마이크"</string>
@@ -258,7 +258,7 @@
     <string name="permgrouplab_camera" msgid="4820372495894586615">"카메라"</string>
     <string name="permgroupdesc_camera" msgid="3250611594678347720">"사진 및 동영상 촬영"</string>
     <string name="permgrouplab_phone" msgid="5229115638567440675">"전화"</string>
-    <string name="permgroupdesc_phone" msgid="6234224354060641055">"통화 상태를 관리하거나 전화를 걸 수 있도록"</string>
+    <string name="permgroupdesc_phone" msgid="6234224354060641055">"전화 걸기 및 관리"</string>
     <string name="permgrouplab_sensors" msgid="416037179223226722">"신체 센서"</string>
     <string name="permgroupdesc_sensors" msgid="7147968539346634043">"생체 신호에 관한 센서 데이터에 액세스"</string>
     <string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"창 콘텐츠 가져오기"</string>
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>까지"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>(다음 알람)까지"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"이 기능을 사용 중지할 때까지"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"알림 일시중지 기능을 사용 중지할 때까지"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"접기"</string>
diff --git a/core/res/res/values-ky-rKG/strings.xml b/core/res/res/values-ky-rKG/strings.xml
index 86128bd..86bcef3 100644
--- a/core/res/res/values-ky-rKG/strings.xml
+++ b/core/res/res/values-ky-rKG/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> чейин"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> чейин (кийинки ойготкуч)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Бул өчүрүлгөнгө чейин"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"\"Тынчымды алба\" режими өчүрүлгөнгө чейин"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Жыйнап коюу"</string>
diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml
index db08110..9e15571 100644
--- a/core/res/res/values-lo-rLA/strings.xml
+++ b/core/res/res/values-lo-rLA/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"ຈົນ​ຮອດ <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"ຈົນ​ກ​່​ວາ <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (ສັນ​ຍານ​ເຕືອນ​ຕໍ່ໄປ​)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"ຈົນກວ່າ​ທ່ານ​ຈະ​ປິດ​"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"ຈົນ​ກ່​ວາ​ທ່ານ​ປິດ​ຫ້າມ​ລົບ​ກວນ"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"ຫຍໍ້"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 888a312..3591ea4 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1684,7 +1684,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Iki <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Iki <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (kitas signalas)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Kol išjungsite"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Kol neišjungsite režimo „Netrukdyti“"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Sutraukti"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 3092fd6..2f2399b 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1649,7 +1649,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Līdz <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Līdz plkst. <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (nākamais signāls)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Līdz brīdim, kad izslēgsiet"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Līdz izslēgsiet statusu “Netraucēt”"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Sakļaut"</string>
diff --git a/core/res/res/values-mk-rMK/strings.xml b/core/res/res/values-mk-rMK/strings.xml
index ad79b88..2a4cd9d 100644
--- a/core/res/res/values-mk-rMK/strings.xml
+++ b/core/res/res/values-mk-rMK/strings.xml
@@ -1616,7 +1616,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (следниот аларм)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Додека не го исклучите"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Додека не го исклучите Не вознемирувај"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Собери"</string>
diff --git a/core/res/res/values-ml-rIN/strings.xml b/core/res/res/values-ml-rIN/strings.xml
index 2d4cf65..0c7eff1 100644
--- a/core/res/res/values-ml-rIN/strings.xml
+++ b/core/res/res/values-ml-rIN/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> വരെ"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> വരെ (അടുത്ത അലാറം)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"നിങ്ങൾ ഇത് ഓ‌ഫാക്കും വരെ"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"\'ശല്ല്യപ്പെടുത്തരുത്\' ഓഫാക്കുന്നതുവരെ"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"ചുരുക്കുക"</string>
diff --git a/core/res/res/values-mn-rMN/strings.xml b/core/res/res/values-mn-rMN/strings.xml
index c364488..73a8be0 100644
--- a/core/res/res/values-mn-rMN/strings.xml
+++ b/core/res/res/values-mn-rMN/strings.xml
@@ -1612,7 +1612,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> хүртэл"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> хүртэл (дараагийн сэрүүлэг)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Таныг унтраах хүртэл"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"\"Бүү саад бол\"-ыг унтраах хүртэл"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Хумих"</string>
diff --git a/core/res/res/values-mr-rIN/strings.xml b/core/res/res/values-mr-rIN/strings.xml
index 03721ce..3228337 100644
--- a/core/res/res/values-mr-rIN/strings.xml
+++ b/core/res/res/values-mr-rIN/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> पर्यंत"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> पर्यंत (पुढील अलार्म)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"आपण हे बंद करेपर्यंत"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"आपण बंद करेपर्यंत व्यत्यय आणू नका"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"संक्षिप्त करा"</string>
diff --git a/core/res/res/values-ms-rMY/strings.xml b/core/res/res/values-ms-rMY/strings.xml
index 3684ebe..bd28553 100644
--- a/core/res/res/values-ms-rMY/strings.xml
+++ b/core/res/res/values-ms-rMY/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Sehingga <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Sehingga <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (penggera akan datang)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Sehingga anda matikan"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Hingga anda mematikan Jangan Ganggu"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Runtuhkan"</string>
diff --git a/core/res/res/values-my-rMM/strings.xml b/core/res/res/values-my-rMM/strings.xml
index 50c3b8c..489a85a 100644
--- a/core/res/res/values-my-rMM/strings.xml
+++ b/core/res/res/values-my-rMM/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>အထိ"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> အထိ (လာမည့် နှိုးစက်)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"သင်က ဒါကို ပိတ်မပစ်သည့် အထိ"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"မနှောင့်ယှက်ရန် ကိုသင်ပိတ်သည်အထိ"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"ခေါက်ရန်"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index e8bab9c..34909e1 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Til <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Til <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (neste alarm)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Inntil du slår av funksjonen"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Inntil du slår av Ikke forstyrr"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Skjul"</string>
diff --git a/core/res/res/values-ne-rNP/strings.xml b/core/res/res/values-ne-rNP/strings.xml
index 838c83b..5df851f 100644
--- a/core/res/res/values-ne-rNP/strings.xml
+++ b/core/res/res/values-ne-rNP/strings.xml
@@ -1620,7 +1620,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> सम्म"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (अर्को अलार्म) सम्म"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"तपाईँले यसलाई बन्द नगरेसम्म"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"तपाईँले बन्द नगरे सम्म बाधा नपुर्याउँनुहोस्"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"संक्षिप्त पार्नुहोस्"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 5c677be..caa8058 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Tot <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Tot <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (volgend alarm)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Totdat u dit uitschakelt"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Totdat u \'Niet storen\' uitschakelt"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Samenvouwen"</string>
diff --git a/core/res/res/values-pa-rIN/strings.xml b/core/res/res/values-pa-rIN/strings.xml
index c1bfad7..e88aa87 100644
--- a/core/res/res/values-pa-rIN/strings.xml
+++ b/core/res/res/values-pa-rIN/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ਤੱਕ"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ਤੱਕ (ਅਗਲਾ ਅਲਾਰਮ)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਇਸਨੂੰ ਬੰਦ ਨਹੀਂ ਕਰਦੇ ਹੋ"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ ਨੂੰ ਬੰਦ ਨਹੀਂ ਕਰਦੇ ਹੋ"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"ਨਸ਼ਟ ਕਰੋ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 8c80ab5..13ea8d3 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1684,7 +1684,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (następny alarm)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Dopóki nie wyłączysz"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Do wyłączenia Nie przeszkadzać"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Zwiń"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 26fb6bb..c0d53f2 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Até às <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Até <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próximo alarme)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Até você desativar"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Até que você desative \"Não perturbe\""</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Recolher"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 18bb723..f6dcb87 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Até às <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Até <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próximo alarme)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Até que o utilizador desative"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Até desativar Não incomodar"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Reduzir"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 26fb6bb..c0d53f2 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Até às <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Até <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próximo alarme)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Até você desativar"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Até que você desative \"Não perturbe\""</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Recolher"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 938df9a..05025c3 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1649,7 +1649,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Până la <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Până la <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (următoarea alarmă)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Până la dezactivare"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Până când dezactivați „Nu deranja”"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Restrângeți"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index f03d783..f83a878 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1684,7 +1684,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (будильник)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Пока я не отключу"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Пока вы не отключите режим \"Не беспокоить\""</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Свернуть"</string>
diff --git a/core/res/res/values-si-rLK/strings.xml b/core/res/res/values-si-rLK/strings.xml
index 8d3eaa1..5911e5e 100644
--- a/core/res/res/values-si-rLK/strings.xml
+++ b/core/res/res/values-si-rLK/strings.xml
@@ -269,8 +269,8 @@
     <string name="capability_desc_canRequestEnhancedWebAccessibility" msgid="7881063961507511765">"යෙදුම් අන්තර්ගතයට ප්‍රවේශ්‍යතාවය වැඩිවන ලෙස සකස් කිරීමට ඇතැම් විට ස්ක්‍රිප්ට් ස්ථාපනය කර ඇත."</string>
     <string name="capability_title_canRequestFilterKeyEvents" msgid="2103440391902412174">"ඔබ ටයිප් කළ පෙළ බලන්න"</string>
     <string name="capability_desc_canRequestFilterKeyEvents" msgid="7463135292204152818">"ණයවරපත් අංක සහ මුරපද වැනි පුද්ගලික දත්ත ඇතුළත් වේ."</string>
-    <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"සංදර්ශන විශාලන මට්ටම පාලනය කිරීම"</string>
-    <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"සංදර්ශනයේ විශාලන මට්ටම සහ පිහිටීම පාලනය කිරීම."</string>
+    <string name="capability_title_canControlMagnification" msgid="3593493281059424855">"සංදර්ශනයේ විශාලනය පාලනය කරන්න"</string>
+    <string name="capability_desc_canControlMagnification" msgid="4791858203568383773">"සංදර්ශනයේ විශාලන මට්ටම සහ පිහිටීම පාලනය කරන්න."</string>
     <string name="capability_title_canPerformGestures" msgid="7418984730362576862">"අභින සිදු කරන්න"</string>
     <string name="capability_desc_canPerformGestures" msgid="8296373021636981249">"තට්ටු කිරීමට, ස්වයිප් කිරීමට, පින්ච් කිරීමට, සහ වෙනත් අභින සිදු කිරීමට හැකිය."</string>
     <string name="permlab_statusBar" msgid="7417192629601890791">"තත්ව තීරුව අබල කරන්න හෝ වෙනස් කරන්න"</string>
@@ -459,13 +459,13 @@
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"සමමුහුර්ත සැකසීම් කියවන්න"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ගිණුම සඳහා සමමුහුර්ත සැකසීම් කියවීමට යෙදුමට අවසර දෙන්න. උදාහරණයක් ලෙස, ගිණුමක් සමඟ පුද්ගල යෙදුම සමමුහුර්ත දැයි මෙයට හඳුනා ගත හැක."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"සමමුහුර්ත කිරීම සක්‍රිය කරන්න සහ අක්‍රිය කරන්න"</string>
-    <string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"ගිණුම සඳහා සමමුහුර්ත සැකසීම් වෙනස් කිරීමට යෙදුමට අවසර දෙන්න. උදාහරණයක් ලෙස, ගිණුම සමඟ පුද්ගල යෙදුම සමමුහුර්ත කිරීම සක්‍රිය කිරීමට භාවිත කල හැක."</string>
+    <string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"ගිණුමක් සඳහා සමමුහුර්ත සැකසීම් විකරණය කිරීමට යෙදුමකට ඉඩ දෙයි. උදාහරණයක් ලෙස, ගිණුමක් සමඟ පුද්ගල යෙදුම සමමුහුර්ත කිරීම සබල කිරීමට මෙය භාවිත කළ හැක."</string>
     <string name="permlab_readSyncStats" msgid="7396577451360202448">"සමමුහුර්ත කිරීමේ සංඛ්‍යාන කියවීම"</string>
     <string name="permdesc_readSyncStats" msgid="1510143761757606156">"සමමුහුර්ත කිරීමේ සිදුවීම් ඉතිහාසය සහ කෙතරම් දත්ත සමමුහුර්ත වී ඇතිදැයි ඇතුලත් ගිණුම සඳහා සමමුහුර්ත කිරීමේ සංඛ්‍යාන කියවීමට යෙදුමට අවසර දෙන්න."</string>
     <string name="permlab_sdcardRead" product="nosdcard" msgid="367275095159405468">"ඔබගේ USB ආචයනය හි අන්තර්ගතය කියවන්න"</string>
     <string name="permlab_sdcardRead" product="default" msgid="2188156462934977940">"ඔබගේ SD කාඩ් පතෙහි අන්තර්ගතය කියවන්න"</string>
     <string name="permdesc_sdcardRead" product="nosdcard" msgid="3446988712598386079">"යෙදුමට ඔබගේ USB ආචයනය අන්තර්ගතය කියවීමට අවසර දෙන්න."</string>
-    <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"යෙදුමට ඔබගේ SD කාඩ් පතින් අන්තර්ගත කියවීමට අවසර දෙන්න."</string>
+    <string name="permdesc_sdcardRead" product="default" msgid="2607362473654975411">"යෙදුමට ඔබගේ SD කාඩ්පතේ අන්තර්ගතය කියවීමට ඉඩ දෙයි."</string>
     <string name="permlab_sdcardWrite" product="nosdcard" msgid="8485979062254666748">"ඔබගේ USB ආචයනයේ අන්තර්ගත වෙනස් කිරීම හෝ මැකීම"</string>
     <string name="permlab_sdcardWrite" product="default" msgid="8805693630050458763">"ඔබගේ SD පතේ අන්තර්ගත වෙනස් කිරීම හෝ මැකීම"</string>
     <string name="permdesc_sdcardWrite" product="nosdcard" msgid="6175406299445710888">"USB ආචයනය වෙත ලිවීමට යෙදුමට අවසර දෙන්න."</string>
@@ -739,7 +739,7 @@
     <!-- String.format failed for translation -->
     <!-- no translation found for keyguard_accessibility_widget_changed (5678624624681400191) -->
     <skip />
-    <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"විජටය එකතු කරන්න."</string>
+    <string name="keyguard_accessibility_add_widget" msgid="8273277058724924654">"විජටය එක් කරන්න."</string>
     <string name="keyguard_accessibility_widget_empty_slot" msgid="1281505703307930757">"හිස්"</string>
     <string name="keyguard_accessibility_unlock_area_expanded" msgid="2278106022311170299">"අගුළු අරින ප්‍රදේශය විදහා ඇත."</string>
     <string name="keyguard_accessibility_unlock_area_collapsed" msgid="6366992066936076396">"අගුළු අරින ප්‍රදේශය හැකිලී ඇත."</string>
@@ -952,7 +952,7 @@
     <string name="undo" msgid="7905788502491742328">"අස් කරන්න"</string>
     <string name="redo" msgid="7759464876566803888">"යළි කරන්න"</string>
     <string name="textSelectionCABTitle" msgid="5236850394370820357">"පෙළ තේරීම"</string>
-    <string name="addToDictionary" msgid="4352161534510057874">"ශබ්ද කෝෂයට එකතු කරන්න"</string>
+    <string name="addToDictionary" msgid="4352161534510057874">"ශබ්ද කෝෂයට එක් කරන්න"</string>
     <string name="deleteText" msgid="6979668428458199034">"මකන්න"</string>
     <string name="inputMethod" msgid="1653630062304567879">"ආදාන ක්‍රමය"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"පෙළ ක්‍රියාවන්"</string>
@@ -1290,7 +1290,7 @@
     <string name="sync_undo_deletes" msgid="2941317360600338602">"මැකීම් අස් කරන්න"</string>
     <string name="sync_do_nothing" msgid="3743764740430821845">"දැනට කිසිවක් නොකරන්න"</string>
     <string name="choose_account_label" msgid="5655203089746423927">"ගිණුමක් තෝරන්න"</string>
-    <string name="add_account_label" msgid="2935267344849993553">"ගිණුමක් එකතු කරන්න"</string>
+    <string name="add_account_label" msgid="2935267344849993553">"ගිණුමක් එක් කරන්න"</string>
     <string name="add_account_button_label" msgid="3611982894853435874">"ගිණුමක් එක් කරන්න"</string>
     <string name="number_picker_increment_button" msgid="2412072272832284313">"වැඩි කරන්න"</string>
     <string name="number_picker_decrement_button" msgid="476050778386779067">"අඩු කරන්න"</string>
@@ -1616,7 +1616,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> තෙක්"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> තෙක් (ඊළඟ එලාමය)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"ඔබ මෙය ක්‍රියාවිරහිත කරන තුරු"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"බාධා නොකරන්න ඔබ අක්‍රිය කරන තුරු"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"හකුළන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 2f44ed6..9a41b07 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1684,7 +1684,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (ďalší budík)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Kým túto funkciu nevypnete"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Dokým nevypnete stav Nerušiť"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Zbaliť"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index eda90a3..e6b08e9 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1684,7 +1684,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (naslednji alarm)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Dokler tega ne izklopite"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Dokler ne izklopite načina »ne moti«"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Strni"</string>
diff --git a/core/res/res/values-sq-rAL/strings.xml b/core/res/res/values-sq-rAL/strings.xml
index 8a05ff8..688d14b 100644
--- a/core/res/res/values-sq-rAL/strings.xml
+++ b/core/res/res/values-sq-rAL/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Deri në <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Deri në <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (alarmi tjetër)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Deri sa ta çaktivizosh këtë"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Deri sa të çaktivizosh gjendjen \"Mos shqetëso\""</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Shpalos"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index aab90e4..1ab6bb3 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1649,7 +1649,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (следећи аларм)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Док не искључите"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Док не искључите режим Не узнемиравај"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Скупи"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 57fa538..b4d7f27 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Till kl. <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Till <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (nästa alarm)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Tills du inaktiverar detta"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Tills du inaktiverar Stör ej"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Komprimera"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 0f6b875..5183c87 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1612,7 +1612,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Hadi <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Mpaka <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (kengele inayofuata)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Hadi utakapozima hili"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Hadi utakapozima Usinisumbue"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Kunja"</string>
diff --git a/core/res/res/values-ta-rIN/strings.xml b/core/res/res/values-ta-rIN/strings.xml
index 3f935c0..1dfcfb2 100644
--- a/core/res/res/values-ta-rIN/strings.xml
+++ b/core/res/res/values-ta-rIN/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> வரை"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> மணி (அடுத்த அலாரம்) வரை"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"இதை முடக்கும்வரை"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"தொந்தரவு செய்ய வேண்டாம் என்பதை முடக்கும் வரை"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"சுருக்கு"</string>
diff --git a/core/res/res/values-te-rIN/strings.xml b/core/res/res/values-te-rIN/strings.xml
index 561492a..9a3bfb1 100644
--- a/core/res/res/values-te-rIN/strings.xml
+++ b/core/res/res/values-te-rIN/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> వరకు"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (తదుపరి అలారం) వరకు"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"మీరు దీన్ని ఆఫ్ చేసే వరకు"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"మీరు అంతరాయం కలిగించవద్దు ఎంపిక ఆఫ్ చేసే వరకు"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"కుదించండి"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 200537f..25fd417 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"จนถึงเวลา <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"จนถึงเวลา <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (การปลุกครั้งถัดไป)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"จนกว่าคุณจะปิดฟังก์ชันนี้"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"จนกว่าคุณจะปิดห้ามรบกวน"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"ยุบ"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 72c52b6..51d22f3 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Hanggang <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Hanggang <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (susunod na alarm)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Hanggang sa i-off mo ito"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Hanggang sa i-off mo ang Huwag Istorbohin"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"I-collapse"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index dd7dffd..86b2ab6 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Şu saate kadar: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (sonraki alarma) saatine kadar"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Siz bunu kapatana kadar"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Rahatsız Etmeyin ayarını kapatana kadar"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Daralt"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index b153518..ce55856 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1684,7 +1684,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (наступний будильник)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Доки ви не вимкнете"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Доки ввімкнено режим \"Не турбувати\""</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Згорнути"</string>
diff --git a/core/res/res/values-ur-rPK/strings.xml b/core/res/res/values-ur-rPK/strings.xml
index 4f4706f..c71617a 100644
--- a/core/res/res/values-ur-rPK/strings.xml
+++ b/core/res/res/values-ur-rPK/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> تک"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> تک (اگلا الارم)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"جب تک آپ اسے آف نہ کر دیں"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"جب تک آپ ڈسڑب نہ کریں کو آف نہیں کر دیتے"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"سکیڑیں"</string>
diff --git a/core/res/res/values-uz-rUZ/strings.xml b/core/res/res/values-uz-rUZ/strings.xml
index 321d795..b3a248d 100644
--- a/core/res/res/values-uz-rUZ/strings.xml
+++ b/core/res/res/values-uz-rUZ/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> gacha"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> gacha (keyingi signal)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Men o‘chirmaguncha"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"“Bezovta qilinmasin” rejimi o‘chirilmaguncha"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Yig‘ish"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index f5ad6a4..8c76073 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Cho đến <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Cho tới <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (cảnh báo tiếp theo)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Cho đến khi bạn tắt tính năng này"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Cho đến khi bạn tắt Đừng làm phiền"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Thu gọn"</string>
diff --git a/core/res/res/values-watch/config.xml b/core/res/res/values-watch/config.xml
index d13d154..ac7b236 100644
--- a/core/res/res/values-watch/config.xml
+++ b/core/res/res/values-watch/config.xml
@@ -65,4 +65,5 @@
 
     <!-- The small screens of watch devices makes multi-window support undesireable. -->
     <bool name="config_supportsMultiWindow">false</bool>
+    <bool name="config_supportsSplitScreenMultiWindow">false</bool>
 </resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index ead9a66..1534e30 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"到<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"直到<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>(闹钟下次响铃时)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"直到您将其关闭"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"直到您关闭“勿扰”模式"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"收起"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index ffad746a..57e901d 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"完成時間:<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"直至<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (下一次響鬧)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"直至您關閉這項設定"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"直至您關閉「請勿騷擾」功能"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"收合"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 1a49aaf..d3bbf67 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"結束時間:<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"到<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> 為止 (下一個鬧鐘)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"手動關閉這項設定前一律啟用"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"直到您關閉「零打擾」模式"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"收合"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 3cde2a1..455dc24 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1614,7 +1614,8 @@
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Kuze kube ngu-<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Kuze kube ngu-<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (i-alamu elandelayo)"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"Uze uvale lokhu"</string>
+    <!-- no translation found for zen_mode_forever (1916263162129197274) -->
+    <skip />
     <string name="zen_mode_forever_dnd" msgid="3792132696572189081">"Uze uvale ungaphazamisi"</string>
     <string name="zen_mode_rule_name_combination" msgid="191109939968076477">"<xliff:g id="FIRST">%1$s</xliff:g> / <xliff:g id="REST">%2$s</xliff:g>"</string>
     <string name="toolbar_collapse_description" msgid="2821479483960330739">"Goqa"</string>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9e75ff9..5967c69 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1267,7 +1267,8 @@
          This may be empty if network scoring and recommending isn't supported.
          -->
     <string-array name="config_networkRecommendationPackageNames" translatable="false">
-        <!-- Add packages here -->
+        <!-- The standard AOSP network recommendation provider -->
+        <item>com.android.networkrecommendation</item>
     </string-array>
 
     <!-- Whether to enable Hardware FLP overlay which allows Hardware FLP to be
@@ -2568,6 +2569,9 @@
          E.g. freeform, split-screen, picture-in-picture. -->
     <bool name="config_supportsMultiWindow">true</bool>
 
+    <!-- True if the device supports split screen as a form of multi-window. -->
+    <bool name="config_supportsSplitScreenMultiWindow">true</bool>
+
     <!-- True if the device requires AppWidgetService even if it does not have
          the PackageManager.FEATURE_APP_WIDGETS feature -->
     <bool name="config_enableAppWidgetService">false</bool>
@@ -2610,6 +2614,9 @@
     <!-- Component that is the default launcher when demo mode is enabled. -->
     <string name="config_demoModeLauncherComponent">com.android.retaildemo/.DemoPlayer</string>
 
+    <!-- Hashed password (SHA-256) used to restrict demo mode operation -->
+    <string name="config_demoModePassword" translatable="false"></string>
+
     <!-- Flag indicating whether round icons should be parsed from the application manifest. -->
     <bool name="config_useRoundIcon">false</bool>
 
@@ -2687,4 +2694,7 @@
          user-set value if toggled by settings so the "Transition animation scale" setting
          should also be hidden if intended to be permanent. -->
     <item name="config_appTransitionAnimationDurationScaleDefault" format="float" type="dimen">1.0</item>
+
+    <!-- Flag indicates that whether non-system apps can be installed on internal storage. -->
+    <bool name="config_allow3rdPartyAppOnInternal">true</bool>
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 6c08473..510e6af 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -103,6 +103,8 @@
     <!-- Displayed when the user dialed an MMI code whose function
          could not be performed because FDN is enabled. This will be displayed in a toast. -->
     <string name="mmiFdnError">Operation is restricted to fixed dialing numbers only.</string>
+    <!-- Displayed when a carrier does not support call forwarding queries when roaming. -->
+    <string name="mmiErrorWhileRoaming">Can not change call forwarding settings from your phone while you are roaming.</string>
 
     <!-- Displayed when a phone feature such as call barring was activated. -->
     <string name="serviceEnabled">Service was enabled.</string>
@@ -204,6 +206,12 @@
     <!-- Displayed to tell the user that all service is blocked by access control. -->
     <string name="RestrictedOnAll">All voice/data/SMS services are blocked.</string>
 
+    <!-- Displayed to tell the user that they should switch their network preference. -->
+    <string name="NetworkPreferenceSwitchTitle">Can\u2019t reach network</string>
+    <!-- Displayed to tell the user that they should switch their network preference. -->
+    <string name="NetworkPreferenceSwitchSummary">To improve reception, try changing the type selected at Settings > Cellular networks > Preferred network type."</string>
+
+
     <!-- Displayed to tell the user that peer changed TTY mode -->
     <string name="peerTtyModeFull">Peer requested TTY Mode FULL</string>
     <string name="peerTtyModeHco">Peer requested TTY Mode HCO</string>
@@ -741,17 +749,11 @@
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_readSms">read your text messages (SMS or MMS)</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_readSms" product="tablet">Allows the app to read SMS
-      messages stored on your tablet or SIM card. This allows the app to read all
-      SMS messages, regardless of content or confidentiality.</string>
+    <string name="permdesc_readSms" product="tablet">This app can read all SMS (text) messages stored on your tablet.</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_readSms" product="tv">Allows the app to read SMS
-      messages stored on your TV or SIM card. This allows the app to read all
-      SMS messages, regardless of content or confidentiality.</string>
+    <string name="permdesc_readSms" product="tv">This app can read all SMS (text) messages stored on your TV.</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_readSms" product="default">Allows the app to read SMS
-      messages stored on your phone or SIM card. This allows the app to read all
-      SMS messages, regardless of content or confidentiality.</string>
+    <string name="permdesc_readSms" product="default">This app can read all SMS (text) messages stored on your phone.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_receiveWapPush">receive text messages (WAP)</string>
@@ -793,12 +795,9 @@
       running.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_systemAlertWindow">draw over other apps</string>
+    <string name="permlab_systemAlertWindow">This app can appear on top of other apps</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_systemAlertWindow">Allows the app to draw on top of other
-        applications or parts of the user interface.  They may interfere with your
-        use of the interface in any application, or change what you think you are
-        seeing in other applications.</string>
+    <string name="permdesc_systemAlertWindow">This app can appear on top of other apps or other parts of the screen. This may interfere with normal app usage and change the way that other apps appear.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_persistentActivity">make app always run</string>
@@ -904,20 +903,7 @@
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_readCallLog">read call log</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_readCallLog" product="tablet">Allows the app to read
-     your tablet\'s call log, including data about incoming and outgoing calls.
-     This permission allows apps to save your call log data, and malicious apps
-     may share call log data without your knowledge.</string>
-    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_readCallLog" product="tv">Allows the app to read
-     your TV\'s call log, including data about incoming and outgoing calls.
-     This permission allows apps to save your call log data, and malicious apps
-     may share call log data without your knowledge.</string>
-    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_readCallLog" product="default">Allows the app to read
-      your phone\'s call log, including data about incoming and outgoing calls.
-      This permission allows apps to save your call log data, and malicious apps
-      may share call log data without your knowledge.</string>
+    <string name="permdesc_readCallLog">This app can read your call history.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_writeCallLog">write call log</string>
@@ -939,46 +925,24 @@
     that monitor your physical condition, such as your heart rate.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_readCalendar">read calendar events plus confidential information</string>
+    <string name="permlab_readCalendar">Read calendar events and details</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_readCalendar" product="tablet">Allows the app to read
-       all calendar events stored on your tablet, including those of friends or
-       co-workers. This may allow the app to share or save your calendar data,
-       regardless of confidentiality or sensitivity.</string>
+    <string name="permdesc_readCalendar" product="tablet">This app can read all calendar events stored on your tablet and share or save your calendar data.</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_readCalendar" product="tv">Allows the app to read
-       all calendar events stored on your TV, including those of friends or
-       co-workers. This may allow the app to share or save your calendar data,
-       regardless of confidentiality or sensitivity.</string>
+    <string name="permdesc_readCalendar" product="tv">This app can read all calendar events stored on your TV and share or save your calendar data.</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_readCalendar" product="default">Allows the app to
-       read all calendar events stored on your phone, including those of friends
-       or co-workers. This may allow the app to share or save your calendar data,
-       regardless of confidentiality or sensitivity.</string>
+    <string name="permdesc_readCalendar" product="default">This app can read all calendar events stored on your phone and share or save your calendar data.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_writeCalendar">add or modify calendar events and send email to guests without owners\' knowledge</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_writeCalendar" product="tablet">Allows the app to
-        add, remove, change events that you can modify on your tablet, including
-        those of friends or co-workers. This may allow the app to send messages
-        that appear to come from calendar owners, or modify events without the
-        owners\' knowledge.</string>
+    <string name="permdesc_writeCalendar" product="tablet">This app can add, remove, or change calendar events on your tablet. This app can send messages that may appear to come from calendar owners, or change events without notifying their owners.</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_writeCalendar" product="tv">Allows the app to
-        add, remove, change events that you can modify on your TV, including
-        those of friends or co-workers. This may allow the app to send messages
-        that appear to come from calendar owners, or modify events without the
-        owners\' knowledge.</string>
+    <string name="permdesc_writeCalendar" product="tv">This app can add, remove, or change calendar events on your TV. This app can send messages that may appear to come from calendar owners, or change events without notifying their owners.</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_writeCalendar" product="default">Allows the app to
-        add, remove, change events that you can modify on your phone, including
-        those of friends or co-workers. This may allow the app to send messages
-        that appear to come from calendar owners, or modify events without the
-        owners\' knowledge.</string>
+    <string name="permdesc_writeCalendar" product="default">This app can add, remove, or change calendar events on your phone. This app can send messages that may appear to come from calendar owners, or change events without notifying their owners.</string>
 
-
-    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the applicatfion to do this. -->
     <string name="permlab_accessLocationExtraCommands">access extra location provider commands</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_accessLocationExtraCommands">Allows the app to access
@@ -989,23 +953,17 @@
     <string name="permlab_accessFineLocation">access precise location (GPS and
       network-based)</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_accessFineLocation">Allows the app to get your
-      precise location using the Global Positioning System (GPS) or network
-      location sources such as cell towers and Wi-Fi. These location services
-      must be turned on and available to your device for the app to use them.
-      Apps may use this to determine where you are, and may consume additional
-      battery power.</string>
+    <string name="permdesc_accessFineLocation">This app can get your location based on GPS or network location sources such as cell towers and Wi-Fi networks. These location services must be turned on and available on your phone for the app to be able to use them. This may increase battery consumption.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_accessCoarseLocation">access approximate location
       (network-based)</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_accessCoarseLocation">Allows the app to get your
-      approximate location. This location is derived by location services using
-      network location sources such as cell towers and Wi-Fi. These location
-      services must be turned on and available to your device for the app to
-      use them. Apps may use this to determine approximately where you
-      are.</string>
+    <string name="permdesc_accessCoarseLocation" product="tablet">This app can get your location based on network sources such as cell towers and Wi-Fi networks. These location services must be turned on and available on your tablet for the app to be able to use them.</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_accessCoarseLocation" product="tv">This app can get your location based on network sources such as cell towers and Wi-Fi networks. These location services must be turned on and available on your TV for the app to be able to use them.</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_accessCoarseLocation" product="default">This app can get your location based on network sources such as cell towers and Wi-Fi networks. These location services must be turned on and available on your phone for the app to be able to use them.</string>
 
  <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_modifyAudioSettings">change your audio settings</string>
@@ -1015,9 +973,7 @@
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_recordAudio">record audio</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_recordAudio">Allows the app to record audio with the
-      microphone.  This permission allows the app to record audio at any time
-      without your confirmation.</string>
+    <string name="permdesc_recordAudio">This app can record audio using the microphone at any time.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_sim_communication">send commands to the SIM</string>
@@ -1027,9 +983,7 @@
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_camera">take pictures and videos</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_camera">Allows the app to take pictures and videos
-      with the camera.  This permission allows the app to use the camera at any
-      time without your confirmation.</string>
+    <string name="permdesc_camera">This app can take pictures and record videos using the camera at any time.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_vibrate">control vibration</string>
@@ -2431,22 +2385,6 @@
     <!-- Appened to express the value is this unit of time. -->
     <string name="years">years</string>
 
-    <!-- Phrase describing a time duration using seconds [CHAR LIMIT=16] -->
-    <plurals name="duration_seconds">
-        <item quantity="one">1 second</item>
-        <item quantity="other"><xliff:g id="count">%d</xliff:g> seconds</item>
-    </plurals>
-    <!-- Phrase describing a time duration using minutes [CHAR LIMIT=16] -->
-    <plurals name="duration_minutes">
-        <item quantity="one">1 minute</item>
-        <item quantity="other"><xliff:g id="count">%d</xliff:g> minutes</item>
-    </plurals>
-    <!-- Phrase describing a time duration using hours [CHAR LIMIT=16] -->
-    <plurals name="duration_hours">
-        <item quantity="one">1 hour</item>
-        <item quantity="other"><xliff:g id="count">%d</xliff:g> hours</item>
-    </plurals>
-
     <!-- A string denoting the current point in time that should be as short as possible. Abbreviations are preferred to full strings as this might be shown repetitively. It is used in the header of notifications. [CHAR LIMIT=8]-->
     <string name="now_string_shortest">now</string>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a85ddf1..7c50f62 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -307,6 +307,7 @@
   <java-symbol type="bool" name="config_supportAudioSourceUnprocessed" />
   <java-symbol type="bool" name="config_freeformWindowManagement" />
   <java-symbol type="bool" name="config_supportsMultiWindow" />
+  <java-symbol type="bool" name="config_supportsSplitScreenMultiWindow" />
   <java-symbol type="bool" name="config_guestUserEphemeral" />
   <java-symbol type="bool" name="config_localDisplaysMirrorContent" />
   <java-symbol type="integer" name="config_defaultDisplayDefaultColorMode" />
@@ -493,6 +494,8 @@
   <java-symbol type="string" name="RestrictedOnData" />
   <java-symbol type="string" name="RestrictedOnEmergency" />
   <java-symbol type="string" name="RestrictedOnNormal" />
+  <java-symbol type="string" name="NetworkPreferenceSwitchSummary" />
+  <java-symbol type="string" name="NetworkPreferenceSwitchTitle" />
   <java-symbol type="string" name="SetupCallDefault" />
   <java-symbol type="string" name="accept" />
   <java-symbol type="string" name="accessibility_enabled" />
@@ -732,6 +735,7 @@
   <java-symbol type="string" name="mmiComplete" />
   <java-symbol type="string" name="mmiError" />
   <java-symbol type="string" name="mmiFdnError" />
+  <java-symbol type="string" name="mmiErrorWhileRoaming" />
   <java-symbol type="string" name="month_day_year" />
   <java-symbol type="string" name="more_item_label" />
   <java-symbol type="string" name="needPuk" />
@@ -1124,6 +1128,7 @@
   <java-symbol type="string" name="config_ethernet_tcp_buffers" />
   <java-symbol type="string" name="config_wifi_tcp_buffers" />
   <java-symbol type="string" name="config_demoModeLauncherComponent" />
+  <java-symbol type="string" name="config_demoModePassword" />
   <java-symbol type="string" name="demo_starting_message" />
   <java-symbol type="string" name="demo_restarting_message" />
   <java-symbol type="string" name="conference_call" />
@@ -1131,9 +1136,6 @@
 
 
   <java-symbol type="plurals" name="bugreport_countdown" />
-  <java-symbol type="plurals" name="duration_hours" />
-  <java-symbol type="plurals" name="duration_minutes" />
-  <java-symbol type="plurals" name="duration_seconds" />
   <java-symbol type="plurals" name="last_num_days" />
   <java-symbol type="plurals" name="matches_found" />
   <java-symbol type="plurals" name="restr_pin_countdown" />
@@ -2753,6 +2755,9 @@
 
   <java-symbol type="dimen" name="config_appTransitionAnimationDurationScaleDefault" />
 
-<!-- Network Recommendation -->
+  <!-- Network Recommendation -->
   <java-symbol type="array" name="config_networkRecommendationPackageNames" />
+
+  <!-- Whether allow 3rd party apps on internal storage. -->
+  <java-symbol type="bool" name="config_allow3rdPartyAppOnInternal" />
 </resources>
diff --git a/core/tests/coretests/src/android/content/pm/PackageHelperTests.java b/core/tests/coretests/src/android/content/pm/PackageHelperTests.java
index 5af2667..c4d00c6 100644
--- a/core/tests/coretests/src/android/content/pm/PackageHelperTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageHelperTests.java
@@ -16,17 +16,28 @@
 
 package android.content.pm;
 
-import static android.net.TrafficStats.MB_IN_BYTES;
-
+import android.content.Context;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.storage.IStorageManager;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
 import com.android.internal.content.PackageHelper;
 
+import org.mockito.Mockito;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static android.net.TrafficStats.MB_IN_BYTES;
+import static android.os.storage.VolumeInfo.STATE_MOUNTED;
+
 public class PackageHelperTests extends AndroidTestCase {
     private static final boolean localLOGV = true;
     public static final String TAG = "PackageHelperTests";
@@ -35,6 +46,94 @@
     private String fullId;
     private String fullId2;
 
+    private static final String sInternalVolPath = "/data";
+    private static final String sAdoptedVolPath = "/mnt/expand/123";
+    private static final String sPublicVolPath = "/emulated";
+
+    private static final String sInternalVolUuid = StorageManager.UUID_PRIVATE_INTERNAL;
+    private static final String sAdoptedVolUuid = "adopted";
+    private static final String sPublicVolUuid = "emulated";
+
+    private static final long sInternalSize = 20000;
+    private static final long sAdoptedSize = 10000;
+    private static final long sPublicSize = 1000000;
+
+    private static final StorageManager sStorageManager = createStorageManagerMock();
+
+    private static StorageManager createStorageManagerMock() {
+        VolumeInfo internalVol = new VolumeInfo("private",
+                VolumeInfo.TYPE_PRIVATE, null /*DiskInfo*/, null /*partGuid*/);
+        internalVol.path = sInternalVolPath;
+        internalVol.state = STATE_MOUNTED;
+        internalVol.fsUuid = sInternalVolUuid;
+
+        VolumeInfo adoptedVol = new VolumeInfo("adopted",
+                VolumeInfo.TYPE_PRIVATE, null /*DiskInfo*/, null /*partGuid*/);
+        adoptedVol.path = sAdoptedVolPath;
+        adoptedVol.state = STATE_MOUNTED;
+        adoptedVol.fsUuid = sAdoptedVolUuid;
+
+        VolumeInfo publicVol = new VolumeInfo("public",
+                VolumeInfo.TYPE_PUBLIC, null /*DiskInfo*/, null /*partGuid*/);
+        publicVol.state = STATE_MOUNTED;
+        publicVol.path = sPublicVolPath;
+        publicVol.fsUuid = sPublicVolUuid;
+
+        List<VolumeInfo> volumes = new ArrayList<>();
+        volumes.add(internalVol);
+        volumes.add(adoptedVol);
+        volumes.add(publicVol);
+
+        StorageManager storageManager = Mockito.mock(StorageManager.class);
+        Mockito.when(storageManager.getVolumes()).thenReturn(volumes);
+
+        File internalFile = new File(sInternalVolPath);
+        File adoptedFile = new File(sAdoptedVolPath);
+        File publicFile = new File(sPublicVolPath);
+        Mockito.when(storageManager.getStorageBytesUntilLow(internalFile)).thenReturn(sInternalSize);
+        Mockito.when(storageManager.getStorageBytesUntilLow(adoptedFile)).thenReturn(sAdoptedSize);
+        Mockito.when(storageManager.getStorageBytesUntilLow(publicFile)).thenReturn(sPublicSize);
+        return storageManager;
+    }
+
+    private static final class MockedInterface extends PackageHelper.TestableInterface {
+        private boolean mForceAllowOnExternal = false;
+        private boolean mAllow3rdPartyOnInternal = true;
+        private ApplicationInfo mApplicationInfo = null;
+
+        public void setMockValues(ApplicationInfo applicationInfo,
+                boolean forceAllowOnExternal, boolean allow3rdPartyOnInternal) {
+            mForceAllowOnExternal = forceAllowOnExternal;
+            mAllow3rdPartyOnInternal = allow3rdPartyOnInternal;
+            mApplicationInfo = applicationInfo;
+        }
+
+        @Override
+        public StorageManager getStorageManager(Context context) {
+            return sStorageManager;
+        }
+
+        @Override
+        public boolean getForceAllowOnExternalSetting(Context context) {
+            return mForceAllowOnExternal;
+        }
+
+        @Override
+        public boolean getAllow3rdPartyOnInternalConfig(Context context) {
+            return mAllow3rdPartyOnInternal;
+        }
+
+        @Override
+        public ApplicationInfo getExistingAppInfo(Context context, String packagename) {
+            return mApplicationInfo;
+        }
+
+        @Override
+        public File getDataDirectory() {
+            return new File(sInternalVolPath);
+        }
+    }
+
     private IStorageManager getSm() {
         IBinder service = ServiceManager.getService("mount");
         if (service != null) {
@@ -131,4 +230,328 @@
         };
         return r;
     }
+
+    public void testResolveInstallVolumeInternal_SystemApp() throws IOException {
+        ApplicationInfo systemAppInfo = new ApplicationInfo();
+        systemAppInfo.flags = ApplicationInfo.FLAG_SYSTEM;
+
+        // All test cases for when the system app fits on internal.
+        MockedInterface mockedInterface = new MockedInterface();
+        mockedInterface.setMockValues(systemAppInfo, false /*force allow on external*/,
+                true /*allow 3rd party on internal*/);
+        String volume = null;
+        volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+            1 /*install location*/, 1000 /*size bytes*/, mockedInterface);
+        assertEquals(StorageManager.UUID_PRIVATE_INTERNAL, volume);
+
+        mockedInterface.setMockValues(systemAppInfo, true /*force allow on external*/,
+                true /*allow 3rd party on internal*/);
+        volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+                1 /*install location*/, 1000 /*size bytes*/, mockedInterface);
+        assertEquals(StorageManager.UUID_PRIVATE_INTERNAL, volume);
+
+        mockedInterface.setMockValues(systemAppInfo, false /*force allow on external*/,
+                false /*allow 3rd party on internal*/);
+        volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+                1 /*install location*/, 1000 /*size bytes*/, mockedInterface);
+        assertEquals(StorageManager.UUID_PRIVATE_INTERNAL, volume);
+
+        mockedInterface.setMockValues(systemAppInfo, true /*force allow on external*/,
+                false /*allow 3rd party on internal*/);
+        volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+                1 /*install location*/, 1000 /*size bytes*/, mockedInterface);
+        assertEquals(StorageManager.UUID_PRIVATE_INTERNAL, volume);
+
+
+        // All test cases for when the system app does not fit on internal.
+        // Exception should be thrown.
+        mockedInterface.setMockValues(systemAppInfo, true /*force allow on external*/,
+                true /*allow 3rd party on internal*/);
+        try {
+            PackageHelper.resolveInstallVolume(getContext(), "package.name",
+                    1 /*install location*/, 1000000 /*size bytes*/, mockedInterface);
+            fail("Expected exception in resolveInstallVolume was not thrown");
+        } catch(IOException e) {
+            // expected
+        }
+
+        mockedInterface.setMockValues(systemAppInfo, false /*force allow on external*/,
+                true /*allow 3rd party on internal*/);
+        try {
+            PackageHelper.resolveInstallVolume(getContext(), "package.name",
+                    1 /*install location*/, 1000000 /*size bytes*/, mockedInterface);
+            fail("Expected exception in resolveInstallVolume was not thrown");
+        } catch(IOException e) {
+            // expected
+        }
+
+        mockedInterface.setMockValues(systemAppInfo, false /*force allow on external*/,
+                false /*allow 3rd party on internal*/);
+        try {
+            PackageHelper.resolveInstallVolume(getContext(), "package.name",
+                    1 /*install location*/, 1000000 /*size bytes*/, mockedInterface);
+            fail("Expected exception in resolveInstallVolume was not thrown");
+        } catch(IOException e) {
+            // expected
+        }
+
+        mockedInterface.setMockValues(systemAppInfo, true /*force allow on external*/,
+                false /*allow 3rd party on internal*/);
+        try {
+            PackageHelper.resolveInstallVolume(getContext(), "package.name",
+                    1 /*install location*/, 1000000 /*size bytes*/, mockedInterface);
+            fail("Expected exception in resolveInstallVolume was not thrown");
+        } catch(IOException e) {
+            // expected
+        }
+    }
+
+    public void testResolveInstallVolumeInternal_3rdParty_existing_not_too_big()
+            throws IOException {
+        // Existing apps always stay on the same volume.
+        // Test cases for existing app on internal.
+        ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.volumeUuid = sInternalVolUuid;
+        MockedInterface mockedInterface = new MockedInterface();
+        mockedInterface.setMockValues(appInfo, false /*force allow on external*/,
+                true /*allow 3rd party on internal*/);
+        String volume = null;
+        volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+                0 /*install location*/, 1000 /*size bytes*/, mockedInterface);
+        assertEquals(sInternalVolUuid, volume);
+
+        mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
+                true /*allow 3rd party on internal*/);
+        volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+                0 /*install location*/, 1000 /*size bytes*/, mockedInterface);
+        assertEquals(sInternalVolUuid, volume);
+    }
+
+    public void testResolveInstallVolumeInternal_3rdParty_existing_not_too_big_adopted()
+            throws IOException {
+        // Test cases for existing app on the adopted media.
+        ApplicationInfo appInfo = new ApplicationInfo();
+        MockedInterface mockedInterface = new MockedInterface();
+        String volume;
+        appInfo.volumeUuid = sAdoptedVolUuid;
+        mockedInterface.setMockValues(appInfo, false /*force allow on external*/,
+                true /*allow 3rd party on internal*/);
+        volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+            0 /*install location*/, 1000 /*size bytes*/, mockedInterface);
+        assertEquals(sAdoptedVolUuid, volume);
+
+        mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
+                true /*allow 3rd party on internal*/);
+        volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+                0 /*install location*/, 1000 /*size bytes*/, mockedInterface);
+        assertEquals(sAdoptedVolUuid, volume);
+
+        mockedInterface.setMockValues(appInfo, false /*force allow on external*/,
+                false /*allow 3rd party on internal*/);
+        volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+                0 /*install location*/, 1000 /*size bytes*/, mockedInterface);
+        assertEquals(sAdoptedVolUuid, volume);
+
+        mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
+                false /*allow 3rd party on internal*/);
+        volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+                0 /*install location*/, 1000 /*size bytes*/, mockedInterface);
+        assertEquals(sAdoptedVolUuid, volume);
+    }
+
+    public void testResolveInstallVolumeAdopted_3rdParty_existing_too_big() {
+        // Test: update size too big, will throw exception.
+        ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.volumeUuid = sAdoptedVolUuid;
+
+        MockedInterface mockedInterface = new MockedInterface();
+        mockedInterface.setMockValues(appInfo, false /*force allow on external*/,
+                true /*allow 3rd party on internal*/);
+        try {
+            PackageHelper.resolveInstallVolume(getContext(), "package.name",
+                    0 /*install location*/, 10000001 /*BIG size, won't fit*/, mockedInterface);
+            fail("Expected exception was not thrown " + appInfo.volumeUuid);
+        } catch (IOException e) {
+            //expected
+        }
+
+        mockedInterface.setMockValues(appInfo, false /*force allow on external*/,
+                false /*allow 3rd party on internal*/);
+        try {
+            PackageHelper.resolveInstallVolume(getContext(), "package.name",
+                    0 /*install location*/, 10000001 /*BIG size, won't fit*/, mockedInterface);
+            fail("Expected exception was not thrown " + appInfo.volumeUuid);
+        } catch (IOException e) {
+            //expected
+        }
+
+        mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
+                false /*allow 3rd party on internal*/);
+        try {
+            PackageHelper.resolveInstallVolume(getContext(), "package.name",
+                    0 /*install location*/, 10000001 /*BIG size, won't fit*/, mockedInterface);
+            fail("Expected exception was not thrown " + appInfo.volumeUuid);
+        } catch (IOException e) {
+            //expected
+        }
+
+        mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
+                true /*allow 3rd party on internal*/);
+        try {
+            PackageHelper.resolveInstallVolume(getContext(), "package.name",
+                    0 /*install location*/, 10000001 /*BIG size, won't fit*/, mockedInterface);
+            fail("Expected exception was not thrown " + appInfo.volumeUuid);
+        } catch (IOException e) {
+            //expected
+        }
+    }
+
+    public void testResolveInstallVolumeInternal_3rdParty_auto() throws IOException {
+        ApplicationInfo appInfo = null;
+        MockedInterface mockedInterface = new MockedInterface();
+        mockedInterface.setMockValues(appInfo, false /*force allow on external*/,
+                true /*allow 3rd party on internal*/);
+        String volume = null;
+        volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+            0 /*install location auto*/, 1000 /*size bytes*/, mockedInterface);
+        // Should return the volume with bigger available space.
+        assertEquals(sInternalVolUuid, volume);
+
+        mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
+                true /*allow 3rd party on internal*/);
+        volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+                0 /*install location auto*/, 1000 /*size bytes*/, mockedInterface);
+        // Should return the volume with bigger available space.
+        assertEquals(sInternalVolUuid, volume);
+
+        mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
+                false /*allow 3rd party on internal*/);
+        volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+                0 /*install location auto*/, 1000 /*size bytes*/, mockedInterface);
+        // Should return the volume with bigger available space.
+        assertEquals(sAdoptedVolUuid, volume);
+
+        mockedInterface.setMockValues(appInfo, false /*force allow on external*/,
+                false /*allow 3rd party on internal*/);
+        volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+                0 /*install location auto*/, 1000 /*size bytes*/, mockedInterface);
+        // Should return the volume with bigger available space.
+        assertEquals(sAdoptedVolUuid, volume);
+
+
+    }
+
+    public void testResolveInstallVolumeInternal_3rdParty_internal_only() throws IOException {
+        ApplicationInfo appInfo = null;
+        MockedInterface mockedInterface = new MockedInterface();
+        mockedInterface.setMockValues(appInfo, false /*force allow on external*/,
+                true /*allow 3rd party on internal*/);
+        String volume = null;
+        volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+            1 /*install location internal ONLY*/, 1000 /*size bytes*/, mockedInterface);
+        assertEquals(sInternalVolUuid, volume);
+
+        mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
+                true /*allow 3rd party on internal*/);
+        volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+                1 /*install location internal ONLY*/, 1000 /*size bytes*/, mockedInterface);
+        assertEquals(sInternalVolUuid, volume);
+
+        mockedInterface.setMockValues(appInfo, false /*force allow on external*/,
+                false /*allow 3rd party on internal*/);
+        try {
+            volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+                    1 /*install location internal only*/, 1000 /*size bytes*/, mockedInterface);
+            fail("Expected exception in resolveInstallVolume was not thrown");
+        } catch (IOException e) {
+            //expected
+        }
+
+        appInfo = null;
+        mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
+                false /*allow 3rd party on internal*/);
+        volume = null;
+        volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+                1 /*install location internal only*/, 1000 /*size bytes*/, mockedInterface);
+        assertEquals(sAdoptedVolUuid, volume);
+    }
+
+    public void testResolveInstallVolumeInternal_3rdParty_not_allowed_on_internal()
+        throws IOException {
+        ApplicationInfo appInfo = null;
+        MockedInterface mockedInterface = new MockedInterface();
+        mockedInterface.setMockValues(appInfo, false /*force allow on external*/,
+                false /*allow 3rd party on internal*/);
+        String volume = null;
+        volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+            0 /*install location auto*/, 1000 /*size bytes*/, mockedInterface);
+        // Should return the non-internal volume.
+        assertEquals(sAdoptedVolUuid, volume);
+
+        appInfo = null;
+        mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
+                false /*allow 3rd party on internal*/);
+        volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+                0 /*install location auto*/, 1000 /*size bytes*/, mockedInterface);
+        // Should return the non-internal volume.
+        assertEquals(sAdoptedVolUuid, volume);
+    }
+
+    public void testResolveInstallVolumeInternal_3rdParty_internal_only_too_big() {
+        ApplicationInfo appInfo = null;
+        MockedInterface mockedInterface = new MockedInterface();
+        mockedInterface.setMockValues(appInfo, false /*force allow on external*/,
+                true /*allow 3rd party on internal*/);
+        String volume = null;
+        try {
+            volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+                    1 /*install location internal ONLY*/,
+                    1000000 /*size too big*/, mockedInterface);
+            fail("Expected exception in resolveInstallVolume was not thrown");
+        } catch (IOException e) {
+            //expected
+        }
+    }
+
+    public void testResolveInstallVolumeInternal_3rdParty_internal_only_not_allowed()
+        throws IOException {
+        ApplicationInfo appInfo = null;
+        MockedInterface mockedInterface = new MockedInterface();
+        mockedInterface.setMockValues(appInfo, false /*force allow on external*/,
+                false /*allow 3rd party on internal*/);
+        String volume = null;
+        try {
+            volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+                    1 /*install location internal only*/, 1000 /*size bytes*/, mockedInterface);
+            fail("Expected exception in resolveInstallVolume was not thrown");
+        } catch (IOException e) {
+            //expected
+        }
+
+        appInfo = null;
+        mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
+                false /*allow 3rd party on internal*/);
+        volume = null;
+        volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+            1 /*install location internal only*/, 1000 /*size bytes*/, mockedInterface);
+        assertEquals(sAdoptedVolUuid, volume);
+
+    }
+
+    public void testResolveInstallVolumeInternal_3rdParty_internal_only_forced_to_external()
+        throws IOException {
+        // New/existing installation: New
+        // app request location: Internal Only
+        // 3rd party allowed on internal: False
+        // Force allow external in setting: True
+        // Size fit? Yes
+        ApplicationInfo appInfo = null;
+        MockedInterface mockedInterface = new MockedInterface();
+        mockedInterface.setMockValues(appInfo, true /*force allow on external*/,
+                false /*allow 3rd party on internal*/);
+        String volume = null;
+        volume = PackageHelper.resolveInstallVolume(getContext(), "package.name",
+            1 /*install location internal only*/, 1000 /*size bytes*/, mockedInterface);
+        assertEquals(sAdoptedVolUuid, volume);
+    }
 }
diff --git a/core/tests/coretests/src/android/text/format/DateUtilsTest.java b/core/tests/coretests/src/android/text/format/DateUtilsTest.java
index 909f971..de43fc6 100644
--- a/core/tests/coretests/src/android/text/format/DateUtilsTest.java
+++ b/core/tests/coretests/src/android/text/format/DateUtilsTest.java
@@ -61,6 +61,18 @@
         assertEquals("1 second", DateUtils.formatDuration(500));
         assertEquals("1 second", DateUtils.formatDuration(1000));
         assertEquals("2 seconds", DateUtils.formatDuration(1500));
+
+        assertEquals("0 seconds", DateUtils.formatDuration(0, DateUtils.LENGTH_LONG));
+        assertEquals("1 second", DateUtils.formatDuration(1000, DateUtils.LENGTH_LONG));
+        assertEquals("2 seconds", DateUtils.formatDuration(1500, DateUtils.LENGTH_LONG));
+
+        assertEquals("0 sec", DateUtils.formatDuration(0, DateUtils.LENGTH_SHORT));
+        assertEquals("1 sec", DateUtils.formatDuration(1000, DateUtils.LENGTH_SHORT));
+        assertEquals("2 sec", DateUtils.formatDuration(1500, DateUtils.LENGTH_SHORT));
+
+        assertEquals("0s", DateUtils.formatDuration(0, DateUtils.LENGTH_SHORTEST));
+        assertEquals("1s", DateUtils.formatDuration(1000, DateUtils.LENGTH_SHORTEST));
+        assertEquals("2s", DateUtils.formatDuration(1500, DateUtils.LENGTH_SHORTEST));
     }
 
     @SmallTest
@@ -69,6 +81,15 @@
         assertEquals("60 seconds", DateUtils.formatDuration(59500));
         assertEquals("1 minute", DateUtils.formatDuration(60000));
         assertEquals("2 minutes", DateUtils.formatDuration(120000));
+
+        assertEquals("1 minute", DateUtils.formatDuration(60000, DateUtils.LENGTH_LONG));
+        assertEquals("2 minutes", DateUtils.formatDuration(120000, DateUtils.LENGTH_LONG));
+
+        assertEquals("1 min", DateUtils.formatDuration(60000, DateUtils.LENGTH_SHORT));
+        assertEquals("2 min", DateUtils.formatDuration(120000, DateUtils.LENGTH_SHORT));
+
+        assertEquals("1m", DateUtils.formatDuration(60000, DateUtils.LENGTH_SHORTEST));
+        assertEquals("2m", DateUtils.formatDuration(120000, DateUtils.LENGTH_SHORTEST));
     }
 
     @SmallTest
@@ -76,5 +97,14 @@
         assertEquals("59 minutes", DateUtils.formatDuration(3540000));
         assertEquals("1 hour", DateUtils.formatDuration(3600000));
         assertEquals("48 hours", DateUtils.formatDuration(172800000));
+
+        assertEquals("1 hour", DateUtils.formatDuration(3600000, DateUtils.LENGTH_LONG));
+        assertEquals("48 hours", DateUtils.formatDuration(172800000, DateUtils.LENGTH_LONG));
+
+        assertEquals("1 hr", DateUtils.formatDuration(3600000, DateUtils.LENGTH_SHORT));
+        assertEquals("48 hr", DateUtils.formatDuration(172800000, DateUtils.LENGTH_SHORT));
+
+        assertEquals("1h", DateUtils.formatDuration(3600000, DateUtils.LENGTH_SHORTEST));
+        assertEquals("48h", DateUtils.formatDuration(172800000, DateUtils.LENGTH_SHORTEST));
     }
 }
diff --git a/docs/html/reference/images/graphics/colorspace_ucs.png b/docs/html/reference/images/graphics/colorspace_ucs.png
new file mode 100644
index 0000000..3e0f0c6
--- /dev/null
+++ b/docs/html/reference/images/graphics/colorspace_ucs.png
Binary files differ
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index cd75fe9..a041a28 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -140,7 +140,7 @@
      * Native bitmap has been reconfigured, so set premult and cached
      * width/height values
      */
-    // called from JNI
+    @SuppressWarnings("unused") // called from JNI
     void reinit(int width, int height, boolean requestPremultiplied) {
         mWidth = width;
         mHeight = height;
@@ -465,6 +465,15 @@
          */
         ARGB_8888   (5),
 
+        /**
+         * Each pixels is stored on 8 bytes. Each channel (RGB and alpha
+         * for translucency) is stored as a
+         * {@link android.util.Half half-precision floating point value}.
+         *
+         * This configuration is particularly suited for wide-gamut and
+         * HDR content.
+         */
+        RGBA_F16    (6),
 
         /**
          * Special configuration, when bitmap is stored only in graphic memory.
@@ -473,12 +482,12 @@
          * It is optimal for cases, when the only operation with the bitmap is to draw it on a
          * screen.
          */
-        HARDWARE    (6);
+        HARDWARE    (7);
 
         final int nativeInt;
 
         private static Config sConfigs[] = {
-            null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888, HARDWARE
+            null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888, RGBA_F16, HARDWARE
         };
 
         Config(int ni) {
@@ -760,6 +769,9 @@
                 case ALPHA_8:
                     newConfig = Config.ALPHA_8;
                     break;
+                case RGBA_F16:
+                    newConfig = Config.RGBA_F16;
+                    break;
                 //noinspection deprecation
                 case ARGB_4444:
                 case ARGB_8888:
@@ -781,8 +793,13 @@
             neww = Math.round(deviceR.width());
             newh = Math.round(deviceR.height());
 
-            bitmap = createBitmap(neww, newh, transformed ? Config.ARGB_8888 : newConfig,
-                    transformed || source.hasAlpha());
+            Config transformedConfig = newConfig;
+            if (transformed) {
+                if (transformedConfig != Config.ARGB_8888 && transformedConfig != Config.RGBA_F16) {
+                    transformedConfig = Config.ARGB_8888;
+                }
+            }
+            bitmap = createBitmap(neww, newh, transformedConfig, transformed || source.hasAlpha());
 
             canvas.translate(-deviceR.left, -deviceR.top);
             canvas.concat(m);
@@ -845,14 +862,14 @@
      * @param width    The width of the bitmap
      * @param height   The height of the bitmap
      * @param config   The bitmap config to create.
-     * @param hasAlpha If the bitmap is ARGB_8888 this flag can be used to mark the
-     *                 bitmap as opaque. Doing so will clear the bitmap in black
+     * @param hasAlpha If the bitmap is ARGB_8888 or RGBA_16F this flag can be used to
+     *                 mark the bitmap as opaque. Doing so will clear the bitmap in black
      *                 instead of transparent.
      *
      * @throws IllegalArgumentException if the width or height are <= 0, or if
      *         Config is Config.HARDWARE, because hardware bitmaps are always immutable
      */
-    private static Bitmap createBitmap(int width, int height, Config config, boolean hasAlpha) {
+    public static Bitmap createBitmap(int width, int height, Config config, boolean hasAlpha) {
         return createBitmap(null, width, height, config, hasAlpha);
     }
 
@@ -865,14 +882,14 @@
      * @param width    The width of the bitmap
      * @param height   The height of the bitmap
      * @param config   The bitmap config to create.
-     * @param hasAlpha If the bitmap is ARGB_8888 this flag can be used to mark the
-     *                 bitmap as opaque. Doing so will clear the bitmap in black
+     * @param hasAlpha If the bitmap is ARGB_8888 or RGBA_16F this flag can be used to
+     *                 mark the bitmap as opaque. Doing so will clear the bitmap in black
      *                 instead of transparent.
      *
      * @throws IllegalArgumentException if the width or height are <= 0, or if
      *         Config is Config.HARDWARE, because hardware bitmaps are always immutable
      */
-    private static Bitmap createBitmap(DisplayMetrics display, int width, int height,
+    public static Bitmap createBitmap(DisplayMetrics display, int width, int height,
             Config config, boolean hasAlpha) {
         if (width <= 0 || height <= 0) {
             throw new IllegalArgumentException("width and height must be > 0");
@@ -885,7 +902,7 @@
             bm.mDensity = display.densityDpi;
         }
         bm.setHasAlpha(hasAlpha);
-        if (config == Config.ARGB_8888 && !hasAlpha) {
+        if ((config == Config.ARGB_8888 || config == Config.RGBA_F16) && !hasAlpha) {
             nativeErase(bm.mNativePtr, 0xff000000);
         }
         // No need to initialize the bitmap to zeroes with other configs;
@@ -1526,7 +1543,7 @@
 
     /**
      * <p>Replace pixels in the bitmap with the colors in the array. Each element
-     * in the array is a packed int prepresenting a non-premultiplied ARGB
+     * in the array is a packed int representing a non-premultiplied ARGB
      * {@link Color}.</p>
      *
      * @param pixels   The colors to write to the bitmap
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index 7dc5de3..d968516 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -1671,6 +1671,28 @@
     }
 
     /**
+     * Converts values from CIE xyY to CIE L*u*v*. Y is assumed to be 1 so the
+     * input xyY array only contains the x and y components. After this method
+     * returns, the xyY array contains the converted u and v components.
+     *
+     * @param xyY The xyY value to convert to XYZ, cannot be null,
+     *            length must be a multiple of 2
+     */
+    private static void xyYToUv(@NonNull @Size(multiple = 2) float[] xyY) {
+        for (int i = 0; i < xyY.length; i += 2) {
+            float x = xyY[i];
+            float y = xyY[i + 1];
+
+            float d = -2.0f * x + 12.0f * y + 3;
+            float u = (4.0f * x) / d;
+            float v = (9.0f * y) / d;
+
+            xyY[i] = u;
+            xyY[i + 1] = v;
+        }
+    }
+
+    /**
      * <p>Computes the chromatic adaptation transform from the specified
      * source white point to the specified destination white point.</p>
      *
@@ -3162,10 +3184,10 @@
     /**
      * <p>A color space renderer can be used to visualize and compare the gamut and
      * white point of one or more color spaces. The output is an sRGB {@link Bitmap}
-     * showing a CIE 1931 xyY chromaticity diagram.</p>
+     * showing a CIE 1931 xyY or a CIE 1976 UCS chromaticity diagram.</p>
      *
      * <p>The following code snippet shows how to compare the {@link Named#SRGB}
-     * and {@link Named#DCI_P3} color spaces:</p>
+     * and {@link Named#DCI_P3} color spaces in a CIE 1931 diagram:</p>
      *
      * <pre class="prettyprint">
      * Bitmap bitmap = ColorSpace.createRenderer()
@@ -3188,12 +3210,18 @@
      */
     public static class Renderer {
         private static final int NATIVE_SIZE = 1440;
+        private static final float UCS_SCALE = 9.0f / 6.0f;
+
+        // Number of subdivision of the inside of the spectral locus
+        private static final int CHROMATICITY_RESOLUTION = 32;
+        private static final double ONE_THIRD = 1.0 / 3.0;
 
         @IntRange(from = 128, to = Integer.MAX_VALUE)
         private int mSize = 1024;
 
         private boolean mShowWhitePoint = true;
         private boolean mClip = false;
+        private boolean mUcs = false;
 
         private final List<Pair<ColorSpace, Integer>> mColorSpaces = new ArrayList<>(2);
         private final List<Point> mPoints = new ArrayList<>(0);
@@ -3241,6 +3269,35 @@
         }
 
         /**
+         * <p>Defines whether the chromaticity diagram should use the uniform
+         * chromaticity scale (CIE 1976 UCS). When the uniform chromaticity scale
+         * is used, the distance between two points on the diagram is approximately
+         * proportional to the perceived color difference.</p>
+         *
+         * <p>The following code snippet shows how to enable the uniform chromaticity
+         * scale. The image below shows the result:</p>
+         * <pre class="prettyprint">
+         * Bitmap bitmap = ColorSpace.createRenderer()
+         *     .uniformChromaticityScale(true)
+         *     .add(ColorSpace.get(ColorSpace.Named.SRGB), 0xffffffff)
+         *     .add(ColorSpace.get(ColorSpace.Named.DCI_P3), 0xffffc845)
+         *     .render();
+         * </pre>
+         * <p>
+         *     <img src="{@docRoot}reference/android/images/graphics/colorspace_ucs.png" />
+         *     <figcaption style="text-align: center;">CIE 1976 UCS diagram</figcaption>
+         * </p>
+         *
+         * @param ucs True to render the chromaticity diagram as the CIE 1976 UCS diagram
+         * @return This instance of {@link Renderer}
+         */
+        @NonNull
+        public Renderer uniformChromaticityScale(boolean ucs) {
+            mUcs = ucs;
+            return this;
+        }
+
+        /**
          * Sets the dimensions (width and height) in pixels of the output bitmap.
          * The size must be at least 128px and defaults to 1024px.
          *
@@ -3302,7 +3359,7 @@
          * </pre>
          * <p>
          *     <img src="{@docRoot}reference/android/images/graphics/colorspace_comparison2.png" />
-         *     <figcaption style="text-align: center;">sRGB vs DCI-P3</figcaption>
+         *     <figcaption style="text-align: center;">sRGB, DCI-P3, ACES and scRGB</figcaption>
          * </p>
          *
          * @param colorSpace The color space whose gamut to render on the diagram
@@ -3385,6 +3442,7 @@
 
             setTransform(canvas, width, height, primaries);
             drawBox(canvas, width, height, paint, path);
+            setUcsTransform(canvas, height);
             drawLocus(canvas, width, height, paint, path, primaries);
             drawGamuts(canvas, width, height, paint, path, primaries, whitePoint);
             drawPoints(canvas, width, height, paint);
@@ -3406,7 +3464,11 @@
 
             paint.setStyle(Paint.Style.FILL);
 
+            float radius = 4.0f / (mUcs ? UCS_SCALE : 1.0f);
+
             float[] v = new float[3];
+            float[] xy = new float[2];
+
             for (final Point point : mPoints) {
                 v[0] = point.mRgb[0];
                 v[1] = point.mRgb[1];
@@ -3415,10 +3477,13 @@
 
                 paint.setColor(point.mColor);
 
-                // XYZ to xyY, assuming Y=1.0
+                // XYZ to xyY, assuming Y=1.0, then to L*u*v* if needed
                 float sum = v[0] + v[1] + v[2];
-                canvas.drawCircle(width * v[0] / sum, height - height * v[1] / sum,
-                        4.0f, paint);
+                xy[0] = v[0] / sum;
+                xy[1] = v[1] / sum;
+                if (mUcs) xyYToUv(xy);
+
+                canvas.drawCircle(width * xy[0], height - height * xy[1], radius, paint);
             }
         }
 
@@ -3440,6 +3505,8 @@
                 @NonNull Paint paint, @NonNull Path path,
                 @NonNull @Size(6) float[] primaries, @NonNull @Size(2) float[] whitePoint) {
 
+            float radius = 4.0f / (mUcs ? UCS_SCALE : 1.0f);
+
             for (final Pair<ColorSpace, Integer> item : mColorSpaces) {
                 ColorSpace colorSpace = item.first;
                 int color = item.second;
@@ -3447,7 +3514,7 @@
                 if (colorSpace.getModel() != Model.RGB) continue;
 
                 Rgb rgb = (Rgb) colorSpace;
-                getPrimaries(rgb, primaries);
+                getPrimaries(rgb, primaries, mUcs);
 
                 path.rewind();
                 path.moveTo(width * primaries[0], height - height * primaries[1]);
@@ -3462,11 +3529,12 @@
                 // Draw the white point
                 if (mShowWhitePoint) {
                     rgb.getWhitePoint(whitePoint);
+                    if (mUcs) xyYToUv(whitePoint);
 
                     paint.setStyle(Paint.Style.FILL);
                     paint.setColor(color);
-                    canvas.drawCircle(width * whitePoint[0], height - height * whitePoint[1],
-                            4.0f, paint);
+                    canvas.drawCircle(
+                            width * whitePoint[0], height - height * whitePoint[1], radius, paint);
                 }
             }
         }
@@ -3477,10 +3545,12 @@
          *
          * @param rgb The color space whose primaries to extract
          * @param primaries A pre-allocated array of 6 floats that will hold the result
+         * @param asUcs True if the primaries should be returned in Luv, false for xyY
          */
         @NonNull
         @Size(6)
-        private static float[] getPrimaries(@NonNull Rgb rgb, @NonNull @Size(6) float[] primaries) {
+        private static float[] getPrimaries(@NonNull Rgb rgb,
+                @NonNull @Size(6) float[] primaries, boolean asUcs) {
             // TODO: We should find a better way to handle these cases
             if (rgb.equals(ColorSpace.get(Named.EXTENDED_SRGB)) ||
                     rgb.equals(ColorSpace.get(Named.LINEAR_EXTENDED_SRGB))) {
@@ -3490,9 +3560,11 @@
                 primaries[3] = 1.24f;
                 primaries[4] = -0.23f;
                 primaries[5] = -0.57f;
-                return primaries;
+            } else {
+                rgb.getPrimaries(primaries);
             }
-            return rgb.getPrimaries(primaries);
+            if (asUcs) xyYToUv(primaries);
+            return primaries;
         }
 
         /**
@@ -3513,7 +3585,13 @@
             int vertexCount = SPECTRUM_LOCUS_X.length * CHROMATICITY_RESOLUTION * 6;
             float[] vertices = new float[vertexCount * 2];
             int[] colors = new int[vertices.length];
-            computeChromaticityMesh(NATIVE_SIZE, NATIVE_SIZE, vertices, colors);
+            computeChromaticityMesh(vertices, colors);
+
+            if (mUcs) xyYToUv(vertices);
+            for (int i = 0; i < vertices.length; i += 2) {
+                vertices[i] *= width;
+                vertices[i + 1] = height - vertices[i + 1] * height;
+            }
 
             // Draw the spectral locus
             if (mClip && mColorSpaces.size() > 0) {
@@ -3522,7 +3600,8 @@
                     if (colorSpace.getModel() != Model.RGB) continue;
 
                     Rgb rgb = (Rgb) colorSpace;
-                    getPrimaries(rgb, primaries);
+                    getPrimaries(rgb, primaries, mUcs);
+
                     break;
                 }
 
@@ -3559,6 +3638,7 @@
             }
             path.close();
 
+            paint.setStrokeWidth(4.0f / (mUcs ? UCS_SCALE : 1.0f));
             paint.setStyle(Paint.Style.STROKE);
             paint.setColor(0xff000000);
             canvas.drawPath(path, paint);
@@ -3576,25 +3656,38 @@
          */
         private void drawBox(@NonNull Canvas canvas, int width, int height, @NonNull Paint paint,
                 @NonNull Path path) {
+
+            int lineCount = 10;
+            float scale = 1.0f;
+            if (mUcs) {
+                lineCount = 7;
+                scale = UCS_SCALE;
+            }
+
             // Draw the unit grid
             paint.setStyle(Paint.Style.STROKE);
             paint.setStrokeWidth(2.0f);
             paint.setColor(0xffc0c0c0);
-            for (int i = 1; i <= 9; i++) {
-                canvas.drawLine(0.0f, height - (height * i / 10.0f),
-                        0.9f * width, height - (height * i / 10.0f), paint);
-                canvas.drawLine(width * i / 10.0f, height,
-                        width * i / 10.0f, 0.1f * height, paint);
+
+            for (int i = 1; i < lineCount - 1; i++) {
+                float v = i / 10.0f;
+                float x = (width * v) * scale;
+                float y = height - (height * v) * scale;
+
+                canvas.drawLine(0.0f, y, 0.9f * width, y, paint);
+                canvas.drawLine(x, height, x, 0.1f * height, paint);
             }
 
             // Draw tick marks
             paint.setStrokeWidth(4.0f);
             paint.setColor(0xff000000);
-            for (int i = 1; i <= 9; i++) {
-                canvas.drawLine(0.0f, height - (height * i / 10.0f),
-                        width / 100.0f, height - (height * i / 10.0f), paint);
-                canvas.drawLine(width * i / 10.0f, height,
-                        width * i / 10.0f, height - (height / 100.0f), paint);
+            for (int i = 1; i < lineCount - 1; i++) {
+                float v = i / 10.0f;
+                float x = (width * v) * scale;
+                float y = height - (height * v) * scale;
+
+                canvas.drawLine(0.0f, y, width / 100.0f, y, paint);
+                canvas.drawLine(x, height, x, height - (height / 100.0f), paint);
             }
 
             // Draw the axis labels
@@ -3603,14 +3696,15 @@
             paint.setTypeface(Typeface.create("sans-serif-light", Typeface.NORMAL));
 
             Rect bounds = new Rect();
-            for (int i = 1; i < 9; i++) {
+            for (int i = 1; i < lineCount - 1; i++) {
                 String text = "0." + i;
                 paint.getTextBounds(text, 0, text.length(), bounds);
 
-                float y = height - (height * i / 10.0f);
-                canvas.drawText(text, -0.05f * width + 10, y + bounds.height() / 2.0f, paint);
+                float v = i / 10.0f;
+                float x = (width * v) * scale;
+                float y = height - (height * v) * scale;
 
-                float x = width * i / 10.0f;
+                canvas.drawText(text, -0.05f * width + 10, y + bounds.height() / 2.0f, paint);
                 canvas.drawText(text, x - bounds.width() / 2.0f,
                         height + bounds.height() + 16, paint);
             }
@@ -3643,7 +3737,7 @@
                 if (colorSpace.getModel() != Model.RGB) continue;
 
                 Rgb rgb = (Rgb) colorSpace;
-                getPrimaries(rgb, primaries);
+                getPrimaries(rgb, primaries, mUcs);
 
                 primariesBounds.left = Math.min(primariesBounds.left, primaries[4]);
                 primariesBounds.top = Math.min(primariesBounds.top, primaries[5]);
@@ -3651,26 +3745,42 @@
                 primariesBounds.bottom = Math.max(primariesBounds.bottom, primaries[3]);
             }
 
+            float max = mUcs ? 0.6f : 0.9f;
+
             primariesBounds.left = Math.min(0.0f, primariesBounds.left);
             primariesBounds.top = Math.min(0.0f, primariesBounds.top);
-            primariesBounds.right = Math.max(0.9f, primariesBounds.right);
-            primariesBounds.bottom = Math.max(0.9f, primariesBounds.bottom);
+            primariesBounds.right = Math.max(max, primariesBounds.right);
+            primariesBounds.bottom = Math.max(max, primariesBounds.bottom);
 
-            float scaleX = 0.9f / primariesBounds.width();
-            float scaleY = 0.9f / primariesBounds.height();
+            float scaleX = max / primariesBounds.width();
+            float scaleY = max / primariesBounds.height();
             float scale = Math.min(scaleX, scaleY);
 
             canvas.scale(mSize / (float) NATIVE_SIZE, mSize / (float) NATIVE_SIZE);
             canvas.scale(scale, scale);
             canvas.translate(
-                    (primariesBounds.width() - 0.9f) * width / 2.0f,
-                    (primariesBounds.height() - 0.9f) * height / 2.0f);
+                    (primariesBounds.width() - max) * width / 2.0f,
+                    (primariesBounds.height() - max) * height / 2.0f);
 
             // The spectrum extends ~0.85 vertically and ~0.65 horizontally
             // We shift the canvas a little bit to get nicer margins
             canvas.translate(0.05f * width, -0.05f * height);
         }
 
+        /**
+         * Computes and applies the Canvas transforms required to render the CIE
+         * 197 UCS chromaticity diagram.
+         *
+         * @param canvas The canvas to transform
+         * @param height Height in pixel of the final image
+         */
+        private void setUcsTransform(@NonNull Canvas canvas, int height) {
+            if (mUcs) {
+                canvas.translate(0.0f, (height - height * UCS_SCALE));
+                canvas.scale(UCS_SCALE, UCS_SCALE);
+            }
+        }
+
         // X coordinates of the spectral locus in CIE 1931
         private static final float[] SPECTRUM_LOCUS_X = {
                 0.175596f, 0.172787f, 0.170806f, 0.170085f, 0.160343f,
@@ -3716,21 +3826,15 @@
                 0.037799f, 0.029673f, 0.021547f, 0.013421f, 0.005295f
         };
 
-        // Number of subdivision of the inside of the spectral locus
-        private static final int CHROMATICITY_RESOLUTION = 32;
-        private static final double ONE_THIRD = 1.0 / 3.0;
-
         /**
          * Computes a 2D mesh representation of the CIE 1931 chromaticity
          * diagram.
          *
-         * @param width Width in pixels of the mesh
-         * @param height Height in pixels of the mesh
          * @param vertices Array of floats that will hold the mesh vertices
          * @param colors Array of floats that will hold the mesh colors
          */
-        private static void computeChromaticityMesh(int width, int height,
-                @NonNull float[] vertices, @NonNull int[] colors) {
+        private static void computeChromaticityMesh(@NonNull float[] vertices,
+                @NonNull int[] colors) {
 
             ColorSpace colorSpace = get(Named.SRGB);
 
@@ -3796,18 +3900,18 @@
                     colorIndex += 6;
 
                     // Flip the mesh upside down to match Canvas' coordinates system
-                    vertices[vertexIndex++] = v1x * width;
-                    vertices[vertexIndex++] = height - v1y * height;
-                    vertices[vertexIndex++] = v2x * width;
-                    vertices[vertexIndex++] = height - v2y * height;
-                    vertices[vertexIndex++] = v3x * width;
-                    vertices[vertexIndex++] = height - v3y * height;
-                    vertices[vertexIndex++] = v1x * width;
-                    vertices[vertexIndex++] = height - v1y * height;
-                    vertices[vertexIndex++] = v3x * width;
-                    vertices[vertexIndex++] = height - v3y * height;
-                    vertices[vertexIndex++] = v4x * width;
-                    vertices[vertexIndex++] = height - v4y * height;
+                    vertices[vertexIndex++] = v1x;
+                    vertices[vertexIndex++] = v1y;
+                    vertices[vertexIndex++] = v2x;
+                    vertices[vertexIndex++] = v2y;
+                    vertices[vertexIndex++] = v3x;
+                    vertices[vertexIndex++] = v3y;
+                    vertices[vertexIndex++] = v1x;
+                    vertices[vertexIndex++] = v1y;
+                    vertices[vertexIndex++] = v3x;
+                    vertices[vertexIndex++] = v3y;
+                    vertices[vertexIndex++] = v4x;
+                    vertices[vertexIndex++] = v4y;
                 }
             }
         }
diff --git a/graphics/java/android/graphics/PixelFormat.java b/graphics/java/android/graphics/PixelFormat.java
index 98082ca..0fa52f8 100644
--- a/graphics/java/android/graphics/PixelFormat.java
+++ b/graphics/java/android/graphics/PixelFormat.java
@@ -22,13 +22,17 @@
 import java.lang.annotation.RetentionPolicy;
 
 public class PixelFormat {
-
     /** @hide */
     @IntDef({UNKNOWN, TRANSLUCENT, TRANSPARENT, OPAQUE})
     @Retention(RetentionPolicy.SOURCE)
     public @interface Opacity {}
 
-    /* these constants need to match those in hardware/hardware.h */
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({RGBA_8888, RGBX_8888, RGBA_F16, RGBX_F16, RGB_888, RGB_565})
+    public @interface Format { };
+
+    // NOTE: these constants must match the values from graphics/common/x.x/types.hal
 
     public static final int UNKNOWN     = 0;
 
@@ -62,7 +66,6 @@
     @Deprecated
     public static final int RGB_332     = 0xB;
 
-
     /**
      * @deprecated use {@link android.graphics.ImageFormat#NV16
      * ImageFormat.NV16} instead.
@@ -84,6 +87,9 @@
     @Deprecated
     public static final int YCbCr_422_I = 0x14;
 
+    public static final int RGBA_F16    = 0x16;
+    public static final int RGBX_F16    = 0x17;
+
     /**
      * @deprecated use {@link android.graphics.ImageFormat#JPEG
      * ImageFormat.JPEG} instead.
@@ -91,7 +97,10 @@
     @Deprecated
     public static final int JPEG        = 0x100;
 
-    public static void getPixelFormatInfo(int format, PixelFormat info) {
+    public int bytesPerPixel;
+    public int bitsPerPixel;
+
+    public static void getPixelFormatInfo(@Format int format, PixelFormat info) {
         switch (format) {
             case RGBA_8888:
             case RGBX_8888:
@@ -124,18 +133,24 @@
                 info.bitsPerPixel = 12;
                 info.bytesPerPixel = 1;
                 break;
+            case RGBA_F16:
+            case RGBX_F16:
+                info.bitsPerPixel = 64;
+                info.bytesPerPixel = 8;
+                break;
             default:
                 throw new IllegalArgumentException("unknown pixel format " + format);
         }
     }
 
-    public static boolean formatHasAlpha(int format) {
+    public static boolean formatHasAlpha(@Format int format) {
         switch (format) {
             case PixelFormat.A_8:
             case PixelFormat.LA_88:
             case PixelFormat.RGBA_4444:
             case PixelFormat.RGBA_5551:
             case PixelFormat.RGBA_8888:
+            case PixelFormat.RGBA_F16:
             case PixelFormat.TRANSLUCENT:
             case PixelFormat.TRANSPARENT:
                 return true;
@@ -143,9 +158,6 @@
         return false;
     }
 
-    public int  bytesPerPixel;
-    public int  bitsPerPixel;
-
     /**
      * Determine whether or not this is a public-visible and non-deprecated {@code format}.
      *
@@ -159,12 +171,14 @@
      *
      * @hide
      */
-    public static boolean isPublicFormat(int format) {
+    public static boolean isPublicFormat(@Format int format) {
         switch (format) {
             case RGBA_8888:
             case RGBX_8888:
             case RGB_888:
             case RGB_565:
+            case RGBA_F16:
+            case RGBX_F16:
                 return true;
         }
 
diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp
index 489039c..dbbf00d 100644
--- a/libs/hwui/CanvasState.cpp
+++ b/libs/hwui/CanvasState.cpp
@@ -225,7 +225,7 @@
     bool outlineIsRounded = MathUtils::isPositive(radius);
     if (!outlineIsRounded || currentTransform()->isSimple()) {
         // TODO: consider storing this rect separately, so that this can't be replaced with clip ops
-        clipRect(bounds.left, bounds.top, bounds.right, bounds.bottom, kIntersect_SkClipOp);
+        clipRect(bounds.left, bounds.top, bounds.right, bounds.bottom, SkClipOp::kIntersect);
     }
     if (outlineIsRounded) {
         setClippingRoundRect(allocator, bounds, radius, false);
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index 5b683e0..a53a55a 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -120,7 +120,7 @@
     mCanvasState.save(SaveFlags::MatrixClip);
     mCanvasState.translate(tx, ty);
     mCanvasState.clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom,
-            kIntersect_SkClipOp);
+            SkClipOp::kIntersect);
     deferNodePropsAndOps(renderNode);
     mCanvasState.restore();
 }
@@ -262,7 +262,7 @@
         Rect clipRect;
         properties.getClippingRectForFlags(clipFlags, &clipRect);
         mCanvasState.clipRect(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom,
-                kIntersect_SkClipOp);
+                SkClipOp::kIntersect);
     }
 
     if (properties.getRevealClip().willClip()) {
diff --git a/libs/hwui/OpenGLReadback.cpp b/libs/hwui/OpenGLReadback.cpp
index 74a8395..938b6ef 100644
--- a/libs/hwui/OpenGLReadback.cpp
+++ b/libs/hwui/OpenGLReadback.cpp
@@ -108,6 +108,15 @@
     return copyGraphicBufferInto(graphicBuffer, transform, srcRect, bitmap);
 }
 
+static float sFlipVInit[16] = {
+    1, 0, 0, 0,
+    0, -1, 0, 0,
+    0, 0, 1, 0,
+    0, 1, 0, 1,
+};
+
+static const Matrix4 sFlipV(sFlipVInit);
+
 ////////////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////////
 
@@ -122,6 +131,13 @@
                 destWidth, destHeight, caches.maxTextureSize);
         return CopyResult::DestinationInvalid;
     }
+
+    // TODO: Add support for RGBA_F16 destinations
+    if (bitmap->colorType() == kRGBA_F16_SkColorType) {
+        ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported");
+        return CopyResult::DestinationInvalid;
+    }
+
     GLuint fbo = renderState.createFramebuffer();
     if (!fbo) {
         ALOGW("Could not obtain an FBO");
@@ -183,11 +199,15 @@
 
         Matrix4 croppedTexTransform(texTransform);
         if (!srcRect.isEmpty()) {
-            croppedTexTransform.loadTranslate(srcRect.left / sourceTexture.width(),
+            // We flipV to convert to 0,0 top-left for the srcRect
+            // coordinates then flip back to 0,0 bottom-left for
+            // GLES coordinates.
+            croppedTexTransform.multiply(sFlipV);
+            croppedTexTransform.translate(srcRect.left / sourceTexture.width(),
                     srcRect.top / sourceTexture.height(), 0);
             croppedTexTransform.scale(srcRect.getWidth() / sourceTexture.width(),
                     srcRect.getHeight() / sourceTexture.height(), 1);
-            croppedTexTransform.multiply(texTransform);
+            croppedTexTransform.multiply(sFlipV);
         }
         Glop glop;
         GlopBuilder(renderState, caches, &glop)
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 8ab57c9d..8cae771 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -199,11 +199,11 @@
 int SkiaCanvas::saveLayer(float left, float top, float right, float bottom,
             const SkPaint* paint, SaveFlags::Flags flags) {
     const SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom);
-    const SkCanvas::SaveLayerRec rec(&bounds, paint, layerFlags(flags));
+    //always save matrix and clip to match the behaviour of Skia and HWUI pipelines and to ensure
+    //android state tracking behavior matches that of the Skia API (partial save is not supported)
+    const SkCanvas::SaveLayerRec rec(&bounds, paint, layerFlags(flags | SaveFlags::MatrixClip));
 
-    int count = mCanvas->saveLayer(rec);
-    recordPartialSave(flags);
-    return count;
+    return mCanvas->saveLayer(rec);
 }
 
 int SkiaCanvas::saveLayerAlpha(float left, float top, float right, float bottom,
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index 287e58a..4f92657 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -115,7 +115,7 @@
      * the specified operation. The specified rectangle is considered
      * already transformed.
      */
-    void clipTransformed(const Rect& r, SkClipOp op = kIntersect_SkClipOp);
+    void clipTransformed(const Rect& r, SkClipOp op = SkClipOp::kIntersect);
 
     /**
      * Modifies the current clip with the specified region and operation.
diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp
index 5b5b74e..705395e 100644
--- a/libs/hwui/Texture.cpp
+++ b/libs/hwui/Texture.cpp
@@ -225,6 +225,12 @@
         *outInternalFormat = GL_LUMINANCE;
         *outType = GL_UNSIGNED_BYTE;
         break;
+    case kRGBA_F16_SkColorType:
+        // This format is always linear
+        *outFormat = GL_RGBA;
+        *outInternalFormat = GL_RGBA16F;
+        *outType = GL_HALF_FLOAT;
+        break;
     default:
         LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", colorType);
         break;
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 64ef866..a9058b1 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -106,6 +106,10 @@
         return PIXEL_FORMAT_RGBA_8888;
     case GL_RGBA:
         return PIXEL_FORMAT_RGBA_8888;
+    case GL_RGB:
+        return PIXEL_FORMAT_RGB_565;
+    case GL_RGBA16F:
+        return PIXEL_FORMAT_RGBA_FP16;
     default:
         LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", internalFormat);
         return PIXEL_FORMAT_UNKNOWN;
@@ -304,7 +308,8 @@
 
 sk_sp<Bitmap> Bitmap::createFrom(sp<GraphicBuffer> graphicBuffer) {
     PixelFormat format = graphicBuffer->getPixelFormat();
-    if (!graphicBuffer.get() || format != PIXEL_FORMAT_RGBA_8888) {
+    if (!graphicBuffer.get() ||
+            (format != PIXEL_FORMAT_RGBA_8888 && format != PIXEL_FORMAT_RGBA_FP16)) {
         return nullptr;
     }
     SkImageInfo info = SkImageInfo::Make(graphicBuffer->getWidth(), graphicBuffer->getHeight(),
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 14cc449..117395b 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -68,7 +68,7 @@
         if (pendingClip && !pendingClip->contains(rect)) {
             canvas->clipRect(*pendingClip);
         }
-        canvas->clipRRect(SkRRect::MakeRectXY(rect, radius, radius), kIntersect_SkClipOp, true);
+        canvas->clipRRect(SkRRect::MakeRectXY(rect, radius, radius), SkClipOp::kIntersect, true);
     } else {
         if (pendingClip) {
             (void)rect.intersect(*pendingClip);
@@ -263,7 +263,7 @@
     }
 
     if (properties.getRevealClip().willClip()) {
-        canvas->clipPath(*properties.getRevealClip().getPath(), kIntersect_SkClipOp, true);
+        canvas->clipPath(*properties.getRevealClip().getPath(), SkClipOp::kIntersect, true);
     } else if (properties.getOutline().willClip()) {
         clipOutline(properties.getOutline(), canvas, pendingClip);
         pendingClip = nullptr;
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 5ba0f42..430d6be 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -100,7 +100,7 @@
             int saveCount = layerCanvas->save();
             SkASSERT(saveCount == 1);
 
-            layerCanvas->clipRect(layerDamage.toSkRect(), kReplace_SkClipOp);
+            layerCanvas->androidFramework_setDeviceClipRestriction(layerDamage.toSkIRect());
 
             auto savedLightCenter = mLightCenter;
             // map current light center into RenderNode's coordinate space
@@ -233,8 +233,8 @@
 void SkiaPipeline::renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip,
         const std::vector<sp<RenderNode>>& nodes, bool opaque, const Rect &contentDrawBounds,
         SkCanvas* canvas) {
-
-    canvas->clipRect(clip, kReplace_SkClipOp);
+    SkAutoCanvasRestore saver(canvas, true);
+    canvas->androidFramework_setDeviceClipRestriction(clip.roundOut());
 
     if (!opaque) {
         canvas->clear(SK_ColorTRANSPARENT);
@@ -242,7 +242,6 @@
 
     if (1 == nodes.size()) {
         if (!nodes[0]->nothingToDraw()) {
-            SkAutoCanvasRestore acr(canvas, true);
             RenderNodeDrawable root(nodes[0].get(), canvas);
             root.draw(canvas);
         }
diff --git a/include/private/README b/libs/hwui/private/README
similarity index 100%
rename from include/private/README
rename to libs/hwui/private/README
diff --git a/include/private/hwui/DrawGlInfo.h b/libs/hwui/private/hwui/DrawGlInfo.h
similarity index 100%
rename from include/private/hwui/DrawGlInfo.h
rename to libs/hwui/private/hwui/DrawGlInfo.h
diff --git a/libs/hwui/tests/common/BitmapAllocationTestUtils.h b/libs/hwui/tests/common/BitmapAllocationTestUtils.h
index 6dadd3e3..4892179 100644
--- a/libs/hwui/tests/common/BitmapAllocationTestUtils.h
+++ b/libs/hwui/tests/common/BitmapAllocationTestUtils.h
@@ -39,7 +39,9 @@
     static sk_sp<Bitmap> allocateHardwareBitmap(int width, int height,
             SkColorType colorType, std::function<void(SkBitmap& bitmap)> setup) {
         SkBitmap skBitmap;
-        sk_sp<Bitmap> heapBitmap(TestUtils::createBitmap(width, height, &skBitmap));
+        SkImageInfo info = SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType);
+        skBitmap.setInfo(info);
+        sk_sp<Bitmap> heapBitmap(Bitmap::allocateHeapBitmap(&skBitmap, nullptr));
         setup(skBitmap);
         return Bitmap::allocateHardwareBitmap(skBitmap);
     }
diff --git a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
index 45443b0..f47e05a 100644
--- a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
@@ -34,11 +34,11 @@
                 [](RenderProperties& props, Canvas& canvas) {
             canvas.save(SaveFlags::MatrixClip);
             {
-                canvas.clipRect(0, 0, 200, 200, kIntersect_SkClipOp);
+                canvas.clipRect(0, 0, 200, 200, SkClipOp::kIntersect);
                 canvas.translate(100, 100);
                 canvas.rotate(45);
                 canvas.translate(-100, -100);
-                canvas.clipRect(0, 0, 200, 200, kIntersect_SkClipOp);
+                canvas.clipRect(0, 0, 200, 200, SkClipOp::kIntersect);
                 canvas.drawColor(Color::Blue_500, SkBlendMode::kSrcOver);
             }
             canvas.restore();
@@ -47,7 +47,7 @@
             {
                 SkPath clipCircle;
                 clipCircle.addCircle(100, 300, 100);
-                canvas.clipPath(&clipCircle, kIntersect_SkClipOp);
+                canvas.clipPath(&clipCircle, SkClipOp::kIntersect);
                 canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver);
             }
             canvas.restore();
diff --git a/libs/hwui/tests/common/scenes/HwBitmap565.cpp b/libs/hwui/tests/common/scenes/HwBitmap565.cpp
new file mode 100644
index 0000000..18fea3d
--- /dev/null
+++ b/libs/hwui/tests/common/scenes/HwBitmap565.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 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 "tests/common/BitmapAllocationTestUtils.h"
+
+class HwBitmap565;
+
+static TestScene::Registrar _HwBitmap565(TestScene::Info{
+    "hwBitmap565",
+    "Draws composite shader with hardware bitmap",
+    TestScene::simpleCreateScene<HwBitmap565>
+});
+
+class HwBitmap565 : public TestScene {
+public:
+    sp<RenderNode> card;
+    void createContent(int width, int height, Canvas& canvas) override {
+        canvas.drawColor(Color::Grey_200, SkBlendMode::kSrcOver);
+
+        sk_sp<Bitmap> hardwareBitmap = BitmapAllocationTestUtils::allocateHardwareBitmap(200, 200,
+                kRGB_565_SkColorType, [](SkBitmap& skBitmap) {
+            skBitmap.eraseColor(Color::White);
+            SkCanvas skCanvas(skBitmap);
+            SkPaint skPaint;
+            skPaint.setColor(Color::Red_500);
+            skCanvas.drawRect(SkRect::MakeWH(100, 100), skPaint);
+            skPaint.setColor(Color::Blue_500);
+            skCanvas.drawRect(SkRect::MakeXYWH(100, 100, 100, 100), skPaint);
+        });
+        canvas.drawBitmap(*hardwareBitmap, 10.0f, 10.0f, nullptr);
+    }
+
+    void doFrame(int frameNr) override { }
+};
\ No newline at end of file
diff --git a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
index 8b2852b..7e8a7d9 100644
--- a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp
@@ -36,7 +36,7 @@
             // nested clipped saveLayers
             canvas.saveLayerAlpha(0, 0, 400, 400, 200, SaveFlags::ClipToLayer);
             canvas.drawColor(Color::Green_700, SkBlendMode::kSrcOver);
-            canvas.clipRect(50, 50, 350, 350, kIntersect_SkClipOp);
+            canvas.clipRect(50, 50, 350, 350, SkClipOp::kIntersect);
             canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::ClipToLayer);
             canvas.drawColor(Color::Blue_500, SkBlendMode::kSrcOver);
             canvas.restore();
diff --git a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
index d44c3d5..09e70eb 100644
--- a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp
@@ -82,7 +82,7 @@
                     int middleCount = canvas.save(SaveFlags::MatrixClip);
                     for (auto op : ops) {
                         int innerCount = canvas.save(SaveFlags::MatrixClip);
-                        canvas.clipRect(0, 0, cellSize, cellSize, kIntersect_SkClipOp);
+                        canvas.clipRect(0, 0, cellSize, cellSize, SkClipOp::kIntersect);
                         canvas.drawColor(Color::White, SkBlendMode::kSrcOver);
                         op(canvas, cellSize, paint);
                         canvas.restoreToCount(innerCount);
diff --git a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
index 3ef0d1c..f166c5c 100644
--- a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
+++ b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp
@@ -177,7 +177,7 @@
         // Clip to padding
         // Can expect ~25% of views to have clip to padding with a non-null padding
         int clipRestoreCount = canvas->save(SaveFlags::MatrixClip);
-        canvas->clipRect(1, 1, 199, 199, kIntersect_SkClipOp);
+        canvas->clipRect(1, 1, 199, 199, SkClipOp::kIntersect);
 
         canvas->insertReorderBarrier(true);
 
diff --git a/libs/hwui/tests/scripts/prep_marlfish.sh b/libs/hwui/tests/scripts/prep_marlfish.sh
new file mode 100755
index 0000000..f3c14d5
--- /dev/null
+++ b/libs/hwui/tests/scripts/prep_marlfish.sh
@@ -0,0 +1,45 @@
+#marlfish is marlin & sailfish  (☞゚ヮ゚)☞
+
+cpubase=/sys/devices/system/cpu
+
+adb root
+adb wait-for-device
+adb shell stop thermal-engine
+adb shell stop perfd
+
+# silver cores
+#307200 384000 460800 537600 614400 691200 768000 844800 902400 979200
+#1056000 1132800 1209600 1286400 1363200 1440000 1516800 1593600
+# gold cores
+#307200 384000 460800 537600 614400 691200 748800 825600 902400 979200
+#1056000 1132800 1209600 1286400 1363200 1440000 1516800 1593600 1670400
+#1747200 1824000 1900800 1977600 2054400 2150400
+
+S=979200
+cpu=0
+# Changing governor and frequency in one core will be automatically applied
+# to other cores in the cluster
+while [ $((cpu < 3)) -eq 1 ]; do
+    adb shell "echo userspace > $cpubase/cpu2/cpufreq/scaling_governor"
+    echo "Setting cpu ${cpu} & $(($cpu + 1)) cluster to $S hz"
+    adb shell "echo $S > $cpubase/cpu${cpu}/cpufreq/scaling_max_freq"
+    adb shell "echo $S > $cpubase/cpu${cpu}/cpufreq/scaling_min_freq"
+    cpu=$(($cpu + 2))
+done
+
+echo "setting GPU bus and idle timer"
+adb shell "echo 0 > /sys/class/kgsl/kgsl-3d0/bus_split"
+adb shell "echo 1 > /sys/class/kgsl/kgsl-3d0/force_clk_on"
+adb shell "echo 10000 > /sys/class/kgsl/kgsl-3d0/idle_timer"
+
+#0 762 1144 1525 2288 3143 4173 5195 5859 7759 9887 11863 13763
+adb shell "echo 13763 > /sys/class/devfreq/soc:qcom,gpubw/min_freq" &> /dev/null
+
+#133000000 214000000 315000000 401800000 510000000 560000000 624000000
+echo "performance mode, 315 MHz"
+adb shell "echo performance > /sys/class/kgsl/kgsl-3d0/devfreq/governor"
+adb shell "echo 315000000 > /sys/class/kgsl/kgsl-3d0/devfreq/min_freq"
+adb shell "echo 315000000 > /sys/class/kgsl/kgsl-3d0/devfreq/max_freq"
+
+adb shell "echo 4 > /sys/class/kgsl/kgsl-3d0/min_pwrlevel"
+adb shell "echo 4 > /sys/class/kgsl/kgsl-3d0/max_pwrlevel"
diff --git a/libs/hwui/tests/scripts/stopruntime.sh b/libs/hwui/tests/scripts/stopruntime.sh
index 85a91db..0a79618 100755
--- a/libs/hwui/tests/scripts/stopruntime.sh
+++ b/libs/hwui/tests/scripts/stopruntime.sh
@@ -23,4 +23,5 @@
 done
 adb shell setprop debug.egl.traceGpuCompletion 1
 adb shell setprop debug.sf.nobootanimation 1
+# Daemonize command is available only in eng builds.
 adb shell daemonize surfaceflinger
diff --git a/libs/hwui/tests/unit/CanvasStateTests.cpp b/libs/hwui/tests/unit/CanvasStateTests.cpp
index 7555fd4..43974f6 100644
--- a/libs/hwui/tests/unit/CanvasStateTests.cpp
+++ b/libs/hwui/tests/unit/CanvasStateTests.cpp
@@ -68,13 +68,13 @@
     state.initializeSaveStack(200, 200,
             0, 0, 200, 200, Vector3());
 
-    state.clipRect(0, 0, 100, 100, kIntersect_SkClipOp);
+    state.clipRect(0, 0, 100, 100, SkClipOp::kIntersect);
     ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(100, 100));
 
-    state.clipRect(10, 10, 200, 200, kIntersect_SkClipOp);
+    state.clipRect(10, 10, 200, 200, SkClipOp::kIntersect);
     ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10, 100, 100));
 
-    state.clipRect(50, 50, 150, 150, kReplace_SkClipOp);
+    state.clipRect(50, 50, 150, 150, SkClipOp::kReplace);
     ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(50, 50, 150, 150));
 }
 
@@ -88,7 +88,7 @@
         // rotated clip causes complex clip
         state.rotate(10);
         EXPECT_TRUE(state.clipIsSimple());
-        state.clipRect(0, 0, 200, 200, kIntersect_SkClipOp);
+        state.clipRect(0, 0, 200, 200, SkClipOp::kIntersect);
         EXPECT_FALSE(state.clipIsSimple());
     }
     state.restore();
@@ -97,7 +97,7 @@
     {
         // subtracted clip causes complex clip
         EXPECT_TRUE(state.clipIsSimple());
-        state.clipRect(50, 50, 150, 150, kDifference_SkClipOp);
+        state.clipRect(50, 50, 150, 150, SkClipOp::kDifference);
         EXPECT_FALSE(state.clipIsSimple());
     }
     state.restore();
@@ -108,7 +108,7 @@
         SkPath path;
         path.addOval(SkRect::MakeWH(200, 200));
         EXPECT_TRUE(state.clipIsSimple());
-        state.clipPath(&path, kDifference_SkClipOp);
+        state.clipPath(&path, SkClipOp::kDifference);
         EXPECT_FALSE(state.clipIsSimple());
     }
     state.restore();
@@ -121,7 +121,7 @@
 
     state.save(SaveFlags::Clip);
     {
-        state.clipRect(0, 0, 10, 10, kIntersect_SkClipOp);
+        state.clipRect(0, 0, 10, 10, SkClipOp::kIntersect);
         ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10));
     }
     state.restore();
@@ -145,7 +145,7 @@
 
     state.save(SaveFlags::Matrix); // NOTE: clip not saved
     {
-        state.clipRect(0, 0, 10, 10, kIntersect_SkClipOp);
+        state.clipRect(0, 0, 10, 10, SkClipOp::kIntersect);
         ASSERT_EQ(state.getRenderTargetClipBounds(), Rect(10, 10));
     }
     state.restore();
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index 12622ff..21394ae 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -174,7 +174,7 @@
     auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
             [](RenderProperties& props, RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
-        canvas.clipRect(200, 200, 400, 400, kIntersect_SkClipOp); // intersection should be empty
+        canvas.clipRect(200, 200, 400, 400, SkClipOp::kIntersect); // intersection should be empty
         canvas.drawRect(0, 0, 400, 400, SkPaint());
         canvas.restore();
     });
@@ -453,19 +453,19 @@
         sk_sp<Bitmap> bitmap(TestUtils::createBitmap(20, 20));
 
         // left side clipped (to inset left half)
-        canvas.clipRect(10, 0, 50, 100, kReplace_SkClipOp);
+        canvas.clipRect(10, 0, 50, 100, SkClipOp::kReplace);
         canvas.drawBitmap(*bitmap, 0, 40, nullptr);
 
         // top side clipped (to inset top half)
-        canvas.clipRect(0, 10, 100, 50, kReplace_SkClipOp);
+        canvas.clipRect(0, 10, 100, 50, SkClipOp::kReplace);
         canvas.drawBitmap(*bitmap, 40, 0, nullptr);
 
         // right side clipped (to inset right half)
-        canvas.clipRect(50, 0, 90, 100, kReplace_SkClipOp);
+        canvas.clipRect(50, 0, 90, 100, SkClipOp::kReplace);
         canvas.drawBitmap(*bitmap, 80, 40, nullptr);
 
         // bottom not clipped, just abutting (inset bottom half)
-        canvas.clipRect(0, 50, 100, 90, kReplace_SkClipOp);
+        canvas.clipRect(0, 50, 100, 90, SkClipOp::kReplace);
         canvas.drawBitmap(*bitmap, 40, 70, nullptr);
     });
 
@@ -488,7 +488,7 @@
         SkPath path;
         path.addCircle(200, 200, 200, SkPath::kCW_Direction);
         canvas.save(SaveFlags::MatrixClip);
-        canvas.clipPath(&path, kIntersect_SkClipOp);
+        canvas.clipPath(&path, SkClipOp::kIntersect);
         SkPaint paint;
         paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
         paint.setAntiAlias(true);
@@ -649,7 +649,7 @@
     auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
             [&layerUpdater](RenderProperties& props, RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
-        canvas.clipRect(50, 50, 150, 150, kIntersect_SkClipOp);
+        canvas.clipRect(50, 50, 150, 150, SkClipOp::kIntersect);
         canvas.drawLayer(layerUpdater.get());
         canvas.restore();
     });
@@ -973,7 +973,7 @@
         auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
                 [](RenderProperties& props, RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
-        canvas.clipRect(200, 200, 400, 400, kIntersect_SkClipOp);
+        canvas.clipRect(200, 200, 400, 400, SkClipOp::kIntersect);
         canvas.saveLayerAlpha(200, 200, 400, 400, 128, SaveFlags::ClipToLayer);
 
         // draw within save layer may still be recorded, but shouldn't be drawn
@@ -1781,7 +1781,7 @@
     auto child = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400,
             [&projectingRipple](RenderProperties& properties, RecordingCanvas& canvas) {
         // Record time clip will be ignored by projectee
-        canvas.clipRect(100, 100, 300, 300, kIntersect_SkClipOp);
+        canvas.clipRect(100, 100, 300, 300, SkClipOp::kIntersect);
 
         canvas.translate(-scrollX, -scrollY); // Apply scroll (note: bg undoes this internally)
         canvas.drawRenderNode(projectingRipple.get());
@@ -1993,7 +1993,7 @@
             [](RenderProperties& props, RecordingCanvas& canvas) {
         // Apply a clip before the reorder barrier/shadow casting child is drawn.
         // This clip must be applied to the shadow cast by the child.
-        canvas.clipRect(25, 25, 75, 75, kIntersect_SkClipOp);
+        canvas.clipRect(25, 25, 75, 75, SkClipOp::kIntersect);
         canvas.insertReorderBarrier(true);
         canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
     });
@@ -2252,7 +2252,7 @@
     };
     auto node = TestUtils::createNode<RecordingCanvas>(20, 20, 30, 30,
             [](RenderProperties& props, RecordingCanvas& canvas) {
-        canvas.clipRect(0, -20, 10, 30, kReplace_SkClipOp);
+        canvas.clipRect(0, -20, 10, 30, SkClipOp::kReplace);
         canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
     });
 
diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
index 70c9391..4a73383 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -58,7 +58,7 @@
 TEST(RecordingCanvas, clipRect) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
-        canvas.clipRect(0, 0, 100, 100, kIntersect_SkClipOp);
+        canvas.clipRect(0, 0, 100, 100, SkClipOp::kIntersect);
         canvas.drawRect(0, 0, 50, 50, SkPaint());
         canvas.drawRect(50, 50, 100, 100, SkPaint());
         canvas.restore();
@@ -74,8 +74,8 @@
 TEST(RecordingCanvas, emptyClipRect) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
-        canvas.clipRect(0, 0, 100, 100, kIntersect_SkClipOp);
-        canvas.clipRect(100, 100, 200, 200, kIntersect_SkClipOp);
+        canvas.clipRect(0, 0, 100, 100, SkClipOp::kIntersect);
+        canvas.clipRect(100, 100, 200, 200, SkClipOp::kIntersect);
         canvas.drawRect(0, 0, 50, 50, SkPaint()); // rejected at record time
         canvas.restore();
     });
@@ -441,7 +441,7 @@
 TEST(RecordingCanvas, saveLayer_addClipFlag) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
-        canvas.clipRect(10, 20, 190, 180, kIntersect_SkClipOp);
+        canvas.clipRect(10, 20, 190, 180, SkClipOp::kIntersect);
         canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped
         canvas.drawRect(10, 20, 190, 180, SkPaint());
         canvas.restore();
@@ -460,7 +460,7 @@
 TEST(RecordingCanvas, saveLayer_viewportCrop) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         // shouldn't matter, since saveLayer will clip to its bounds
-        canvas.clipRect(-1000, -1000, 1000, 1000, kReplace_SkClipOp);
+        canvas.clipRect(-1000, -1000, 1000, 1000, SkClipOp::kReplace);
 
         canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::ClipToLayer);
         canvas.drawRect(0, 0, 400, 400, SkPaint());
@@ -550,7 +550,7 @@
         canvas.save(SaveFlags::MatrixClip);
         canvas.translate(0, -20); // avoid identity case
         // empty clip rect should force layer + contents to be rejected
-        canvas.clipRect(0, -20, 200, -20, kIntersect_SkClipOp);
+        canvas.clipRect(0, -20, 200, -20, SkClipOp::kIntersect);
         canvas.saveLayerAlpha(0, 0, 200, 200, 128, SaveFlags::ClipToLayer);
         canvas.drawRect(0, 0, 200, 200, SkPaint());
         canvas.restore();
@@ -569,7 +569,7 @@
     });
 
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [&child](RecordingCanvas& canvas) {
-        canvas.clipRect(0, 0, 0, 0, kIntersect_SkClipOp); // empty clip, reject node
+        canvas.clipRect(0, 0, 0, 0, SkClipOp::kIntersect); // empty clip, reject node
         canvas.drawRenderNode(child.get()); // shouldn't crash when rejecting node...
     });
     ASSERT_TRUE(dl->isEmpty());
@@ -622,7 +622,7 @@
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
         // since no explicit clip set on canvas, this should be the one observed on op:
-        canvas.clipRect(-100, -100, 300, 300, kIntersect_SkClipOp);
+        canvas.clipRect(-100, -100, 300, 300, SkClipOp::kIntersect);
 
         SkPaint paint;
         paint.setColor(SK_ColorWHITE);
@@ -638,7 +638,7 @@
 TEST(RecordingCanvas, replaceClipIntersectWithRoot) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
         canvas.save(SaveFlags::MatrixClip);
-        canvas.clipRect(-10, -10, 110, 110, kReplace_SkClipOp);
+        canvas.clipRect(-10, -10, 110, 110, SkClipOp::kReplace);
         canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
         canvas.restore();
     });
@@ -676,7 +676,7 @@
         canvas.drawRect(0, 0, 400, 400, SkPaint());
 
         // second chunk: no recorded clip, since inorder region
-        canvas.clipRect(0, 0, 200, 200, kIntersect_SkClipOp);
+        canvas.clipRect(0, 0, 200, 200, SkClipOp::kIntersect);
         canvas.insertReorderBarrier(false);
         canvas.drawRect(0, 0, 400, 400, SkPaint());
 
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 4d4705c..0906cc8 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -168,6 +168,59 @@
 }
 
 namespace {
+static SkRect getRecorderClipBounds(const SkiaRecordingCanvas& recorder) {
+    SkRect clipBounds;
+    recorder.getClipBounds(&clipBounds);
+    return clipBounds;
+}
+
+static SkMatrix getRecorderMatrix(const SkiaRecordingCanvas& recorder) {
+    SkMatrix matrix;
+    recorder.getMatrix(&matrix);
+    return matrix;
+}
+}
+
+TEST(RenderNodeDrawable, saveLayerClipAndMatrixRestore)
+{
+    auto surface = SkSurface::MakeRasterN32Premul(400, 800);
+    SkCanvas& canvas = *surface->getCanvas();
+    canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
+    ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE);
+
+    auto rootNode = TestUtils::createSkiaNode(0, 0, 400, 800,
+        [](RenderProperties& props, SkiaRecordingCanvas& recorder) {
+            SkPaint layerPaint;
+            ASSERT_EQ(SkRect::MakeLTRB(0, 0, 400, 800), getRecorderClipBounds(recorder));
+            EXPECT_TRUE(getRecorderMatrix(recorder).isIdentity());
+
+            //note we don't pass SaveFlags::MatrixClip, but matrix and clip will be saved
+            recorder.saveLayer(0, 0, 400, 400, &layerPaint, SaveFlags::ClipToLayer);
+            ASSERT_EQ(SkRect::MakeLTRB(0, 0, 400, 400), getRecorderClipBounds(recorder));
+            EXPECT_TRUE(getRecorderMatrix(recorder).isIdentity());
+
+            recorder.clipRect(50, 50, 350, 350, SkClipOp::kIntersect);
+            ASSERT_EQ(SkRect::MakeLTRB(50, 50, 350, 350), getRecorderClipBounds(recorder));
+
+            recorder.translate(300.0f, 400.0f);
+            EXPECT_EQ(SkMatrix::MakeTrans(300.0f, 400.0f), getRecorderMatrix(recorder));
+
+            recorder.restore();
+            ASSERT_EQ(SkRect::MakeLTRB(0, 0, 400, 800), getRecorderClipBounds(recorder));
+            EXPECT_TRUE(getRecorderMatrix(recorder).isIdentity());
+
+            SkPaint paint;
+            paint.setAntiAlias(true);
+            paint.setColor(SK_ColorGREEN);
+            recorder.drawRect(0.0f, 400.0f, 400.0f, 800.0f, paint);
+        });
+
+    RenderNodeDrawable drawable(rootNode.get(), &canvas, true);
+    canvas.drawDrawable(&drawable);
+    ASSERT_EQ(SK_ColorGREEN, TestUtils::getColor(surface, 200, 600));
+}
+
+namespace {
 class ContextFactory : public IContextFactory {
 public:
     virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
@@ -455,7 +508,7 @@
     auto child = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
             [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
         // Record time clip will be ignored by projectee
-        canvas.clipRect(100, 100, 300, 300, kIntersect_SkClipOp);
+        canvas.clipRect(100, 100, 300, 300, SkClipOp::kIntersect);
 
         canvas.translate(-SCROLL_X, -SCROLL_Y); // Apply scroll (note: bg undoes this internally)
         canvas.drawRenderNode(projectingRipple.get());
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 06f8693..f3a663e 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -201,12 +201,11 @@
 template <typename T>
 class DeferLayer : public SkSurface_Base {
 public:
-    DeferLayer(T *canvas)
-        : SkSurface_Base(canvas->imageInfo(), nullptr)
-        , mCanvas(canvas) {
+    DeferLayer()
+        : SkSurface_Base(T().imageInfo(), nullptr) {
     }
     SkCanvas* onNewCanvas() override {
-        return mCanvas;
+        return new T();
     }
     sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override {
         return sk_sp<SkSurface>();
@@ -214,8 +213,8 @@
     sk_sp<SkImage> onNewImageSnapshot(SkBudgeted, SkCopyPixelsMode) override {
         return sk_sp<SkImage>();
     }
+    T* canvas() { return static_cast<T*>(getCanvas()); }
     void onCopyOnWrite(ContentChangeMode) override {}
-    T* mCanvas;  // bare pointer, not owned/ref'd
 };
 }
 
@@ -280,10 +279,9 @@
     LayerUpdateQueue layerUpdateQueue;
     SkRect dirty = SkRect::MakeWH(800, 600);
     auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
-    DeferTestCanvas canvas;
-    sk_sp<SkSurface> surface(new DeferLayer<DeferTestCanvas>(&canvas));
+    sk_sp<DeferLayer<DeferTestCanvas>> surface(new DeferLayer<DeferTestCanvas>());
     pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true, contentDrawBounds, surface);
-    EXPECT_EQ(4, canvas.mDrawCounter);
+    EXPECT_EQ(4, surface->canvas()->mDrawCounter);
 }
 
 RENDERTHREAD_TEST(SkiaPipeline, clipped) {
@@ -311,11 +309,10 @@
     LayerUpdateQueue layerUpdateQueue;
     SkRect dirty = SkRect::MakeLTRB(10, 20, 30, 40);
     auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
-    ClippedTestCanvas canvas;
-    sk_sp<SkSurface> surface(new DeferLayer<ClippedTestCanvas>(&canvas));
+    sk_sp<DeferLayer<ClippedTestCanvas>> surface(new DeferLayer<ClippedTestCanvas>());
     pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true,
             SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), surface);
-    EXPECT_EQ(1, canvas.mDrawCounter);
+    EXPECT_EQ(1, surface->canvas()->mDrawCounter);
 }
 
 RENDERTHREAD_TEST(SkiaPipeline, clip_replace) {
@@ -327,11 +324,8 @@
         }
         void onDrawPaint(const SkPaint&) {
             EXPECT_EQ(0, mDrawCounter++);
-            //TODO: this unit test is failing on the commented check below, because of a missing
-            //feature. In Snapshot::applyClip HWUI is intersecting the clip with the clip root,
-            //even for kReplace_Op clips. We need to implement the same for Skia pipelines.
-            //EXPECT_EQ(SkRect::MakeLTRB(20, 10, 30, 40), TestUtils::getClipBounds(this)) //got instead 20 0 30 50
-            //        << "Expect resolved clip to be intersection of viewport clip and clip op";
+            EXPECT_EQ(SkRect::MakeLTRB(20, 10, 30, 40), TestUtils::getClipBounds(this))
+                    << "Expect resolved clip to be intersection of viewport clip and clip op";
         }
         int mDrawCounter = 0;
     };
@@ -339,16 +333,15 @@
     std::vector<sp<RenderNode>> nodes;
     nodes.push_back(TestUtils::createSkiaNode(20, 20, 30, 30,
             [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
-        canvas.clipRect(0, -20, 10, 30, kReplace_SkClipOp);
+        canvas.clipRect(0, -20, 10, 30, SkClipOp::kReplace);
         canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
     }));
 
     LayerUpdateQueue layerUpdateQueue;
     SkRect dirty = SkRect::MakeLTRB(10, 10, 40, 40);
     auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
-    ClipReplaceTestCanvas canvas;
-    sk_sp<SkSurface> surface(new DeferLayer<ClipReplaceTestCanvas>(&canvas));
+    sk_sp<DeferLayer<ClipReplaceTestCanvas>> surface(new DeferLayer<ClipReplaceTestCanvas>());
     pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true,
             SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), surface);
-    EXPECT_EQ(1, canvas.mDrawCounter);
+    EXPECT_EQ(1, surface->canvas()->mDrawCounter);
 }
diff --git a/libs/hwui/utils/TestWindowContext.cpp b/libs/hwui/utils/TestWindowContext.cpp
index 598cd1e..8b80d69 100644
--- a/libs/hwui/utils/TestWindowContext.cpp
+++ b/libs/hwui/utils/TestWindowContext.cpp
@@ -93,7 +93,7 @@
 
     SkCanvas* prepareToDraw() {
         //mCanvas->reset(mSize.width(), mSize.height());
-        mCanvas->clipRect(0, 0, mSize.width(), mSize.height(), kReplace_SkClipOp);
+        mCanvas->clipRect(0, 0, mSize.width(), mSize.height(), SkClipOp::kReplace);
         return mCanvas->asSkCanvas();
     }
 
diff --git a/libs/incident/Android.mk b/libs/incident/Android.mk
new file mode 100644
index 0000000..439e86d
--- /dev/null
+++ b/libs/incident/Android.mk
@@ -0,0 +1,41 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libincident
+
+LOCAL_CFLAGS := \
+        -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
+
+LOCAL_SHARED_LIBRARIES := \
+        libbinder \
+        liblog \
+        libutils
+
+LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/../../core/java
+LOCAL_C_INCLUDES := \
+        $(LOCAL_PATH)/include
+
+LOCAL_SRC_FILES := \
+        ../../core/java/android/os/IIncidentManager.aidl \
+        ../../core/java/android/os/IIncidentReportCompletedListener.aidl \
+        ../../core/java/android/os/IIncidentReportStatusListener.aidl \
+        src/IncidentReportArgs.cpp
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/libs/incident/include/android/os/IncidentReportArgs.h b/libs/incident/include/android/os/IncidentReportArgs.h
new file mode 100644
index 0000000..956ef6c
--- /dev/null
+++ b/libs/incident/include/android/os/IncidentReportArgs.h
@@ -0,0 +1,62 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_OS_DUMPSTATE_ARGS_H_
+#define ANDROID_OS_DUMPSTATE_ARGS_H_
+
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <utils/String16.h>
+
+#include <set>
+#include <vector>
+
+namespace android {
+namespace os {
+
+using namespace std;
+
+class IncidentReportArgs : public Parcelable {
+public:
+    IncidentReportArgs();
+    explicit IncidentReportArgs(const IncidentReportArgs& that);
+    virtual ~IncidentReportArgs();
+
+    virtual status_t writeToParcel(Parcel* out) const;
+    virtual status_t readFromParcel(const Parcel* in);
+
+    void setAll(bool all);
+    void addSection(int section);
+    void addHeader(const vector<int8_t>& header);
+
+    inline bool all() const { return mAll; };
+    bool containsSection(int section) const;
+
+    inline const set<int>& sections() const { return mSections; }
+    inline const vector<vector<int8_t>>& headers() const { return mHeaders; }
+
+    void merge(const IncidentReportArgs& that);
+
+private:
+    set<int> mSections;
+    vector<vector<int8_t>> mHeaders;
+    bool mAll;
+};
+
+}
+}
+
+#endif // ANDROID_OS_DUMPSTATE_ARGS_H_
diff --git a/libs/incident/proto/android/privacy.proto b/libs/incident/proto/android/privacy.proto
new file mode 100644
index 0000000..ae5af0e
--- /dev/null
+++ b/libs/incident/proto/android/privacy.proto
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+syntax = "proto2";
+
+option java_package = "android";
+option java_multiple_files = true;
+
+import "google/protobuf/descriptor.proto";
+
+package android;
+
+// TODO: It's better to track this by semantic types and set policy for those.
+// Do this for now to bootstrap the tools.
+enum Destination {
+    // Fields or messages annotated with DEST_LOCAL must never be
+    // extracted from the device automatically. They can be accessed
+    // by tools on the developer's workstation, and if they are sent
+    // to another device that must be by the user, with a PII warning. (TBD)
+    DEST_LOCAL = 0;
+
+    // Fields or messages annotated with DEST_EXPLICIT can be sent
+    // off the device with an explicit user action.
+    DEST_EXPLICIT = 1;
+
+    // Fields or messages annotated with DEST_LOCAL can be sent by
+    // automatic means, without per-sending user consent. The user
+    // still must have previously accepted a consent to share this
+    // information.
+    DEST_AUTOMATIC = 2;
+
+    // There is no more permissive option than DEST_AUTOMATIC.
+}
+
+message PrivacyFlags {
+  optional Destination dest = 1  [
+      default = DEST_LOCAL
+  ];
+}
+
+extend google.protobuf.FieldOptions {
+    // Flags for automatically filtering statistics
+    optional PrivacyFlags privacy = 102672883;
+}
diff --git a/libs/incident/src/IncidentReportArgs.cpp b/libs/incident/src/IncidentReportArgs.cpp
new file mode 100644
index 0000000..f604909
--- /dev/null
+++ b/libs/incident/src/IncidentReportArgs.cpp
@@ -0,0 +1,172 @@
+/**
+ * Copyright (c) 2016, 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.
+ */
+
+#define LOG_TAG "dumpstate"
+
+#include <android/os/IncidentReportArgs.h>
+
+#include <cutils/log.h>
+
+namespace android {
+namespace os {
+
+IncidentReportArgs::IncidentReportArgs()
+    :mSections(),
+     mAll(false)
+{
+}
+
+IncidentReportArgs::IncidentReportArgs(const IncidentReportArgs& that)
+    :mSections(that.mSections),
+     mHeaders(that.mHeaders),
+     mAll(that.mAll)
+{
+}
+
+IncidentReportArgs::~IncidentReportArgs()
+{
+}
+
+status_t
+IncidentReportArgs::writeToParcel(Parcel* out) const
+{
+    status_t err;
+
+    err = out->writeInt32(mAll);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    err = out->writeInt32(mSections.size());
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    for (set<int>::const_iterator it=mSections.begin(); it!=mSections.end(); it++) {
+        err = out->writeInt32(*it);
+        if (err != NO_ERROR) {
+            return err;
+        }
+    }
+
+    err = out->writeInt32(mHeaders.size());
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    for (vector<vector<int8_t>>::const_iterator it = mHeaders.begin(); it != mHeaders.end(); it++) {
+        err = out->writeByteVector(*it);
+        if (err != NO_ERROR) {
+            return err;
+        }
+    }
+
+    return NO_ERROR;
+}
+
+status_t
+IncidentReportArgs::readFromParcel(const Parcel* in)
+{
+    status_t err;
+
+    int32_t all;
+    err = in->readInt32(&all);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    if (all != 0) {
+        mAll = all;
+    }
+
+    mSections.clear();
+    int32_t sectionCount;
+    err = in->readInt32(&sectionCount);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    for (int i=0; i<sectionCount; i++) {
+        int32_t section;
+        err = in->readInt32(&section);
+        if (err != NO_ERROR) {
+            return err;
+        }
+
+        mSections.insert(section);
+    }
+
+    int32_t headerCount;
+    err = in->readInt32(&headerCount);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    mHeaders.resize(headerCount);
+    for (int i=0; i<headerCount; i++) {
+        err = in->readByteVector(&mHeaders[i]);
+        if (err != NO_ERROR) {
+            return err;
+        }
+    }
+
+    return OK;
+}
+
+void
+IncidentReportArgs::setAll(bool all)
+{
+    mAll = all;
+    if (all) {
+        mSections.clear();
+    }
+}
+
+void
+IncidentReportArgs::addSection(int section)
+{
+    if (!mAll) {
+        mSections.insert(section);
+    }
+}
+
+void
+IncidentReportArgs::addHeader(const vector<int8_t>& header)
+{
+    mHeaders.push_back(header);
+}
+
+bool
+IncidentReportArgs::containsSection(int section) const
+{
+     return mAll || mSections.find(section) != mSections.end();
+}
+
+void
+IncidentReportArgs::merge(const IncidentReportArgs& that)
+{
+    if (mAll) {
+        return;
+    } else if (that.mAll) {
+        mAll = true;
+        mSections.clear();
+    } else {
+        for (set<int>::const_iterator it=that.mSections.begin();
+                it!=that.mSections.end(); it++) {
+            mSections.insert(*it);
+        }
+    }
+}
+
+}
+}
diff --git a/libs/services/Android.mk b/libs/services/Android.mk
new file mode 100644
index 0000000..cbfd4b3
--- /dev/null
+++ b/libs/services/Android.mk
@@ -0,0 +1,43 @@
+# Copyright (C) 2010 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+# Provides C++ wrappers for system services.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libservices
+LOCAL_SRC_FILES := \
+    ../../core/java/com/android/internal/os/IDropBoxManagerService.aidl \
+    src/os/DropBoxManager.cpp
+
+LOCAL_AIDL_INCLUDES := \
+    $(LOCAL_PATH)/../../core/java
+LOCAL_C_INCLUDES := \
+    system/core/include
+LOCAL_SHARED_LIBRARIES := \
+    libbinder \
+    liblog \
+    libcutils \
+    libutils
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
+
+LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
+
+include $(BUILD_SHARED_LIBRARY)
+
+
diff --git a/libs/services/include/android/os/DropBoxManager.h b/libs/services/include/android/os/DropBoxManager.h
new file mode 100644
index 0000000..8717178
--- /dev/null
+++ b/libs/services/include/android/os/DropBoxManager.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_OS_DROPBOXMANAGER_H
+#define _ANDROID_OS_DROPBOXMANAGER_H
+
+#include <android-base/unique_fd.h>
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <binder/Status.h>
+#include <utils/RefBase.h>
+
+#include <vector>
+
+namespace android {
+namespace os {
+
+using namespace android;
+using namespace android::base;
+using namespace android::binder;
+using namespace std;
+
+class DropBoxManager : public virtual RefBase
+{
+public:
+    enum {
+        IS_EMPTY = 1,
+        IS_TEXT = 2,
+        IS_GZIPPED = 4
+    };
+
+    DropBoxManager();
+    virtual ~DropBoxManager();
+
+    static sp<DropBoxManager> create();
+
+    // Create a new entry with plain text contents.
+    Status addText(const String16& tag, const string& text);
+
+    // Create a new Entry with byte array contents. Makes a copy of the data.
+    Status addData(const String16& tag, uint8_t const* data, size_t size, int flags);
+
+    // Create a new Entry from a file. The file will be opened in this process
+    // and a handle will be passed to the system process, so no additional permissions
+    // are required from the system process.  Returns NULL if the file can't be opened.
+    Status addFile(const String16& tag, const string& filename, int flags);
+    
+    class Entry : public virtual RefBase, public Parcelable {
+    public:
+        Entry();
+        virtual ~Entry();
+
+        virtual status_t writeToParcel(Parcel* out) const;
+        virtual status_t readFromParcel(const Parcel* in);
+        
+    private:
+        Entry(const String16& tag, int32_t flags);
+        Entry(const String16& tag, int32_t flags, int fd);
+
+        String16 mTag;
+        int64_t mTimeMillis;
+        int32_t mFlags;
+
+        vector<uint8_t> mData;
+        unique_fd mFd;
+
+        friend class DropBoxManager;
+    };
+
+private:
+    enum {
+        HAS_BYTE_ARRAY = 8
+    };
+
+    Status add(const Entry& entry);
+};
+
+}} // namespace android::os
+
+#endif // _ANDROID_OS_DROPBOXMANAGER_H
+
diff --git a/libs/services/src/os/DropBoxManager.cpp b/libs/services/src/os/DropBoxManager.cpp
new file mode 100644
index 0000000..bbb45f0
--- /dev/null
+++ b/libs/services/src/os/DropBoxManager.cpp
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#define LOG_TAG "DropBoxManager"
+
+#include <android/os/DropBoxManager.h>
+
+#include <binder/IServiceManager.h>
+#include <com/android/internal/os/IDropBoxManagerService.h>
+#include <cutils/log.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+namespace android {
+namespace os {
+
+using namespace ::com::android::internal::os;
+
+DropBoxManager::Entry::Entry()
+    :mTag(),
+     mTimeMillis(0),
+     mFlags(IS_EMPTY),
+     mData(),
+     mFd()
+{
+    mFlags = IS_EMPTY;
+}
+
+DropBoxManager::Entry::Entry(const String16& tag, int32_t flags)
+    :mTag(tag),
+     mTimeMillis(0),
+     mFlags(flags),
+     mData(),
+     mFd()
+{
+}
+
+DropBoxManager::Entry::Entry(const String16& tag, int32_t flags, int fd)
+    :mTag(tag),
+     mTimeMillis(0),
+     mFlags(flags),
+     mData(),
+     mFd(fd)
+{
+}
+
+DropBoxManager::Entry::~Entry()
+{
+}
+
+status_t
+DropBoxManager::Entry::writeToParcel(Parcel* out) const
+{
+    status_t err;
+
+    err = out->writeString16(mTag);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    err = out->writeInt64(mTimeMillis);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    if (mFd.get() != -1) {
+        err = out->writeInt32(mFlags & ~HAS_BYTE_ARRAY);  // Clear bit just to be safe
+        if (err != NO_ERROR) {
+            return err;
+        }
+        ALOGD("writing fd %d\n", mFd.get());
+        err = out->writeParcelFileDescriptor(mFd);
+        if (err != NO_ERROR) {
+            return err;
+        }
+    } else {
+        err = out->writeInt32(mFlags | HAS_BYTE_ARRAY);
+        if (err != NO_ERROR) {
+            return err;
+        }
+        err = out->writeByteVector(mData);
+        if (err != NO_ERROR) {
+            return err;
+        }
+    }
+    return NO_ERROR;
+}
+
+status_t
+DropBoxManager::Entry::readFromParcel(const Parcel* in)
+{
+    status_t err;
+
+    err = in->readString16(&mTag);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    err = in->readInt64(&mTimeMillis);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    err = in->readInt32(&mFlags);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    if ((mFlags & HAS_BYTE_ARRAY) != 0) {
+        err = in->readByteVector(&mData);
+        if (err != NO_ERROR) {
+            return err;
+        }
+        mFlags &= ~HAS_BYTE_ARRAY;
+    } else {
+        int fd;
+        fd = in->readParcelFileDescriptor();
+        if (fd == -1) {
+            return EBADF;
+        }
+        fd = dup(fd);
+        if (fd == -1) {
+            return errno;
+        }
+        mFd.reset(fd);
+    }
+
+    return NO_ERROR;
+}
+
+
+DropBoxManager::DropBoxManager()
+{
+}
+
+DropBoxManager::~DropBoxManager()
+{
+}
+
+Status
+DropBoxManager::addText(const String16& tag, const string& text)
+{
+    Entry entry(tag, IS_TEXT);
+    entry.mData.assign(text.c_str(), text.c_str() + text.size());
+    return add(entry);
+}
+
+Status
+DropBoxManager::addData(const String16& tag, uint8_t const* data,
+        size_t size, int flags)
+{
+    Entry entry(tag, flags);
+    entry.mData.assign(data, data+size);
+    return add(entry);
+}
+
+Status
+DropBoxManager::addFile(const String16& tag, const string& filename, int flags)
+{
+    int fd = open(filename.c_str(), O_RDONLY);
+    if (fd == -1) {
+        string message("addFile can't open file: ");
+        message += filename;
+        ALOGW("DropboxManager: %s", message.c_str());
+        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, message.c_str());
+    }
+
+    Entry entry(tag, flags, fd);
+    return add(entry);
+}
+
+Status
+DropBoxManager::add(const Entry& entry)
+{
+    sp<IDropBoxManagerService> service = interface_cast<IDropBoxManagerService>(
+        defaultServiceManager()->getService(android::String16("dropbox")));
+    if (service == NULL) {
+        return Status::fromExceptionCode(Status::EX_NULL_POINTER, "can't find dropbox service");
+    }
+    return service->add(entry);
+}
+
+}} // namespace android::os
+
diff --git a/libs/storage/Android.bp b/libs/storage/Android.bp
new file mode 100644
index 0000000..911bd1d
--- /dev/null
+++ b/libs/storage/Android.bp
@@ -0,0 +1,19 @@
+cc_library_static {
+    name: "libstorage",
+
+    srcs: [
+        "IMountServiceListener.cpp",
+        "IMountShutdownObserver.cpp",
+        "IObbActionListener.cpp",
+        "IMountService.cpp",
+    ],
+
+    export_include_dirs: ["include"],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    shared_libs: ["libbinder"],
+}
diff --git a/libs/storage/Android.mk b/libs/storage/Android.mk
deleted file mode 100644
index d0eb6d4..0000000
--- a/libs/storage/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-	IMountServiceListener.cpp \
-	IMountShutdownObserver.cpp \
-	IObbActionListener.cpp \
-	IMountService.cpp
-
-LOCAL_MODULE:= libstorage
-
-LOCAL_CFLAGS += -Wall -Werror
-
-LOCAL_SHARED_LIBRARIES := libbinder
-
-include $(BUILD_STATIC_LIBRARY)
diff --git a/include/storage/IMountService.h b/libs/storage/include/storage/IMountService.h
similarity index 100%
rename from include/storage/IMountService.h
rename to libs/storage/include/storage/IMountService.h
diff --git a/include/storage/IMountServiceListener.h b/libs/storage/include/storage/IMountServiceListener.h
similarity index 100%
rename from include/storage/IMountServiceListener.h
rename to libs/storage/include/storage/IMountServiceListener.h
diff --git a/include/storage/IMountShutdownObserver.h b/libs/storage/include/storage/IMountShutdownObserver.h
similarity index 100%
rename from include/storage/IMountShutdownObserver.h
rename to libs/storage/include/storage/IMountShutdownObserver.h
diff --git a/include/storage/IObbActionListener.h b/libs/storage/include/storage/IObbActionListener.h
similarity index 100%
rename from include/storage/IObbActionListener.h
rename to libs/storage/include/storage/IObbActionListener.h
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index 957c2d6..81db37e 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -109,13 +109,13 @@
     private IMediaBrowserServiceCallbacks mServiceCallbacks;
 
     /**
-     * Creates a media browser for the specified media browse service.
+     * Creates a media browser for the specified media browser service.
      *
      * @param context The context.
-     * @param serviceComponent The component name of the media browse service.
+     * @param serviceComponent The component name of the media browser service.
      * @param callback The connection callback.
      * @param rootHints An optional bundle of service-specific arguments to send
-     * to the media browse service when connecting and retrieving the root id
+     * to the media browser service when connecting and retrieving the root id
      * for browsing, or null if none. The contents of this bundle may affect
      * the information returned when browsing.
      * @see android.service.media.MediaBrowserService.BrowserRoot#EXTRA_RECENT
@@ -140,7 +140,7 @@
     }
 
     /**
-     * Connects to the media browse service.
+     * Connects to the media browser service.
      * <p>
      * The connection callback specified in the constructor will be invoked
      * when the connection completes or fails.
@@ -206,7 +206,7 @@
     }
 
     /**
-     * Disconnects from the media browse service.
+     * Disconnects from the media browser service.
      * After this, no more callbacks will be received.
      */
     public void disconnect() {
@@ -362,7 +362,7 @@
      *
      * @param parentId The id of the parent media item whose list of children
      *            will be subscribed.
-     * @param options A bundle of service-specific arguments to send to the media
+     * @param options The bundle of service-specific arguments to send to the media
      *            browse service. The contents of this bundle may affect the
      *            information returned when browsing.
      * @param callback The callback to receive the list of children.
@@ -370,7 +370,7 @@
     public void subscribe(@NonNull String parentId, @NonNull Bundle options,
             @NonNull SubscriptionCallback callback) {
         if (options == null) {
-            throw new IllegalArgumentException("options are null");
+            throw new IllegalArgumentException("options cannot be null");
         }
         subscribeInternal(parentId, new Bundle(options), callback);
     }
@@ -398,11 +398,11 @@
      *
      * @param parentId The id of the parent media item whose list of children
      *            will be unsubscribed.
-     * @param callback A callback sent to the media browse service to subscribe.
+     * @param callback A callback sent to the media browser service to subscribe.
      */
     public void unsubscribe(@NonNull String parentId, @NonNull SubscriptionCallback callback) {
         if (callback == null) {
-            throw new IllegalArgumentException("callback is null");
+            throw new IllegalArgumentException("callback cannot be null");
         }
         unsubscribeInternal(parentId, callback);
     }
@@ -417,10 +417,10 @@
      */
     public void getItem(final @NonNull String mediaId, @NonNull final ItemCallback cb) {
         if (TextUtils.isEmpty(mediaId)) {
-            throw new IllegalArgumentException("mediaId is empty.");
+            throw new IllegalArgumentException("mediaId cannot be empty.");
         }
         if (cb == null) {
-            throw new IllegalArgumentException("cb is null.");
+            throw new IllegalArgumentException("cb cannot be null.");
         }
         if (mState != CONNECT_STATE_CONNECTED) {
             Log.i(TAG, "Not connected, unable to retrieve the MediaItem.");
@@ -451,7 +451,7 @@
         try {
             mServiceBinder.getMediaItem(mediaId, receiver, mServiceCallbacks);
         } catch (RemoteException e) {
-            Log.i(TAG, "Remote error getting media item.");
+            Log.i(TAG, "Remote error getting media item.", e);
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
@@ -461,13 +461,74 @@
         }
     }
 
+    /**
+     * Searches {@link MediaItem media items} from the connected service. Not
+     * all services may support this, and {@link SearchCallback#onError} will be
+     * called if not implemented.
+     *
+     * @param query The search query that contains keywords separated by space. Should not be
+     *            an empty string.
+     * @param extras The bundle of service-specific arguments to send to the media browser
+     *            service. The contents of this bundle may affect the search result.
+     * @param callback The callback to receive the search result.
+     */
+    public void search(@NonNull final String query, final Bundle extras, SearchCallback callback) {
+        if (TextUtils.isEmpty(query)) {
+            throw new IllegalArgumentException("query cannot be empty.");
+        }
+        if (callback == null) {
+            throw new IllegalArgumentException("callback cannot be null.");
+        }
+        if (mState != CONNECT_STATE_CONNECTED) {
+            Log.i(TAG, "Not connected, unable to search.");
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    callback.onError(query, extras);
+                }
+            });
+            return;
+        }
+        ResultReceiver receiver = new ResultReceiver(mHandler) {
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                if (resultCode != 0 || resultData == null
+                        || !resultData.containsKey(MediaBrowserService.KEY_SEARCH_RESULTS)) {
+                    callback.onError(query, extras);
+                    return;
+                }
+                Parcelable[] items = resultData.getParcelableArray(
+                        MediaBrowserService.KEY_SEARCH_RESULTS);
+                List<MediaItem> results = null;
+                if (items != null) {
+                    results = new ArrayList<>();
+                    for (Parcelable item : items) {
+                        results.add((MediaItem) item);
+                    }
+                }
+                callback.onSearchResult(query, extras, results);
+            }
+        };
+        try {
+            mServiceBinder.search(query, extras, receiver, mServiceCallbacks);
+        } catch (RemoteException e) {
+            Log.i(TAG, "Remote error getting media item.", e);
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    callback.onError(query, extras);
+                }
+            });
+        }
+    }
+
     private void subscribeInternal(String parentId, Bundle options, SubscriptionCallback callback) {
         // Check arguments.
         if (TextUtils.isEmpty(parentId)) {
-            throw new IllegalArgumentException("parentId is empty.");
+            throw new IllegalArgumentException("parentId cannot be empty.");
         }
         if (callback == null) {
-            throw new IllegalArgumentException("callback is null");
+            throw new IllegalArgumentException("callback cannot be null");
         }
         // Update or create the subscription.
         Subscription sub = mSubscriptions.get(parentId);
@@ -497,7 +558,7 @@
     private void unsubscribeInternal(String parentId, SubscriptionCallback callback) {
         // Check arguments.
         if (TextUtils.isEmpty(parentId)) {
-            throw new IllegalArgumentException("parentId is empty.");
+            throw new IllegalArgumentException("parentId cannot be empty.");
         }
 
         Subscription sub = mSubscriptions.get(parentId);
@@ -712,7 +773,9 @@
     }
 
     /**
-     * A class with information on a single media item for use in browsing media.
+     * A class with information on a single media item for use in browsing/searching media.
+     * MediaItems are application dependent so we cannot guarantee that they contain the
+     * right values.
      */
     public static class MediaItem implements Parcelable {
         private final int mFlags;
@@ -830,7 +893,7 @@
          * Returns the media id in the {@link MediaDescription} for this item.
          * @see android.media.MediaMetadata#METADATA_KEY_MEDIA_ID
          */
-        public @NonNull String getMediaId() {
+        public @Nullable String getMediaId() {
             return mDescription.getMediaId();
         }
     }
@@ -882,7 +945,7 @@
          *
          * @param parentId The media id of the parent media item.
          * @param children The children which were loaded.
-         * @param options A bundle of service-specific arguments sent to the media
+         * @param options The bundle of service-specific arguments sent to the media
          *            browse service. The contents of this bundle may affect the
          *            information returned when browsing.
          */
@@ -912,8 +975,8 @@
          *
          * @param parentId The media id of the parent media item whose children could
          *            not be loaded.
-         * @param options A bundle of service-specific arguments sent to the media
-         *            browse service.
+         * @param options The bundle of service-specific arguments sent to the media
+         *            browser service.
          */
         public void onError(@NonNull String parentId, @NonNull Bundle options) {
         }
@@ -924,7 +987,7 @@
      */
     public static abstract class ItemCallback {
         /**
-         * Called when the item has been returned by the browser service.
+         * Called when the item has been returned by the connected service.
          *
          * @param item The item that was returned or null if it doesn't exist.
          */
@@ -932,11 +995,38 @@
         }
 
         /**
-         * Called when the item doesn't exist or there was an error retrieving it.
+         * Called there was an error retrieving it or the connected service doesn't support
+         * {@link #getItem}.
          *
-         * @param itemId The media id of the media item which could not be loaded.
+         * @param mediaId The media id of the media item which could not be loaded.
          */
-        public void onError(@NonNull String itemId) {
+        public void onError(@NonNull String mediaId) {
+        }
+    }
+
+    /**
+     * Callback for receiving the result of {@link #search}.
+     */
+    public static abstract class SearchCallback {
+        /**
+         * Called when the {@link #search} finished successfully.
+         *
+         * @param query The search query sent for the search request to the connected service.
+         * @param extras The bundle of service-specific arguments sent to the connected service.
+         * @param items The list of media items which contains the search result.
+         */
+        public void onSearchResult(@NonNull String query, Bundle extras,
+                @NonNull List<MediaItem> items) {
+        }
+
+        /**
+         * Called when an error happens while {@link #search} or the connected service doesn't
+         * support {@link #search}.
+         *
+         * @param query The search query sent for the search request to the connected service.
+         * @param extras The bundle of service-specific arguments sent to the connected service.
+         */
+        public void onError(@NonNull String query, Bundle extras) {
         }
     }
 
diff --git a/media/java/android/service/media/IMediaBrowserService.aidl b/media/java/android/service/media/IMediaBrowserService.aidl
index 84f41f6..e95154f 100644
--- a/media/java/android/service/media/IMediaBrowserService.aidl
+++ b/media/java/android/service/media/IMediaBrowserService.aidl
@@ -19,8 +19,10 @@
 
     void addSubscriptionDeprecated(String uri, IMediaBrowserServiceCallbacks callbacks);
     void removeSubscriptionDeprecated(String uri, IMediaBrowserServiceCallbacks callbacks);
-
     void getMediaItem(String uri, in ResultReceiver cb, IMediaBrowserServiceCallbacks callbacks);
+    void search(String query, in Bundle extras, in ResultReceiver cb,
+            IMediaBrowserServiceCallbacks callbacks);
+
     void addSubscription(String uri, in IBinder token, in Bundle options,
             IMediaBrowserServiceCallbacks callbacks);
     void removeSubscription(String uri, in IBinder token, IMediaBrowserServiceCallbacks callbacks);
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index a19e347..16847c1 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -52,9 +52,9 @@
 import java.util.List;
 
 /**
- * Base class for media browse services.
+ * Base class for media browser services.
  * <p>
- * Media browse services enable applications to browse media content provided by an application
+ * Media browser services enable applications to browse media content provided by an application
  * and ask the application to start playing it. They may also be used to control content that
  * is already playing by way of a {@link MediaSession}.
  * </p>
@@ -85,18 +85,27 @@
 
     /**
      * A key for passing the MediaItem to the ResultReceiver in getItem.
-     *
      * @hide
      */
     public static final String KEY_MEDIA_ITEM = "media_item";
 
-    private static final int RESULT_FLAG_OPTION_NOT_HANDLED = 0x00000001;
-    private static final int RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED = 0x00000002;
+    /**
+     * A key for passing the list of MediaItems to the ResultReceiver in search.
+     * @hide
+     */
+    public static final String KEY_SEARCH_RESULTS = "search_results";
+
+    private static final int RESULT_FLAG_OPTION_NOT_HANDLED = 1 << 0;
+    private static final int RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED = 1 << 1;
+    private static final int RESULT_FLAG_ON_SEARCH_NOT_IMPLEMENTED = 1 << 2;
+
+    private static final int RESULT_ERROR = -1;
+    private static final int RESULT_OK = 0;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag=true, value = { RESULT_FLAG_OPTION_NOT_HANDLED,
-            RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED })
+            RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED, RESULT_FLAG_ON_SEARCH_NOT_IMPLEMENTED })
     private @interface ResultFlags { }
 
     private final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap<>();
@@ -307,10 +316,6 @@
         @Override
         public void getMediaItem(final String mediaId, final ResultReceiver receiver,
                 final IMediaBrowserServiceCallbacks callbacks) {
-            if (TextUtils.isEmpty(mediaId) || receiver == null) {
-                return;
-            }
-
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
@@ -324,6 +329,23 @@
                 }
             });
         }
+
+        @Override
+        public void search(final String query, Bundle extras, ResultReceiver receiver,
+                final IMediaBrowserServiceCallbacks callbacks) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    final IBinder b = callbacks.asBinder();
+                    ConnectionRecord connection = mConnections.get(b);
+                    if (connection == null) {
+                        Log.w(TAG, "search for callback that isn't registered query=" + query);
+                        return;
+                    }
+                    performSearch(query, extras, connection, receiver);
+                }
+            });
+        }
     }
 
     @Override
@@ -358,7 +380,7 @@
      * @param clientUid The uid of the application which is requesting access to
      *            browse media.
      * @param rootHints An optional bundle of service-specific arguments to send
-     *            to the media browse service when connecting and retrieving the
+     *            to the media browser service when connecting and retrieving the
      *            root id for browsing, or null if none. The contents of this
      *            bundle may affect the information returned when browsing.
      * @return The {@link BrowserRoot} for accessing this app's content or null.
@@ -412,8 +434,8 @@
      * @param parentId The id of the parent media item whose children are to be
      *            queried.
      * @param result The Result to send the list of children to.
-     * @param options A bundle of service-specific arguments sent from the media
-     *            browse. The information returned through the result should be
+     * @param options The bundle of service-specific arguments sent from the media
+     *            browser. The information returned through the result should be
      *            affected by the contents of this bundle.
      */
     public void onLoadChildren(@NonNull String parentId,
@@ -450,6 +472,32 @@
     }
 
     /**
+     * Called to get the search result.
+     * <p>
+     * Implementations must call {@link Result#sendResult result.sendResult}. If
+     * the search will be an expensive operation {@link Result#detach result.detach}
+     * may be called before returning from this function, and then {@link Result#sendResult
+     * result.sendResult} called when the search has been completed.
+     * </p><p>
+     * In case there are no search results, call {@link Result#sendResult} with an empty list.
+     * In case there are some errors happened, call {@link Result#sendResult result.sendResult}
+     * with {@code null}, which will invoke {@link MediaBrowser.SearchCallback#onError}.
+     * </p><p>
+     * The default implementation will invoke {@link MediaBrowser.SearchCallback#onError}.
+     * </p>
+     *
+     * @param query The search query sent from the media browser. It contains keywords separated
+     *            by space.
+     * @param extras The bundle of service-specific arguments sent from the media browser.
+     * @param result The {@link Result} to send the search result.
+     */
+    public void onSearch(@NonNull String query, Bundle extras,
+            Result<List<MediaBrowser.MediaItem>> result) {
+        result.setFlags(RESULT_FLAG_ON_SEARCH_NOT_IMPLEMENTED);
+        result.sendResult(null);
+    }
+
+    /**
      * Call to set the media session.
      * <p>
      * This should be called as soon as possible during the service's startup.
@@ -531,8 +579,8 @@
      *
      * @param parentId The id of the parent media item whose
      *            children changed.
-     * @param options A bundle of service-specific arguments to send
-     *            to the media browse. The contents of this bundle may
+     * @param options The bundle of service-specific arguments to send
+     *            to the media browser. The contents of this bundle may
      *            contain the information about the change.
      */
     public void notifyChildrenChanged(@NonNull String parentId, @NonNull Bundle options) {
@@ -705,12 +753,12 @@
             @Override
             void onResultSent(MediaBrowser.MediaItem item, @ResultFlags int flag) {
                 if ((flag & RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED) != 0) {
-                    receiver.send(-1, null);
+                    receiver.send(RESULT_ERROR, null);
                     return;
                 }
                 Bundle bundle = new Bundle();
                 bundle.putParcelable(KEY_MEDIA_ITEM, item);
-                receiver.send(0, bundle);
+                receiver.send(RESULT_OK, bundle);
             }
         };
 
@@ -724,6 +772,34 @@
         }
     }
 
+    private void performSearch(String query, Bundle extras, final ConnectionRecord connection,
+            final ResultReceiver receiver) {
+        final Result<List<MediaBrowser.MediaItem>> result =
+                new Result<List<MediaBrowser.MediaItem>>(query) {
+            @Override
+            void onResultSent(List<MediaBrowser.MediaItem> items, @ResultFlags int flag) {
+                if ((flag & RESULT_FLAG_ON_SEARCH_NOT_IMPLEMENTED) != 0
+                        || items == null) {
+                    receiver.send(RESULT_ERROR, null);
+                    return;
+                }
+                Bundle bundle = new Bundle();
+                bundle.putParcelableArray(KEY_SEARCH_RESULTS,
+                        items.toArray(new MediaBrowser.MediaItem[0]));
+                receiver.send(RESULT_OK, bundle);
+            }
+        };
+
+        mCurConnection = connection;
+        onSearch(query, extras, result);
+        mCurConnection = null;
+
+        if (!result.isDone()) {
+            throw new IllegalStateException("onSearch must call detach() or sendResult()"
+                    + " before returning for query=" + query);
+        }
+    }
+
     /**
      * Contains information that the browser service needs to send to the client
      * when first connected.
diff --git a/media/mca/filterfw/native/core/shader_program.cpp b/media/mca/filterfw/native/core/shader_program.cpp
index 1e573fb..d460512 100644
--- a/media/mca/filterfw/native/core/shader_program.cpp
+++ b/media/mca/filterfw/native/core/shader_program.cpp
@@ -1028,7 +1028,11 @@
   attrib.values = data_cpy;
   attrib.owned_data = data_cpy; // Marks this for deletion later on
 
-  return StoreAttribute(attrib);
+  if (StoreAttribute(attrib))
+    return true;
+  // If storing this failed, then it won't be deleted on its own.
+  delete[] data_cpy;
+  return false;
 }
 
 bool ShaderProgram::StoreAttribute(VertexAttrib attrib) {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkIntegrationTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkIntegrationTestRunner.java
index 7751fcc..6511cc8 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkIntegrationTestRunner.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkIntegrationTestRunner.java
@@ -38,7 +38,7 @@
 
     private static final String TAG = "MediaFrameworkIntegrationTestRunner";
 
-    public static int mCameraId = 0;
+    public static String mCameraId = "0";
 
     @Override
     public TestSuite getAllTests() {
@@ -62,7 +62,7 @@
             try {
                 Log.v(TAG,
                         String.format("Reading camera_id from icicle: '%s'", cameraId));
-                mCameraId = Integer.parseInt(cameraId);
+                mCameraId = cameraId;
             }
             catch (NumberFormatException e) {
                 Log.e(TAG, String.format("Failed to convert camera_id to integer"));
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 4f2fdff..fcb861c 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
@@ -116,8 +116,8 @@
     @SmallTest
     public void testSupportsCamera2Api() throws Exception {
         for (int cameraId = 0; cameraId < mUtils.getGuessedNumCameras(); ++cameraId) {
-
-            boolean supports = mUtils.getCameraService().supportsCameraApi(cameraId, API_VERSION_2);
+            boolean supports = mUtils.getCameraService().supportsCameraApi(
+                String.valueOf(cameraId), API_VERSION_2);
 
             Log.v(TAG, "Camera " + cameraId + " supports api2: " + supports);
         }
@@ -128,7 +128,8 @@
     public void testSupportsCamera1Api() throws Exception {
         for (int cameraId = 0; cameraId < mUtils.getGuessedNumCameras(); ++cameraId) {
 
-            boolean supports = mUtils.getCameraService().supportsCameraApi(cameraId, API_VERSION_1);
+            boolean supports = mUtils.getCameraService().supportsCameraApi(
+                String.valueOf(cameraId), API_VERSION_1);
             assertTrue(
                     "Camera service returned false when queried if it supports camera1 api " +
                     " for camera ID " + cameraId, supports);
@@ -285,7 +286,7 @@
 
             ICameraDeviceUser cameraUser =
                     mUtils.getCameraService().connectDevice(
-                        dummyCallbacks, cameraId,
+                        dummyCallbacks, String.valueOf(cameraId),
                         clientPackageName,
                         ICameraService.USE_CALLING_UID);
             assertNotNull(String.format("Camera %s was null", cameraId), cameraUser);
@@ -298,9 +299,9 @@
 
     static class DummyCameraServiceListener extends ICameraServiceListener.Stub {
         @Override
-        public void onStatusChanged(int status, int cameraId)
+        public void onStatusChanged(int status, String cameraId)
                 throws RemoteException {
-            Log.v(TAG, String.format("Camera %d has status changed to 0x%x", cameraId, status));
+            Log.v(TAG, String.format("Camera %s has status changed to 0x%x", cameraId, status));
         }
         public void onTorchStatusChanged(int status, String cameraId)
                 throws RemoteException {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
index 832363c..9a78544 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -66,7 +66,7 @@
     private static final int DEFAULT_IMAGE_HEIGHT = 480;
     private static final int MAX_NUM_IMAGES = 5;
 
-    private int mCameraId;
+    private String mCameraId;
     private ICameraDeviceUser mCameraUser;
     private CameraBinderTestUtils mUtils;
     private ICameraDeviceCallbacks.Stub mMockCb;
diff --git a/packages/BackupRestoreConfirmation/res/values-si-rLK/strings.xml b/packages/BackupRestoreConfirmation/res/values-si-rLK/strings.xml
index aaeaf04..cbc130d 100644
--- a/packages/BackupRestoreConfirmation/res/values-si-rLK/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-si-rLK/strings.xml
@@ -29,7 +29,7 @@
     <string name="device_encryption_backup_text" msgid="5866590762672844664">"කරුණාකර ඔබගේ උපාංගයේ සංකේතන මුරපදය පහත ඇතුලත් කරන්න. සංරක්ෂිත උපස්ථ සංකේතනය කිරීමට මෙය භාවිත කළ හැක."</string>
     <string name="backup_enc_password_text" msgid="4981585714795233099">"කරුණාකර සියලු උපස්ථ දත්ත සංකේතනය කිරීම සඳහා භාවිතයට මුරපදයක් ඇතුළත් කරන්න. මෙය හිස්ව තැබුවොත්, ඔබගේ වර්තමාන උපස්ථ මුරපදය භාවිත වෙයි:"</string>
     <string name="backup_enc_password_optional" msgid="1350137345907579306">"සියලු උපස්ථ දත්ත සංකේතනය කිරීමට ඔබ අදහස් කරන්නේ නම්, මුරපදය පහලින් ඇතුලත් කරන්න:"</string>
-    <string name="backup_enc_password_required" msgid="7889652203371654149">"ඔබගේ උපාංගය සංකේතනය කර තිබෙන නිසා, ඔබගේ උපස්ථය සංකේතනය ඔබට අවශ්‍ය වී තිබේ. කරුණාකර මුරපදය පහළින් එකතු කරන්න:"</string>
+    <string name="backup_enc_password_required" msgid="7889652203371654149">"ඔබගේ උපාංගය සංකේතනය කර තිබෙන නිසා, ඔබගේ උපස්ථය සංකේතනය ඔබට අවශ්‍ය වී තිබේ. මුරපදය පහළින් එක් කරන්න:"</string>
     <string name="restore_enc_password_text" msgid="6140898525580710823">"යළි පිහිටුවන දත්ත සංකේතනය කරන ලද ඒවානම්, කරුණාකර මුරපදය පහලින් ඇතුල් කරන්න:"</string>
     <string name="toast_backup_started" msgid="550354281452756121">"උපස්ථ කිරීම ආරම්භ කරමින්..."</string>
     <string name="toast_backup_ended" msgid="3818080769548726424">"උපස්ථය අවසන්"</string>
diff --git a/packages/PrintSpooler/res/values-si-rLK/strings.xml b/packages/PrintSpooler/res/values-si-rLK/strings.xml
index ffff8e0..e661f93 100644
--- a/packages/PrintSpooler/res/values-si-rLK/strings.xml
+++ b/packages/PrintSpooler/res/values-si-rLK/strings.xml
@@ -52,7 +52,7 @@
     <string name="add_print_service_label" msgid="5356702546188981940">"සේවාව එක් කිරීම"</string>
     <string name="print_search_box_shown_utterance" msgid="7967404953901376090">"සෙවීම් කොටුව පෙන්වන ලදී"</string>
     <string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"සෙවීම් කොටුව සඟවන ලදී"</string>
-    <string name="print_add_printer" msgid="1088656468360653455">"මුද්‍රණ යන්ත්‍ර එකතු කරන්න"</string>
+    <string name="print_add_printer" msgid="1088656468360653455">"මුද්‍රණ යන්ත්‍ර එක් කරන්න"</string>
     <string name="print_select_printer" msgid="7388760939873368698">"මුද්‍රකය තේරීම"</string>
     <string name="print_forget_printer" msgid="5035287497291910766">"මුද්‍රකය අමතක කිරීම"</string>
     <plurals name="print_search_result_count_utterance" formatted="false" msgid="6997663738361080868">
diff --git a/packages/SettingsLib/res/drawable/ic_menu.xml b/packages/SettingsLib/res/drawable/ic_menu.xml
index 910a3d0..b77db08 100644
--- a/packages/SettingsLib/res/drawable/ic_menu.xml
+++ b/packages/SettingsLib/res/drawable/ic_menu.xml
@@ -17,7 +17,8 @@
         android:width="24.0dp"
         android:height="24.0dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <path
         android:fillColor="#FFFFFFFF"
         android:pathData="M3.0,18.0l18.0,0.0l0.0,-2.0L3.0,16.0l0.0,2.0zm0.0,-5.0l18.0,0.0l0.0,-2.0L3.0,11.0l0.0,2.0zm0.0,-7.0l0.0,2.0l18.0,0.0L21.0,6.0L3.0,6.0z"/>
diff --git a/packages/SettingsLib/src/com/android/settingslib/widget/AnimatedImageView.java b/packages/SettingsLib/src/com/android/settingslib/widget/AnimatedImageView.java
index f5e39be..a8cd655 100644
--- a/packages/SettingsLib/src/com/android/settingslib/widget/AnimatedImageView.java
+++ b/packages/SettingsLib/src/com/android/settingslib/widget/AnimatedImageView.java
@@ -55,7 +55,7 @@
 
     private void updateAnimating() {
         if (mDrawable != null) {
-            if (isShown() && mAnimating) {
+            if (getVisibility() == View.VISIBLE && mAnimating) {
                 mDrawable.start();
             } else {
                 mDrawable.stop();
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index aee6c3c..e7450fa 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -288,7 +288,7 @@
 
     public boolean matches(WifiConfiguration config) {
         if (config.isPasspoint() && mConfig != null && mConfig.isPasspoint()) {
-            return config.FQDN.equals(mConfig.FQDN);
+            return ssid.equals(removeDoubleQuotes(config.SSID)) && config.FQDN.equals(mConfig.FQDN);
         } else {
             return ssid.equals(removeDoubleQuotes(config.SSID))
                     && security == getSecurity(config)
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java
new file mode 100644
index 0000000..2c9c868
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AnimatedImageViewTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 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.settingslib.widget;
+
+import android.app.Activity;
+import android.graphics.drawable.AnimatedRotateDrawable;
+import android.view.View;
+import com.android.settingslib.TestConfig;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import static com.google.common.truth.Truth.assertThat;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class AnimatedImageViewTest {
+    private AnimatedImageView mAnimatedImageView;
+
+    @Before
+    public void setUp() {
+        Activity activity = Robolectric.setupActivity(Activity.class);
+        mAnimatedImageView = new AnimatedImageView(activity);
+        mAnimatedImageView.setImageDrawable(new AnimatedRotateDrawable());
+    }
+
+    @Test
+    public void testAnimation_ViewVisible_AnimationRunning() {
+        mAnimatedImageView.setVisibility(View.VISIBLE);
+        mAnimatedImageView.setAnimating(true);
+        AnimatedRotateDrawable drawable = (AnimatedRotateDrawable) mAnimatedImageView.getDrawable();
+        assertThat(drawable.isRunning()).isTrue();
+    }
+
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 88ee33c..c149876 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -57,6 +57,7 @@
 import android.os.UserManager;
 import android.os.UserManagerInternal;
 import android.provider.Settings;
+import android.provider.Settings.Global;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -2175,7 +2176,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 135;
+            private static final int SETTINGS_VERSION = 136;
 
             private final int mUserId;
 
@@ -2553,6 +2554,30 @@
                     currentVersion = 135;
                 }
 
+                if (currentVersion == 135) {
+                    // Version 135: Migrating the NETWORK_SCORER_APP setting to the
+                    // NETWORK_RECOMMENDATIONS_ENABLED setting.
+                    if (userId == UserHandle.USER_SYSTEM) {
+                        final SettingsState globalSettings = getGlobalSettingsLocked();
+                        Setting currentSetting = globalSettings.getSettingLocked(
+                            Global.NETWORK_SCORER_APP);
+                        if (!currentSetting.isNull()) {
+                            // A scorer was set so enable recommendations.
+                            globalSettings.insertSettingLocked(
+                                Global.NETWORK_RECOMMENDATIONS_ENABLED,
+                                "1",
+                                SettingsState.SYSTEM_PACKAGE_NAME);
+
+                            // and clear the scorer setting since it's no longer needed.
+                            globalSettings.insertSettingLocked(
+                                Global.NETWORK_SCORER_APP,
+                                null,
+                                SettingsState.SYSTEM_PACKAGE_NAME);
+                        }
+                    }
+                    currentVersion = 136;
+                }
+
                 if (currentVersion != newVersion) {
                     Slog.wtf("SettingsProvider", "warning: upgrading settings database to version "
                             + newVersion + " left it at "
diff --git a/packages/SystemUI/res/drawable/major_a_b.xml b/packages/SystemUI/res/drawable/major_a_b.xml
index 9900048..45386f9 100644
--- a/packages/SystemUI/res/drawable/major_a_b.xml
+++ b/packages/SystemUI/res/drawable/major_a_b.xml
@@ -20,7 +20,8 @@
     android:width="16dp"
     android:viewportWidth="16"
     android:height="8dp"
-    android:viewportHeight="8" >
+    android:viewportHeight="8"
+    android:tint="?android:attr/colorControlNormal">
     <group
         android:name="dot_01"
         android:translateX="3.25"
diff --git a/packages/SystemUI/res/drawable/major_b_a.xml b/packages/SystemUI/res/drawable/major_b_a.xml
index 3115887..4fcff0c 100644
--- a/packages/SystemUI/res/drawable/major_b_a.xml
+++ b/packages/SystemUI/res/drawable/major_b_a.xml
@@ -20,7 +20,8 @@
     android:width="16dp"
     android:viewportWidth="16"
     android:height="8dp"
-    android:viewportHeight="8" >
+    android:viewportHeight="8"
+    android:tint="?android:attr/colorControlNormal" >
     <group
         android:name="dot_01"
         android:translateX="8"
diff --git a/packages/SystemUI/res/drawable/major_b_c.xml b/packages/SystemUI/res/drawable/major_b_c.xml
index 899109e..c03a8d5 100644
--- a/packages/SystemUI/res/drawable/major_b_c.xml
+++ b/packages/SystemUI/res/drawable/major_b_c.xml
@@ -20,7 +20,8 @@
     android:width="16dp"
     android:viewportWidth="16"
     android:height="8dp"
-    android:viewportHeight="8" >
+    android:viewportHeight="8"
+    android:tint="?android:attr/colorControlNormal" >
     <group
         android:name="dot_01"
         android:translateX="8"
diff --git a/packages/SystemUI/res/drawable/major_c_b.xml b/packages/SystemUI/res/drawable/major_c_b.xml
index cc6c615..9e4e245 100644
--- a/packages/SystemUI/res/drawable/major_c_b.xml
+++ b/packages/SystemUI/res/drawable/major_c_b.xml
@@ -20,7 +20,8 @@
     android:width="16dp"
     android:viewportWidth="16"
     android:height="8dp"
-    android:viewportHeight="8" >
+    android:viewportHeight="8"
+    android:tint="?android:attr/colorControlNormal" >
     <group
         android:name="dot_01"
         android:translateX="12.75"
diff --git a/packages/SystemUI/res/drawable/minor_a_b.xml b/packages/SystemUI/res/drawable/minor_a_b.xml
index c5f5c98..0622aac 100644
--- a/packages/SystemUI/res/drawable/minor_a_b.xml
+++ b/packages/SystemUI/res/drawable/minor_a_b.xml
@@ -21,7 +21,8 @@
     android:width="16dp"
     android:viewportWidth="16"
     android:height="8dp"
-    android:viewportHeight="8" >
+    android:viewportHeight="8"
+    android:tint="?android:attr/colorControlNormal" >
     <group
         android:name="dot_02"
         android:translateX="3.25"
diff --git a/packages/SystemUI/res/drawable/minor_b_a.xml b/packages/SystemUI/res/drawable/minor_b_a.xml
index 3bb08c4..ecb4341 100644
--- a/packages/SystemUI/res/drawable/minor_b_a.xml
+++ b/packages/SystemUI/res/drawable/minor_b_a.xml
@@ -21,7 +21,8 @@
     android:width="16dp"
     android:viewportWidth="16"
     android:height="8dp"
-    android:viewportHeight="8" >
+    android:viewportHeight="8"
+    android:tint="?android:attr/colorControlNormal" >
     <group
         android:name="dot_02"
         android:translateX="8"
diff --git a/packages/SystemUI/res/drawable/minor_b_c.xml b/packages/SystemUI/res/drawable/minor_b_c.xml
index 95c6463..7f59e34 100644
--- a/packages/SystemUI/res/drawable/minor_b_c.xml
+++ b/packages/SystemUI/res/drawable/minor_b_c.xml
@@ -21,7 +21,8 @@
     android:width="16dp"
     android:viewportWidth="16"
     android:height="8dp"
-    android:viewportHeight="8" >
+    android:viewportHeight="8"
+    android:tint="?android:attr/colorControlNormal" >
     <group
         android:name="dot_02"
         android:translateX="8"
diff --git a/packages/SystemUI/res/drawable/minor_c_b.xml b/packages/SystemUI/res/drawable/minor_c_b.xml
index 523afaa..97309bc 100644
--- a/packages/SystemUI/res/drawable/minor_c_b.xml
+++ b/packages/SystemUI/res/drawable/minor_c_b.xml
@@ -21,7 +21,8 @@
     android:width="16dp"
     android:viewportWidth="16"
     android:height="8dp"
-    android:viewportHeight="8" >
+    android:viewportHeight="8"
+    android:tint="?android:attr/colorControlNormal" >
     <group
         android:name="dot_02"
         android:translateX="12.75"
diff --git a/packages/SystemUI/res/layout/pip_menu_action.xml b/packages/SystemUI/res/layout/pip_menu_action.xml
new file mode 100644
index 0000000..db6ae19
--- /dev/null
+++ b/packages/SystemUI/res/layout/pip_menu_action.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="0dp"
+    android:layout_height="match_parent"
+    android:layout_weight="1"
+    android:background="?android:selectableItemBackground">
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:orientation="vertical">
+        <ImageView
+            android:id="@+id/icon"
+            android:layout_width="@dimen/pip_menu_action_icon_size"
+            android:layout_height="@dimen/pip_menu_action_icon_size"
+            android:layout_gravity="center_horizontal"
+            android:layout_marginBottom="4dp" />
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:includeFontPadding="false"
+            android:gravity="center"
+            android:textSize="12sp"
+            android:textColor="#ffffffff"
+            android:textAllCaps="true"
+            android:fontFamily="sans-serif" />
+    </LinearLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/pip_menu_activity.xml b/packages/SystemUI/res/layout/pip_menu_activity.xml
index 88e6e72..054bfab 100644
--- a/packages/SystemUI/res/layout/pip_menu_activity.xml
+++ b/packages/SystemUI/res/layout/pip_menu_activity.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!-- Copyright (C) 2016 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.
@@ -16,21 +16,44 @@
 
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/menu"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:background="#33000000">
+
     <LinearLayout
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center">
-        <Button
-            android:id="@+id/expand_pip"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center"
-            android:textSize="14sp"
+        android:id="@+id/actions"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_marginBottom="48dp">
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="48dp"
+        android:layout_gravity="bottom"
+        android:orientation="horizontal">
+        <TextView
+            android:id="@+id/dismiss"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:gravity="center"
+            android:textSize="12sp"
             android:textColor="#ffffffff"
-            android:text="@string/pip_phone_expand"
-            android:fontFamily="sans-serif" />
+            android:fontFamily="sans-serif"
+            android:text="@string/pip_phone_dismiss"
+            android:background="?android:selectableItemBackground" />
+        <TextView
+            android:id="@+id/minimize"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:gravity="center"
+            android:textSize="12sp"
+            android:textColor="#ffffffff"
+            android:fontFamily="sans-serif"
+            android:text="@string/pip_phone_minimize"
+            android:background="?android:selectableItemBackground" />
     </LinearLayout>
 </FrameLayout>
diff --git a/packages/SystemUI/res/layout/recents_grid_task_view.xml b/packages/SystemUI/res/layout/recents_grid_task_view.xml
new file mode 100644
index 0000000..53bec70
--- /dev/null
+++ b/packages/SystemUI/res/layout/recents_grid_task_view.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.systemui.recents.views.grid.GridTaskView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:focusable="true">
+    <com.android.systemui.recents.views.TaskViewThumbnail
+        android:id="@+id/task_view_thumbnail"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+    <include layout="@layout/recents_task_view_header" />
+
+    <!-- TODO: Move this into a view stub -->
+    <include layout="@layout/recents_task_view_lock_to_app"/>
+
+    <!-- The incompatible app toast -->
+    <include layout="@layout/recents_task_view_incompatible_app_toast"/>
+</com.android.systemui.recents.views.grid.GridTaskView>
+
+
diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml
index c8e5b61..015e4a2 100644
--- a/packages/SystemUI/res/layout/recents_task_view.xml
+++ b/packages/SystemUI/res/layout/recents_task_view.xml
@@ -4,9 +4,9 @@
      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.
@@ -26,35 +26,10 @@
     <include layout="@layout/recents_task_view_header" />
 
     <!-- TODO: Move this into a view stub -->
-    <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
-        android:id="@+id/lock_to_app_fab"
-        android:layout_width="@dimen/recents_lock_to_app_size"
-        android:layout_height="@dimen/recents_lock_to_app_size"
-        android:layout_gravity="bottom|end"
-        android:layout_marginEnd="15dp"
-        android:layout_marginBottom="15dp"
-        android:translationZ="4dp"
-        android:contentDescription="@string/recents_lock_to_app_button_label"
-        android:background="@drawable/recents_lock_to_task_button_bg"
-        android:visibility="invisible"
-        android:alpha="0">
-        <ImageView
-            android:layout_width="@dimen/recents_lock_to_app_icon_size"
-            android:layout_height="@dimen/recents_lock_to_app_icon_size"
-            android:layout_gravity="center"
-            android:src="@drawable/recents_lock_to_app_pin" />
-    </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
+    <include layout="@layout/recents_task_view_lock_to_app"/>
 
     <!-- The incompatible app toast -->
-    <ViewStub android:id="@+id/incompatible_app_toast_stub"
-                android:inflatedId="@+id/incompatible_app_toast"
-                android:layout="@*android:layout/transient_notification"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_gravity="top|center_horizontal"
-                android:layout_marginTop="48dp"
-                android:layout_marginLeft="16dp"
-                android:layout_marginRight="16dp" />
+    <include layout="@layout/recents_task_view_incompatible_app_toast"/>
 </com.android.systemui.recents.views.TaskView>
 
 
diff --git a/packages/SystemUI/res/layout/recents_task_view_incompatible_app_toast.xml b/packages/SystemUI/res/layout/recents_task_view_incompatible_app_toast.xml
new file mode 100644
index 0000000..d573d6b
--- /dev/null
+++ b/packages/SystemUI/res/layout/recents_task_view_incompatible_app_toast.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+<ViewStub
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/incompatible_app_toast_stub"
+    android:inflatedId="@+id/incompatible_app_toast"
+    android:layout="@*android:layout/transient_notification"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_gravity="top|center_horizontal"
+    android:layout_marginTop="48dp"
+    android:layout_marginLeft="16dp"
+    android:layout_marginRight="16dp" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_task_view_lock_to_app.xml b/packages/SystemUI/res/layout/recents_task_view_lock_to_app.xml
new file mode 100644
index 0000000..8cece11
--- /dev/null
+++ b/packages/SystemUI/res/layout/recents_task_view_lock_to_app.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.systemui.statusbar.AlphaOptimizedFrameLayout
+  xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/lock_to_app_fab"
+    android:layout_width="@dimen/recents_lock_to_app_size"
+    android:layout_height="@dimen/recents_lock_to_app_size"
+    android:layout_gravity="bottom|end"
+    android:layout_marginEnd="15dp"
+    android:layout_marginBottom="15dp"
+    android:translationZ="4dp"
+    android:contentDescription="@string/recents_lock_to_app_button_label"
+    android:background="@drawable/recents_lock_to_task_button_bg"
+    android:visibility="invisible"
+    android:alpha="0">
+    <ImageView
+        android:layout_width="@dimen/recents_lock_to_app_icon_size"
+        android:layout_height="@dimen/recents_lock_to_app_icon_size"
+        android:layout_gravity="center"
+        android:src="@drawable/recents_lock_to_app_pin" />
+</com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index a7d4aa0..fe5606e 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -717,6 +717,9 @@
     <!-- The size of the PIP dismiss target. -->
     <dimen name="pip_dismiss_target_size">48dp</dimen>
 
+    <!-- The size of a PIP menu action icon. -->
+    <dimen name="pip_menu_action_icon_size">32dp</dimen>
+
     <!-- Values specific to grid-based recents. -->
     <!-- Margins around recent tasks. -->
     <dimen name="recents_grid_margin_left">15dp</dimen>
diff --git a/packages/SystemUI/res/values/dimens_grid.xml b/packages/SystemUI/res/values/dimens_grid.xml
new file mode 100644
index 0000000..43e152a
--- /dev/null
+++ b/packages/SystemUI/res/values/dimens_grid.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+*/
+-->
+<resources>
+  <dimen name="recents_grid_padding_left_right">32dp</dimen>
+  <dimen name="recents_grid_padding_top_bottom">84dp</dimen>
+  <dimen name="recents_grid_padding_task_view">20dp</dimen>
+</resources>
+
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index c8b3b69d..31b83cc 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1121,19 +1121,18 @@
     <!-- Toast shown when user unlocks screen and managed profile activity is in the foreground -->
     <string name="managed_profile_foreground_toast">You\'re using your work profile</string>
 
-    <string-array name="volume_stream_titles">
-        <item>Call</item> <!-- STREAM_VOICE_CALL -->
-        <item>System</item> <!-- STREAM_SYSTEM -->
-        <item>Ring</item> <!-- STREAM_RING -->
-        <item>Media</item> <!-- STREAM_MUSIC -->
-        <item>Alarm</item> <!-- STREAM_ALARM -->
-        <item></item> <!-- STREAM_NOTIFICATION -->
-        <item>Bluetooth</item> <!-- STREAM_BLUETOOTH_SCO -->
-        <item></item> <!-- STREAM_SYSTEM_ENFORCED -->
-        <item></item> <!-- STREAM_DTMF -->
-        <item></item> <!-- STREAM_TTS -->
-        <item>Accessibility</item> <!-- STREAM_ACCESSIBILITY -->
-    </string-array>
+    <!-- volume stream names. All nouns. -->
+    <string name="stream_voice_call">Call</string> <!-- STREAM_VOICE_CALL -->
+    <string name="stream_system">System</string> <!-- STREAM_SYSTEM -->
+    <string name="stream_ring">Ring</string> <!-- STREAM_RING -->
+    <string name="stream_music">Media</string> <!-- STREAM_MUSIC -->
+    <string name="stream_alarm">Alarm</string> <!-- STREAM_ALARM -->
+    <string name="stream_notification">Notification</string> <!-- STREAM_NOTIFICATION -->
+    <string name="stream_bluetooth_sco">Bluetooth</string> <!-- STREAM_BLUETOOTH_SCO -->
+    <string name="stream_system_enforced">System enforced</string> <!-- STREAM_SYSTEM_ENFORCED -->
+    <string name="stream_dtmf">Dual multi tone frequency</string> <!-- STREAM_DTMF -->
+    <string name="stream_tts">Text to speech</string> <!-- STREAM_TTS -->
+    <string name="stream_accessibility">Accessibility</string> <!-- STREAM_ACCESSIBILITY -->
 
     <string name="volume_stream_muted" translatable="false">%s silent</string>
     <string name="volume_stream_vibrate" translatable="false">%s vibrate</string>
@@ -1688,9 +1687,15 @@
         not appear on production builds ever. -->
     <string name="tuner_doze_always_on" translatable="false">Always on</string>
 
-    <!-- Making the PIP fullscreen -->
+    <!-- Making the PIP fullscreen [CHAR LIMIT=25] -->
     <string name="pip_phone_expand">Expand</string>
 
+    <!-- Label for PIP action to Minimize the PIP [CHAR LIMIT=25] -->
+    <string name="pip_phone_minimize">Minimize</string>
+
+    <!-- Label for PIP action to Dismiss the PIP -->
+    <string name="pip_phone_dismiss">Dismiss</string>
+
     <!-- PIP section of the tuner. Non-translatable since it should
         not appear on production builds ever. -->
     <string name="picture_in_picture" translatable="false">Picture-in-Picture</string>
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index 3f9ae55..2576bb7 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -32,6 +32,7 @@
 import com.android.internal.app.AssistUtils;
 import com.android.internal.app.IVoiceInteractionSessionListener;
 import com.android.internal.app.IVoiceInteractionSessionShowCallback;
+import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.BaseStatusBar;
 import com.android.systemui.statusbar.CommandQueue;
@@ -284,7 +285,7 @@
 
     @Nullable
     private ComponentName getAssistInfo() {
-        return mAssistUtils.getAssistComponentForUser(UserHandle.USER_CURRENT);
+        return mAssistUtils.getAssistComponentForUser(KeyguardUpdateMonitor.getCurrentUser());
     }
 
     public void showDisclosure() {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingLog.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingLog.java
index 1338d9d..71ddba5 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingLog.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingLog.java
@@ -84,6 +84,11 @@
         log("I", tag, s);
     }
 
+    public static void wLogcat(String tag, String s) {
+        Log.w(TAG, tag + "\t" + s);
+        log("W", tag, s);
+    }
+
     public static void w(String tag, String s) {
         if (LOGCAT) {
             Log.w(TAG, tag + "\t" + s);
@@ -133,7 +138,7 @@
         pw.println();
     }
 
-    public static synchronized void wtf(String tag, String s) {
+    public static synchronized void wtf(String tag, String s, Throwable here) {
         if (!ENABLED) {
             return;
         }
@@ -161,6 +166,6 @@
             Log.e(TAG, "Unable to write log, build must be debuggable.");
         }
 
-        Log.wtf(TAG, tag + " " + s + "; " + fileMessage);
+        Log.wtf(TAG, tag + " " + s + "; " + fileMessage, here);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
index 8fc555f..1abea37 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
@@ -71,6 +71,7 @@
     private boolean mSessionActive = false;
     private int mState = StatusBarState.SHADE;
     private boolean mScreenOn;
+    private Runnable mPendingWtf;
 
     protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
         @Override
@@ -136,6 +137,7 @@
     private void onSessionStart() {
         if (FalsingLog.ENABLED) {
             FalsingLog.i("onSessionStart", "classifierEnabled=" + isClassiferEnabled());
+            clearPendingWtf();
         }
         mBouncerOn = false;
         mSessionActive = true;
@@ -172,13 +174,35 @@
         if (FalsingLog.ENABLED) {
             // We're getting some false wtfs from touches that happen after the device went
             // to sleep. Only report missing sessions that happen when the device is interactive.
-            if (!mSessionActive && mContext.getSystemService(PowerManager.class).isInteractive()) {
-                FalsingLog.wtf("isFalseTouch", new StringBuilder()
+            if (!mSessionActive && mContext.getSystemService(PowerManager.class).isInteractive()
+                    && mPendingWtf == null) {
+                int enabled = isEnabled() ? 1 : 0;
+                int screenOn = mScreenOn ? 1 : 0;
+                String state = StatusBarState.toShortString(mState);
+                Throwable here = new Throwable("here");
+                FalsingLog.wLogcat("isFalseTouch", new StringBuilder()
                         .append("Session is not active, yet there's a query for a false touch.")
-                        .append(" enabled=").append(isEnabled() ? 1 : 0)
-                        .append(" mScreenOn=").append(mScreenOn ? 1 : 0)
-                        .append(" mState=").append(StatusBarState.toShortString(mState))
+                        .append(" enabled=").append(enabled)
+                        .append(" mScreenOn=").append(screenOn)
+                        .append(" mState=").append(state)
+                        .append(". Escalating to WTF if screen does not turn on soon.")
                         .toString());
+
+                // Unfortunately we're also getting false positives for touches that happen right
+                // after the screen turns on, but before that notification has made it to us.
+                // Unfortunately there's no good way to catch that, except to wait and see if we get
+                // the screen on notification soon.
+                mPendingWtf = () -> FalsingLog.wtf("isFalseTouch", new StringBuilder()
+                        .append("Session did not become active after query for a false touch.")
+                        .append(" enabled=").append(enabled)
+                        .append('/').append(isEnabled() ? 1 : 0)
+                        .append(" mScreenOn=").append(screenOn)
+                        .append('/').append(mScreenOn ? 1 : 0)
+                        .append(" mState=").append(state)
+                        .append('/').append(StatusBarState.toShortString(mState))
+                        .append(". Look for warnings ~1000ms earlier to see root cause.")
+                        .toString(), here);
+                mHandler.postDelayed(mPendingWtf, 1000);
             }
         }
         if (mAccessibilityManager.isTouchExplorationEnabled()) {
@@ -189,6 +213,13 @@
         return mHumanInteractionClassifier.isFalseTouch();
     }
 
+    private void clearPendingWtf() {
+        if (mPendingWtf != null) {
+            mHandler.removeCallbacks(mPendingWtf);
+            mPendingWtf = null;
+        }
+    }
+
     @Override
     public synchronized void onSensorChanged(SensorEvent event) {
         mDataCollector.onSensorChanged(event);
@@ -224,6 +255,7 @@
             FalsingLog.i("onScreenTurningOn", new StringBuilder()
                     .append("from=").append(mScreenOn ? 1 : 0)
                     .toString());
+            clearPendingWtf();
         }
         mScreenOn = true;
         if (sessionEntrypoint()) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index 13e047c..6a868d5 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -218,7 +218,8 @@
                     Preconditions.checkState(mState == State.DOZE_REQUEST_PULSE);
                     break;
                 case DOZE_PULSE_DONE:
-                    Preconditions.checkState(mState == State.DOZE_PULSING);
+                    Preconditions.checkState(
+                            mState == State.DOZE_REQUEST_PULSE || mState == State.DOZE_PULSING);
                     break;
                 default:
                     break;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index 43cfa32..7e275d8 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -16,9 +16,17 @@
 
 package com.android.systemui.pip.phone;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import android.app.ActivityManager;
 import android.app.IActivityManager;
 import android.content.Context;
+import android.content.pm.ParceledListSlice;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.IPinnedStackController;
+import android.view.IPinnedStackListener;
 import android.view.IWindowManager;
 import android.view.WindowManagerGlobal;
 
@@ -33,10 +41,52 @@
     private Context mContext;
     private IActivityManager mActivityManager;
     private IWindowManager mWindowManager;
+    private Handler mHandler = new Handler();
+
+    private final PinnedStackListener mPinnedStackListener = new PinnedStackListener();
 
     private PipMenuActivityController mMenuController;
     private PipTouchHandler mTouchHandler;
 
+    /**
+     * Handler for messages from the PIP controller.
+     */
+    private class PinnedStackListener extends IPinnedStackListener.Stub {
+
+        @Override
+        public void onListenerRegistered(IPinnedStackController controller) {
+            mHandler.post(() -> {
+                mTouchHandler.setPinnedStackController(controller);
+            });
+        }
+
+        @Override
+        public void onBoundsChanged(boolean adjustedForIme) {
+            // Do nothing
+        }
+
+        @Override
+        public void onActionsChanged(ParceledListSlice actions) {
+            mHandler.post(() -> {
+                mMenuController.setActions(actions);
+            });
+        }
+
+        @Override
+        public void onMinimizedStateChanged(boolean isMinimized) {
+            mHandler.post(() -> {
+                mTouchHandler.onMinimizedStateChanged(isMinimized);
+            });
+        }
+
+        @Override
+        public void onSnapToEdgeStateChanged(boolean isSnapToEdge) {
+            mHandler.post(() -> {
+                mTouchHandler.onSnapToEdgeStateChanged(isSnapToEdge);
+            });
+        }
+    }
+
     private PipManager() {}
 
     /**
@@ -47,6 +97,12 @@
         mActivityManager = ActivityManager.getService();
         mWindowManager = WindowManagerGlobal.getWindowManagerService();
 
+        try {
+            mWindowManager.registerPinnedStackListener(DEFAULT_DISPLAY, mPinnedStackListener);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to register pinned stack listener", e);
+        }
+
         mMenuController = new PipMenuActivityController(context, mActivityManager, mWindowManager);
         mTouchHandler = new PipTouchHandler(context, mMenuController, mActivityManager,
                 mWindowManager);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index bfe5cff..fe8ee6f 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -19,16 +19,27 @@
 import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.ActivityManager;
+import android.app.PendingIntent.CanceledException;
+import android.app.RemoteAction;
 import android.content.Intent;
+import android.content.pm.ParceledListSlice;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
 import android.os.Messenger;
 import android.os.RemoteException;
 import android.util.Log;
+import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
 import com.android.systemui.R;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Translucent activity that gets started on top of a task in PIP to allow the user to control it.
  */
@@ -36,16 +47,25 @@
 
     private static final String TAG = "PipMenuActivity";
 
-    public static final int MESSAGE_FINISH_SELF = 2;
+    public static final int MESSAGE_FINISH_SELF = 1;
+    public static final int MESSAGE_UPDATE_ACTIONS = 2;
 
     private static final long INITIAL_DISMISS_DELAY = 2000;
     private static final long POST_INTERACTION_DISMISS_DELAY = 1500;
 
+    private List<RemoteAction> mActions = new ArrayList<>();
+    private View mDismissButton;
+    private View mMinimizeButton;
+
+    private Handler mHandler = new Handler();
     private Messenger mToControllerMessenger;
     private Messenger mMessenger = new Messenger(new Handler() {
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
+                case MESSAGE_UPDATE_ACTIONS:
+                    setActions(((ParceledListSlice) msg.obj).getList());
+                    break;
                 case MESSAGE_FINISH_SELF:
                     finish();
                     break;
@@ -63,15 +83,27 @@
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-
-        Intent startingIntet = getIntent();
-        mToControllerMessenger = startingIntet.getParcelableExtra(
-                PipMenuActivityController.EXTRA_CONTROLLER_MESSENGER);
-
         setContentView(R.layout.pip_menu_activity);
-        findViewById(R.id.expand_pip).setOnClickListener((view) -> {
-            finish();
-            notifyExpandPip();
+
+        Intent startingIntent = getIntent();
+        mToControllerMessenger = startingIntent.getParcelableExtra(
+                PipMenuActivityController.EXTRA_CONTROLLER_MESSENGER);
+        ParceledListSlice actions = startingIntent.getParcelableExtra(
+                PipMenuActivityController.EXTRA_ACTIONS);
+        if (actions != null) {
+            setActions(actions.getList());
+        }
+
+        findViewById(R.id.menu).setOnClickListener((v) -> {
+            expandPip();
+        });
+        mDismissButton = findViewById(R.id.dismiss);
+        mDismissButton.setOnClickListener((v) -> {
+            dismissPip();
+        });
+        mMinimizeButton = findViewById(R.id.minimize);
+        mMinimizeButton.setOnClickListener((v) -> {
+            minimizePip();
         });
     }
 
@@ -107,25 +139,74 @@
         // Do nothing
     }
 
+    private void setActions(List<RemoteAction> actions) {
+        mActions.clear();
+        mActions.addAll(actions);
+        updateActionViews();
+    }
+
+    private void updateActionViews() {
+        ViewGroup actionsContainer = (ViewGroup) findViewById(R.id.actions);
+        if (actionsContainer != null) {
+            actionsContainer.removeAllViews();
+
+            // Recreate the layout
+            final LayoutInflater inflater = LayoutInflater.from(this);
+            for (int i = 0; i < mActions.size(); i++) {
+                final RemoteAction action = mActions.get(i);
+                final ViewGroup actionContainer = (ViewGroup) inflater.inflate(
+                        R.layout.pip_menu_action, actionsContainer, false);
+                actionContainer.setOnClickListener((v) -> {
+                    action.sendActionInvoked();
+                });
+
+                final TextView title = (TextView) actionContainer.findViewById(R.id.title);
+                title.setText(action.getTitle());
+                title.setContentDescription(action.getContentDescription());
+
+                final ImageView icon = (ImageView) actionContainer.findViewById(R.id.icon);
+                action.getIcon().loadDrawableAsync(this, (d) -> {
+                    icon.setImageDrawable(d);
+                }, mHandler);
+                actionsContainer.addView(actionContainer);
+            }
+        }
+    }
+
     private void notifyActivityVisibility(boolean visible) {
         Message m = Message.obtain();
         m.what = PipMenuActivityController.MESSAGE_ACTIVITY_VISIBILITY_CHANGED;
         m.arg1 = visible ? 1 : 0;
         m.replyTo = visible ? mMessenger : null;
-        try {
-            mToControllerMessenger.send(m);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Could not notify controller of PIP menu visibility", e);
-        }
+        sendMessage(m, "Could not notify controller of PIP menu visibility");
     }
 
-    private void notifyExpandPip() {
+    private void expandPip() {
+        sendEmptyMessage(PipMenuActivityController.MESSAGE_EXPAND_PIP,
+                "Could not notify controller to expand PIP");
+    }
+
+    private void minimizePip() {
+        sendEmptyMessage(PipMenuActivityController.MESSAGE_MINIMIZE_PIP,
+                "Could not notify controller to minimize PIP");
+    }
+
+    private void dismissPip() {
+        sendEmptyMessage(PipMenuActivityController.MESSAGE_DISMISS_PIP,
+                "Could not notify controller to dismiss PIP");
+    }
+
+    private void sendEmptyMessage(int what, String errorMsg) {
         Message m = Message.obtain();
-        m.what = PipMenuActivityController.MESSAGE_EXPAND_PIP;
+        m.what = what;
+        sendMessage(m, errorMsg);
+    }
+
+    private void sendMessage(Message m, String errorMsg) {
         try {
             mToControllerMessenger.send(m);
         } catch (RemoteException e) {
-            Log.e(TAG, "Could not notify controller to expand PIP", e);
+            Log.e(TAG, errorMsg, e);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index d1bce0c..64e2d1a 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -1,14 +1,13 @@
 package com.android.systemui.pip.phone;
 
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.view.WindowManager.INPUT_CONSUMER_PIP;
 
 import android.app.ActivityManager.StackInfo;
 import android.app.ActivityOptions;
 import android.app.IActivityManager;
 import android.content.Context;
 import android.content.Intent;
-import android.graphics.Rect;
+import android.content.pm.ParceledListSlice;
 import android.os.Handler;
 import android.os.Message;
 import android.os.Messenger;
@@ -24,8 +23,12 @@
     private static final String TAG = "PipMenuActivityController";
 
     public static final String EXTRA_CONTROLLER_MESSENGER = "messenger";
-    public static final int MESSAGE_ACTIVITY_VISIBILITY_CHANGED = 1;
-    public static final int MESSAGE_EXPAND_PIP = 3;
+    public static final String EXTRA_ACTIONS = "actions";
+
+    public static final int MESSAGE_ACTIVITY_VISIBILITY_CHANGED = 100;
+    public static final int MESSAGE_EXPAND_PIP = 101;
+    public static final int MESSAGE_MINIMIZE_PIP = 102;
+    public static final int MESSAGE_DISMISS_PIP = 103;
 
     /**
      * A listener interface to receive notification on changes in PIP.
@@ -35,12 +38,29 @@
          * Called when the PIP menu visibility changes.
          */
         void onPipMenuVisibilityChanged(boolean visible);
+
+        /**
+         * Called when the PIP requested to be expanded.
+         */
+        void onPipExpand();
+
+        /**
+         * Called when the PIP requested to be minimized.
+         */
+        void onPipMinimize();
+
+        /**
+         * Called when the PIP requested to be expanded.
+         */
+        void onPipDismiss();
     }
 
     private Context mContext;
     private IActivityManager mActivityManager;
     private IWindowManager mWindowManager;
+
     private ArrayList<Listener> mListeners = new ArrayList<>();
+    private ParceledListSlice mActions;
 
     private Messenger mToActivityMessenger;
     private Messenger mMessenger = new Messenger(new Handler() {
@@ -57,10 +77,23 @@
                     break;
                 }
                 case MESSAGE_EXPAND_PIP: {
-                    try {
-                        mActivityManager.resizeStack(PINNED_STACK_ID, null, true, true, true, 225);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Error showing PIP menu activity", e);
+                    int listenerCount = mListeners.size();
+                    for (int i = 0; i < listenerCount; i++) {
+                        mListeners.get(i).onPipExpand();
+                    }
+                    break;
+                }
+                case MESSAGE_MINIMIZE_PIP: {
+                    int listenerCount = mListeners.size();
+                    for (int i = 0; i < listenerCount; i++) {
+                        mListeners.get(i).onPipMinimize();
+                    }
+                    break;
+                }
+                case MESSAGE_DISMISS_PIP: {
+                    int listenerCount = mListeners.size();
+                    for (int i = 0; i < listenerCount; i++) {
+                        mListeners.get(i).onPipDismiss();
                     }
                     break;
                 }
@@ -95,6 +128,7 @@
                     pinnedStackInfo.taskIds.length > 0) {
                 Intent intent = new Intent(mContext, PipMenuActivity.class);
                 intent.putExtra(EXTRA_CONTROLLER_MESSENGER, mMessenger);
+                intent.putExtra(EXTRA_ACTIONS, mActions);
                 ActivityOptions options = ActivityOptions.makeBasic();
                 options.setLaunchTaskId(
                         pinnedStackInfo.taskIds[pinnedStackInfo.taskIds.length - 1]);
@@ -123,4 +157,22 @@
             mToActivityMessenger = null;
         }
     }
+
+    /**
+     * Sets the {@param actions} associated with the PiP.
+     */
+    public void setActions(ParceledListSlice actions) {
+        mActions = actions;
+
+        if (mToActivityMessenger != null) {
+            Message m = Message.obtain();
+            m.what = PipMenuActivity.MESSAGE_UPDATE_ACTIONS;
+            m.obj = actions;
+            try {
+                mToActivityMessenger.send(m);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Could not notify menu activity to update actions", e);
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 09671e7..ff3cc79 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -17,7 +17,6 @@
 package com.android.systemui.pip.phone;
 
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.INPUT_CONSUMER_PIP;
 
 import static com.android.systemui.Interpolators.FAST_OUT_LINEAR_IN;
@@ -38,7 +37,6 @@
 import android.os.RemoteException;
 import android.util.Log;
 import android.view.IPinnedStackController;
-import android.view.IPinnedStackListener;
 import android.view.IWindowManager;
 import android.view.InputChannel;
 import android.view.InputEvent;
@@ -80,7 +78,6 @@
     private final IActivityManager mActivityManager;
     private final IWindowManager mWindowManager;
     private final ViewConfiguration mViewConfig;
-    private final PinnedStackListener mPinnedStackListener = new PinnedStackListener();
     private final PipMenuListener mMenuListener = new PipMenuListener();
     private IPinnedStackController mPinnedStackController;
 
@@ -149,26 +146,6 @@
     }
 
     /**
-     * Handler for messages from the PIP controller.
-     */
-    private class PinnedStackListener extends IPinnedStackListener.Stub {
-
-        @Override
-        public void onListenerRegistered(IPinnedStackController controller) {
-            mPinnedStackController = controller;
-
-            // Update the controller with the current tuner state
-            setMinimizedState(mIsMinimized);
-            setSnapToEdge(mEnableSnapToEdge);
-        }
-
-        @Override
-        public void onBoundsChanged(boolean adjustedForIme) {
-            // Do nothing
-        }
-    }
-
-    /**
      * A listener for the PIP menu activity.
      */
     private class PipMenuListener implements PipMenuActivityController.Listener {
@@ -181,17 +158,30 @@
                 unregisterInputConsumer();
             }
         }
+
+        @Override
+        public void onPipExpand() {
+            if (!mIsMinimized) {
+                expandPinnedStackToFullscreen();
+            }
+        }
+
+        @Override
+        public void onPipMinimize() {
+            setMinimizedState(true);
+            animateToClosestMinimizedTarget();
+        }
+
+        @Override
+        public void onPipDismiss() {
+            animateDismissPinnedStack(mPinnedStackBounds);
+        }
     }
 
     public PipTouchHandler(Context context, PipMenuActivityController menuController,
             IActivityManager activityManager, IWindowManager windowManager) {
 
         // Initialize the Pip input consumer
-        try {
-            windowManager.registerPinnedStackListener(DEFAULT_DISPLAY, mPinnedStackListener);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to create PIP input consumer", e);
-        }
         mContext = context;
         mActivityManager = activityManager;
         mWindowManager = windowManager;
@@ -255,6 +245,14 @@
         updateBoundedPinnedStackBounds(false /* updatePinnedStackBounds */);
     }
 
+    public void onMinimizedStateChanged(boolean isMinimized) {
+        mIsMinimized = isMinimized;
+    }
+
+    public void onSnapToEdgeStateChanged(boolean isSnapToEdge) {
+        mSnapAlgorithm.setSnapToEdge(isSnapToEdge);
+    }
+
     private boolean handleTouchEvent(MotionEvent ev) {
         // Skip touch handling until we are bound to the controller
         if (mPinnedStackController == null) {
@@ -353,10 +351,17 @@
     }
 
     /**
-     * Sets the snap-to-edge state.
+     * Sets the controller to update the system of changes from user interaction.
+     */
+    void setPinnedStackController(IPinnedStackController controller) {
+        mPinnedStackController = controller;
+    }
+
+    /**
+     * Sets the snap-to-edge state and notifies the controller.
      */
     private void setSnapToEdge(boolean snapToEdge) {
-        mSnapAlgorithm.setSnapToEdge(snapToEdge);
+        onSnapToEdgeStateChanged(snapToEdge);
 
         if (mPinnedStackController != null) {
             try {
@@ -371,7 +376,7 @@
      * Sets the minimized state and notifies the controller.
      */
     private void setMinimizedState(boolean isMinimized) {
-        mIsMinimized = isMinimized;
+        onMinimizedStateChanged(isMinimized);
 
         if (mPinnedStackController != null) {
             try {
@@ -432,6 +437,12 @@
         mPinnedStackBoundsAnimator = mMotionHelper.createAnimationToBounds(mPinnedStackBounds,
                 toBounds, MINIMIZE_STACK_MAX_DURATION, LINEAR_OUT_SLOW_IN,
                 mUpdatePinnedStackBoundsListener);
+        mPinnedStackBoundsAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mMenuController.hideMenu();
+            }
+        });
         mPinnedStackBoundsAnimator.start();
     }
 
@@ -759,7 +770,8 @@
 
         @Override
         public boolean onUp(PipTouchState touchState) {
-            if (mEnableTapThrough && !touchState.isDragging() && !mIsTappingThrough) {
+            if (mEnableTapThrough && !touchState.isDragging() && !mIsMinimized &&
+                    !mIsTappingThrough) {
                 mMenuController.showMenu();
                 mIsTappingThrough = true;
                 return true;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
index 17d9864..2e84ced 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
@@ -16,11 +16,7 @@
 
 package com.android.systemui.pip.phone;
 
-import android.app.IActivityManager;
 import android.graphics.PointF;
-import android.view.IPinnedStackController;
-import android.view.IPinnedStackListener;
-import android.view.IWindowManager;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.ViewConfiguration;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 790f3f6..d9fc2fc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -207,8 +207,6 @@
                 getSystemService(Context.UI_MODE_SERVICE);
         if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
             mImpl = new RecentsTvImpl(mContext);
-        } else if (SystemProperties.getBoolean("ro.recents.grid", false) == true) {
-            mImpl = new RecentsGridImpl(mContext);
         } else {
             mImpl = new RecentsImpl(mContext);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 73c6e6e..711f0c6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -20,6 +20,7 @@
 import android.content.res.Resources;
 import android.graphics.Rect;
 
+import android.os.SystemProperties;
 import com.android.systemui.R;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 
@@ -58,6 +59,10 @@
     public boolean fakeShadows;
     public int svelteLevel;
 
+    // Whether this product supports Grid-based Recents. If this is field is set to true, then
+    // Recents will layout task views in a grid mode when there's enough space in the screen.
+    public boolean isGridEnabled;
+
     public RecentsConfiguration(Context context) {
         // Load only resources that can not change after the first load either through developer
         // settings or via multi window
@@ -66,6 +71,7 @@
         Resources res = appContext.getResources();
         fakeShadows = res.getBoolean(R.bool.config_recents_fake_shadows);
         svelteLevel = res.getInteger(R.integer.recents_svelte_level);
+        isGridEnabled = SystemProperties.getBoolean("ro.recents.grid", false);
 
         float screenDensity = context.getResources().getDisplayMetrics().density;
         smallestWidth = ssp.getDeviceSmallestWidth();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index ea50d89..ddffea2 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -74,6 +74,7 @@
 import android.view.WindowManager.KeyboardShortcutsReceiver;
 import android.view.WindowManagerGlobal;
 import android.view.accessibility.AccessibilityManager;
+import android.app.KeyguardManager;
 
 import com.android.internal.app.AssistUtils;
 import com.android.internal.os.BackgroundThread;
@@ -124,6 +125,7 @@
     AssistUtils mAssistUtils;
     WindowManager mWm;
     IWindowManager mIwm;
+    KeyguardManager mKgm;
     UserManager mUm;
     Display mDisplay;
     String mRecentsPackage;
@@ -212,6 +214,7 @@
         mAssistUtils = new AssistUtils(context);
         mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
         mIwm = WindowManagerGlobal.getWindowManagerService();
+        mKgm = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
         mUm = UserManager.get(context);
         mDisplay = mWm.getDefaultDisplay();
         mRecentsPackage = context.getPackageName();
@@ -862,6 +865,16 @@
         return label;
     }
 
+    /**
+     * Returns whether the provided {@param userId} is currently locked (and showing Keyguard).
+     */
+    public boolean isDeviceLocked(int userId) {
+        if (mKgm == null) {
+            return false;
+        }
+        return mKgm.isDeviceLocked(userId);
+    }
+
     /** Returns the package name of the home activity. */
     public String getHomeActivityPackageName() {
         if (mPm == null) return null;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
index 2c5c437..4349e30 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
@@ -86,7 +86,7 @@
     public static <T extends View> T findParent(View v, Class<T> parentClass) {
         ViewParent parent = v.getParent();
         while (parent != null) {
-            if (parent.getClass().equals(parentClass)) {
+            if (parentClass.isAssignableFrom(parent.getClass())) {
                 return (T) parent;
             }
             parent = parent.getParent();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index 9b48e4d..5877440 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -28,6 +28,7 @@
 import android.os.UserManager;
 import android.util.ArraySet;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
 
 import com.android.systemui.Prefs;
@@ -130,6 +131,7 @@
 
         SparseArray<Task.TaskKey> affiliatedTasks = new SparseArray<>();
         SparseIntArray affiliatedTaskCounts = new SparseIntArray();
+        SparseBooleanArray lockedUsers = new SparseBooleanArray();
         String dismissDescFormat = mContext.getString(
                 R.string.accessibility_recents_item_will_be_dismissed);
         String appInfoDescFormat = mContext.getString(
@@ -177,12 +179,17 @@
             int backgroundColor = loader.getActivityBackgroundColor(t.taskDescription);
             boolean isSystemApp = (info != null) &&
                     ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
+            if (lockedUsers.indexOfKey(t.userId) < 0) {
+                lockedUsers.put(t.userId, Recents.getSystemServices().isDeviceLocked(t.userId));
+            }
+            boolean isLocked = lockedUsers.get(t.userId);
 
             // Add the task to the stack
             Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, icon,
                     thumbnail, title, titleDescription, dismissDescription, appInfoDescription,
                     activityColor, backgroundColor, isLaunchTarget, isStackTask, isSystemApp,
-                    t.isDockable, t.bounds, t.taskDescription, t.resizeMode, t.topActivity);
+                    t.isDockable, t.bounds, t.taskDescription, t.resizeMode, t.topActivity,
+                    isLocked);
 
             allTasks.add(task);
             affiliatedTaskCounts.put(taskKey.id, affiliatedTaskCounts.get(taskKey.id, 0) + 1);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 86a0315..53f713a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -189,6 +189,9 @@
     @ViewDebug.ExportedProperty(category="recents")
     public ComponentName topActivity;
 
+    @ViewDebug.ExportedProperty(category="recents")
+    public boolean isLocked;
+
     private ArrayList<TaskCallbacks> mCallbacks = new ArrayList<>();
 
     public Task() {
@@ -200,7 +203,7 @@
                 String appInfoDescription, int colorPrimary, int colorBackground,
                 boolean isLaunchTarget, boolean isStackTask, boolean isSystemApp,
                 boolean isDockable, Rect bounds, ActivityManager.TaskDescription taskDescription,
-                int resizeMode, ComponentName topActivity) {
+                int resizeMode, ComponentName topActivity, boolean isLocked) {
         boolean isInAffiliationGroup = (affiliationTaskId != key.id);
         boolean hasAffiliationGroupColor = isInAffiliationGroup && (affiliationColor != 0);
         this.key = key;
@@ -224,6 +227,7 @@
         this.isDockable = isDockable;
         this.resizeMode = resizeMode;
         this.topActivity = topActivity;
+        this.isLocked = isLocked;
     }
 
     /**
@@ -250,6 +254,7 @@
         this.isSystemApp = o.isSystemApp;
         this.isDockable = o.isDockable;
         this.resizeMode = o.resizeMode;
+        this.isLocked = o.isLocked;
         this.topActivity = o.topActivity;
     }
 
@@ -355,6 +360,9 @@
         if (isFreeformTask()) {
             writer.print(" freeform=Y");
         }
+        if (isLocked) {
+            writer.print(" locked=Y");
+        }
         writer.print(" "); writer.print(title);
         writer.println();
     }
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 253d06a..dba085e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/AnimateableViewBounds.java
@@ -30,17 +30,17 @@
     private static final float MIN_ALPHA = 0.1f;
     private static final float MAX_ALPHA = 0.8f;
 
-    View mSourceView;
+    protected View mSourceView;
     @ViewDebug.ExportedProperty(category="recents")
-    Rect mClipRect = new Rect();
+    protected Rect mClipRect = new Rect();
     @ViewDebug.ExportedProperty(category="recents")
-    Rect mClipBounds = new Rect();
+    protected Rect mClipBounds = new Rect();
     @ViewDebug.ExportedProperty(category="recents")
-    Rect mLastClipBounds = new Rect();
+    protected Rect mLastClipBounds = new Rect();
     @ViewDebug.ExportedProperty(category="recents")
-    int mCornerRadius;
+    protected int mCornerRadius;
     @ViewDebug.ExportedProperty(category="recents")
-    float mAlpha = 1f;
+    protected float mAlpha = 1f;
 
     public AnimateableViewBounds(View source, int cornerRadius) {
         mSourceView = source;
@@ -110,7 +110,7 @@
         return mClipRect.bottom;
     }
 
-    private void updateClipBounds() {
+    protected void updateClipBounds() {
         mClipBounds.set(Math.max(0, mClipRect.left), Math.max(0, mClipRect.top),
                 mSourceView.getWidth() - Math.max(0, mClipRect.right),
                 mSourceView.getHeight() - Math.max(0, mClipRect.bottom));
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
index 493e618..c1f4c8a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
@@ -157,7 +157,7 @@
 
             // Get the current transform for the task, which will be used to position it offscreen
             stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
-                    null);
+                    null, mStackView.useGridLayout());
 
             if (hideTask) {
                 tv.setVisibility(View.INVISIBLE);
@@ -230,7 +230,7 @@
             // Get the current transform for the task, which will be updated to the final transform
             // to animate to depending on how recents was invoked
             stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
-                    null);
+                    null, mStackView.useGridLayout());
 
             if (launchState.launchedFromApp && !launchState.launchedViaDockGesture) {
                 if (task.isLaunchTarget) {
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 571c0f6..3c97310 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -38,7 +38,7 @@
 import com.android.systemui.recents.misc.Utilities;
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.recents.model.TaskStack;
-
+import com.android.systemui.recents.views.grid.TaskGridLayoutAlgorithm;
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -326,7 +326,7 @@
     @ViewDebug.ExportedProperty(category="recents")
     int mMinTranslationZ;
     @ViewDebug.ExportedProperty(category="recents")
-    int mMaxTranslationZ;
+    public int mMaxTranslationZ;
 
     // Optimization, allows for quick lookup of task -> index
     private SparseIntArray mTaskIndexMap = new SparseIntArray();
@@ -334,6 +334,7 @@
 
     // The freeform workspace layout
     FreeformWorkspaceLayoutAlgorithm mFreeformLayoutAlgorithm;
+    TaskGridLayoutAlgorithm mTaskGridLayoutAlgorithm;
 
     // The transform to place TaskViews at the front and back of the stack respectively
     TaskViewTransform mBackOfStackTransform = new TaskViewTransform();
@@ -344,6 +345,7 @@
         mContext = context;
         mCb = cb;
         mFreeformLayoutAlgorithm = new FreeformWorkspaceLayoutAlgorithm(context);
+        mTaskGridLayoutAlgorithm = new TaskGridLayoutAlgorithm(context);
         reloadOnConfigurationChange(context);
     }
 
@@ -377,6 +379,7 @@
                 R.dimen.recents_layout_initial_bottom_offset_tablet,
                 R.dimen.recents_layout_initial_bottom_offset_tablet);
         mFreeformLayoutAlgorithm.reloadOnConfigurationChange(context);
+        mTaskGridLayoutAlgorithm.reloadOnConfigurationChange(context);
         mMinMargin = res.getDimensionPixelSize(R.dimen.recents_layout_min_margin);
         mBaseTopMargin = getDimensionForDevice(context,
                 R.dimen.recents_layout_top_margin_phone,
@@ -470,6 +473,9 @@
 
             updateFrontBackTransforms();
         }
+
+        // Initialize the grid layout
+        mTaskGridLayoutAlgorithm.initialize(displayRect, windowRect);
     }
 
     /**
@@ -825,24 +831,30 @@
      * is what the view is measured and laid out with.
      */
     public TaskViewTransform getStackTransform(Task task, float stackScroll,
-            TaskViewTransform transformOut, TaskViewTransform frontTransform) {
+            TaskViewTransform transformOut, TaskViewTransform frontTransform,
+            boolean useGridLayout) {
         return getStackTransform(task, stackScroll, mFocusState, transformOut, frontTransform,
-                false /* forceUpdate */, false /* ignoreTaskOverrides */);
+                false /* forceUpdate */, false /* ignoreTaskOverrides */, useGridLayout);
     }
 
     public TaskViewTransform getStackTransform(Task task, float stackScroll,
             TaskViewTransform transformOut, TaskViewTransform frontTransform,
-            boolean ignoreTaskOverrides) {
+            boolean ignoreTaskOverrides, boolean useGridLayout) {
         return getStackTransform(task, stackScroll, mFocusState, transformOut, frontTransform,
-                false /* forceUpdate */, ignoreTaskOverrides);
+                false /* forceUpdate */, ignoreTaskOverrides, useGridLayout);
     }
 
     public TaskViewTransform getStackTransform(Task task, float stackScroll, int focusState,
             TaskViewTransform transformOut, TaskViewTransform frontTransform, boolean forceUpdate,
-            boolean ignoreTaskOverrides) {
+            boolean ignoreTaskOverrides, boolean useGridLayout) {
         if (mFreeformLayoutAlgorithm.isTransformAvailable(task, this)) {
             mFreeformLayoutAlgorithm.getTransform(task, transformOut, this);
             return transformOut;
+        } else if (useGridLayout) {
+            int taskIndex = mTaskIndexMap.get(task.key.id);
+            int taskCount = mTaskIndexMap.size();
+            mTaskGridLayoutAlgorithm.getTransform(taskIndex, taskCount, transformOut, this);
+            return transformOut;
         } else {
             // Return early if we have an invalid index
             int nonOverrideTaskProgress = mTaskIndexMap.get(task.key.id, -1);
@@ -867,7 +879,7 @@
             Rect windowOverrideRect) {
         TaskViewTransform transform = getStackTransform(task, stackScroll, mFocusState,
                 transformOut, frontTransform, true /* forceUpdate */,
-                false /* ignoreTaskOverrides */);
+                false /* ignoreTaskOverrides */, false /* useGridLayout */);
         return transformToScreenCoordinates(transform, windowOverrideRect);
     }
 
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 8c94c35..57fde67 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -92,6 +92,7 @@
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.recents.model.TaskStack;
 
+import com.android.systemui.recents.views.grid.GridTaskView;
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -158,6 +159,7 @@
     private int mTaskCornerRadiusPx;
     private int mDividerSize;
     private int mStartTimerIndicatorDuration;
+    private boolean mDraggingOverDockState;
 
     @ViewDebug.ExportedProperty(category="recents")
     private boolean mTaskViewsClipDirty = true;
@@ -499,13 +501,13 @@
 
             // Calculate the current and (if necessary) the target transform for the task
             transform = mLayoutAlgorithm.getStackTransform(task, curStackScroll,
-                    taskTransforms.get(i), frontTransform, ignoreTaskOverrides);
+                    taskTransforms.get(i), frontTransform, ignoreTaskOverrides, useGridLayout());
             if (useTargetStackScroll && !transform.visible) {
                 // If we have a target stack scroll and the task is not currently visible, then we
                 // just update the transform at the new scroll
                 // TODO: Optimize this
-                transformAtTarget = mLayoutAlgorithm.getStackTransform(task,
-                        targetStackScroll, new TaskViewTransform(), frontTransformAtTarget);
+                transformAtTarget = mLayoutAlgorithm.getStackTransform(task, targetStackScroll,
+                    new TaskViewTransform(), frontTransformAtTarget, useGridLayout());
                 if (transformAtTarget.visible) {
                     transform.copyFrom(transformAtTarget);
                 }
@@ -736,7 +738,7 @@
             } else {
                 mLayoutAlgorithm.getStackTransform(task, mStackScroller.getStackScroll(),
                         focusState, transform, null, true /* forceUpdate */,
-                        false /* ignoreTaskOverrides */);
+                        false /* ignoreTaskOverrides */, useGridLayout());
             }
             transform.visible = true;
         }
@@ -753,7 +755,7 @@
             Task task = tasks.get(i);
             TaskViewTransform transform = transformsOut.get(i);
             mLayoutAlgorithm.getStackTransform(task, stackScroll, focusState, transform, null,
-                    true /* forceUpdate */, ignoreTaskOverrides);
+                    true /* forceUpdate */, ignoreTaskOverrides, useGridLayout());
             transform.visible = true;
         }
     }
@@ -782,6 +784,11 @@
      * Updates the clip for each of the task views from back to front.
      */
     private void clipTaskViews() {
+        // We never clip task views in grid layout
+        if (Recents.getConfiguration().isGridEnabled) {
+            return;
+        }
+
         // Update the clip on each task child
         List<TaskView> taskViews = getTaskViews();
         TaskView tmpTv = null;
@@ -1506,7 +1513,11 @@
 
     @Override
     public TaskView createView(Context context) {
-        return (TaskView) mInflater.inflate(R.layout.recents_task_view, this, false);
+        if (Recents.getConfiguration().isGridEnabled) {
+            return (GridTaskView) mInflater.inflate(R.layout.recents_grid_task_view, this, false);
+        } else {
+            return (TaskView) mInflater.inflate(R.layout.recents_task_view, this, false);
+        }
     }
 
     @Override
@@ -1830,7 +1841,7 @@
         // Enlarge the dragged view slightly
         float finalScale = event.taskView.getScaleX() * DRAG_SCALE_FACTOR;
         mLayoutAlgorithm.getStackTransform(event.task, getScroller().getStackScroll(),
-                mTmpTransform, null);
+                mTmpTransform, null, useGridLayout());
         mTmpTransform.scale = finalScale;
         mTmpTransform.translationZ = mLayoutAlgorithm.mMaxTranslationZ + 1;
         mTmpTransform.dimAlpha = 0f;
@@ -1851,6 +1862,7 @@
                 Interpolators.FAST_OUT_SLOW_IN);
         boolean ignoreTaskOverrides = false;
         if (event.dropTarget instanceof TaskStack.DockState) {
+            mDraggingOverDockState = true;
             // Calculate the new task stack bounds that matches the window size that Recents will
             // have after the drop
             final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget;
@@ -1870,6 +1882,7 @@
             updateLayoutAlgorithm(true /* boundScroll */);
             ignoreTaskOverrides = true;
         } else {
+            mDraggingOverDockState = false;
             // Restore the pre-drag task stack bounds, but ensure that we don't layout the dragging
             // task view, so add it back to the ignore set after updating the layout
             removeIgnoreTask(event.task);
@@ -1880,6 +1893,7 @@
     }
 
     public final void onBusEvent(final DragEndEvent event) {
+        mDraggingOverDockState = false;
         // We don't handle drops on the dock regions
         if (event.dropTarget instanceof TaskStack.DockState) {
             // However, we do need to reset the overrides, since the last state of this task stack
@@ -2119,6 +2133,20 @@
     }
 
     /**
+     * Check whether we should use the grid layout.
+     * We use the grid layout for Recents iff all the following is true:
+     *  1. Grid-mode is enabled.
+     *  2. The activity is not in multi-window mode.
+     *  3. The user is not dragging a task view over the dock state.
+     * @return True if we should use the grid layout.
+     */
+    public boolean useGridLayout() {
+        return Recents.getConfiguration().isGridEnabled
+            && !((RecentsActivity) mContext).isInMultiWindowMode()
+            && !mDraggingOverDockState;
+    }
+
+    /**
      * Reads current system flags related to accessibility and screen pinning.
      */
     private void readSystemFlags() {
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 de7def6..9b30515 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -176,8 +176,7 @@
         super(context, attrs, defStyleAttr, defStyleRes);
         RecentsConfiguration config = Recents.getConfiguration();
         Resources res = context.getResources();
-        mViewBounds = new AnimateableViewBounds(this, res.getDimensionPixelSize(
-                R.dimen.recents_task_view_shadow_rounded_corners_radius));
+        mViewBounds = createOutlineProvider();
         if (config.fakeShadows) {
             setBackground(new FakeShadowDrawable(res, config));
         }
@@ -207,6 +206,12 @@
         return mTask;
     }
 
+    /* Create an outline provider to clip and outline the view */
+    protected AnimateableViewBounds createOutlineProvider() {
+        return new AnimateableViewBounds(this, mContext.getResources().getDimensionPixelSize(
+            R.dimen.recents_task_view_shadow_rounded_corners_radius));
+    }
+
     /** Returns the view bounds. */
     AnimateableViewBounds getViewBounds() {
         return mViewBounds;
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 c46adf1..780cbca 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -67,6 +67,7 @@
     private float mDimAlpha;
     private Matrix mScaleMatrix = new Matrix();
     private Paint mDrawPaint = new Paint();
+    private Paint mLockedPaint = new Paint();
     private Paint mBgFillPaint = new Paint();
     private BitmapShader mBitmapShader;
     private LightingColorFilter mLightingColorFilter = new LightingColorFilter(0xffffffff, 0);
@@ -102,6 +103,7 @@
         mCornerRadius = getResources().getDimensionPixelSize(
                 R.dimen.recents_task_view_rounded_corners_radius);
         mBgFillPaint.setColor(Color.WHITE);
+        mLockedPaint.setColor(Color.WHITE);
         mFullscreenThumbnailScale = context.getResources().getFraction(
                 com.android.internal.R.fraction.thumbnail_fullscreen_scale, 1, 1);
     }
@@ -133,7 +135,11 @@
                 (int) (mThumbnailRect.width() * mThumbnailScale));
         int thumbnailHeight = Math.min(viewHeight,
                 (int) (mThumbnailRect.height() * mThumbnailScale));
-        if (mBitmapShader != null && thumbnailWidth > 0 && thumbnailHeight > 0) {
+
+        if (mTask.isLocked) {
+            canvas.drawRoundRect(0, 0, viewWidth, viewHeight, mCornerRadius, mCornerRadius,
+                    mLockedPaint);
+        } else if (mBitmapShader != null && thumbnailWidth > 0 && thumbnailHeight > 0) {
             int topOffset = mTaskBar != null
                     ? mTaskBar.getHeight() - mCornerRadius
                     : 0;
@@ -200,11 +206,13 @@
                 ColorMatrixColorFilter filter = new ColorMatrixColorFilter(TMP_FILTER_COLOR_MATRIX);
                 mDrawPaint.setColorFilter(filter);
                 mBgFillPaint.setColorFilter(filter);
+                mLockedPaint.setColorFilter(filter);
             } else {
                 mLightingColorFilter.setColorMultiply(Color.argb(255, mul, mul, mul));
                 mDrawPaint.setColorFilter(mLightingColorFilter);
                 mDrawPaint.setColor(0xFFffffff);
                 mBgFillPaint.setColorFilter(mLightingColorFilter);
+                mLockedPaint.setColorFilter(mLightingColorFilter);
             }
         } else {
             int grey = mul;
@@ -299,6 +307,7 @@
         if (t.colorBackground != 0) {
             mBgFillPaint.setColor(t.colorBackground);
         }
+        mLockedPaint.setColor(t.colorPrimary);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/AnimateableGridViewBounds.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/AnimateableGridViewBounds.java
new file mode 100644
index 0000000..a029478
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/grid/AnimateableGridViewBounds.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2016 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.recents.views.grid;
+
+import android.view.View;
+import com.android.systemui.recents.views.AnimateableViewBounds;
+
+/* An outline provider for grid-based task views. */
+class AnimateableGridViewBounds extends AnimateableViewBounds {
+
+    public AnimateableGridViewBounds(View source, int cornerRadius) {
+        super(source, cornerRadius);
+    }
+
+    @Override
+    protected void updateClipBounds() {
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskView.java
new file mode 100644
index 0000000..da14e2b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/grid/GridTaskView.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2016 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.recents.views.grid;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import com.android.systemui.R;
+import com.android.systemui.recents.views.AnimateableViewBounds;
+import com.android.systemui.recents.views.TaskView;
+
+public class GridTaskView extends TaskView {
+    public GridTaskView(Context context) {
+        this(context, null);
+    }
+
+    public GridTaskView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public GridTaskView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public GridTaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    protected AnimateableViewBounds createOutlineProvider() {
+        return new AnimateableGridViewBounds(this, mContext.getResources().getDimensionPixelSize(
+            R.dimen.recents_task_view_shadow_rounded_corners_radius));
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
new file mode 100644
index 0000000..8d1bec8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2016 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.recents.views.grid;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import com.android.systemui.R;
+import com.android.systemui.recents.misc.Utilities;
+import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
+import com.android.systemui.recents.views.TaskViewTransform;
+
+public class TaskGridLayoutAlgorithm  {
+
+    private int mPaddingLeftRight;
+    private int mPaddingTopBottom;
+    private int mPaddingTaskView;
+
+    private Rect mDisplayRect;
+    private Rect mWindowRect;
+
+    private Rect mTaskGridRect;
+
+    public TaskGridLayoutAlgorithm(Context context) {
+        reloadOnConfigurationChange(context);
+    }
+
+    public void reloadOnConfigurationChange(Context context) {
+        Resources res = context.getResources();
+        mPaddingLeftRight = res.getDimensionPixelSize(R.dimen.recents_grid_padding_left_right);
+        mPaddingTopBottom = res.getDimensionPixelSize(R.dimen.recents_grid_padding_top_bottom);
+        mPaddingTaskView = res.getDimensionPixelSize(R.dimen.recents_grid_padding_task_view);
+
+        mTaskGridRect = new Rect();
+    }
+
+    public TaskViewTransform getTransform(int taskIndex, int taskAmount,
+        TaskViewTransform transformOut, TaskStackLayoutAlgorithm stackLayout) {
+
+        int taskPerLine = taskAmount < 2 ? 1 : (
+            taskAmount < 5 ? 2 : 3);
+
+        int taskWidth = (mDisplayRect.width() - mPaddingLeftRight * 2
+            - mPaddingTaskView * (taskPerLine - 1)) / taskPerLine;
+        int taskHeight = taskWidth * mDisplayRect.height() / mDisplayRect.width();
+        mTaskGridRect.set(0, 0, taskWidth, taskHeight);
+
+        int xIndex = taskIndex % taskPerLine;
+        int yIndex = taskIndex / taskPerLine;
+        int x = mPaddingLeftRight + (taskWidth + mPaddingTaskView) * xIndex;
+        int y = mPaddingTopBottom + (taskHeight + mPaddingTaskView) * yIndex;
+        float z = stackLayout.mMaxTranslationZ;
+
+        float dimAlpha = 0f;
+        float viewOutlineAlpha = 0f;
+
+        // Fill out the transform
+        transformOut.scale = 1f;
+        transformOut.alpha = 1f;
+        transformOut.translationZ = z;
+        transformOut.dimAlpha = dimAlpha;
+        transformOut.viewOutlineAlpha = viewOutlineAlpha;
+        transformOut.rect.set(mTaskGridRect);
+        transformOut.rect.offset(x, y);
+        Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
+        transformOut.visible = true;
+        return transformOut;
+    }
+
+    public void initialize(Rect displayRect, Rect windowRect) {
+        mDisplayRect = displayRect;
+        mWindowRect = windowRect;
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 252b5c9..5da652d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -2968,7 +2968,7 @@
 
         runPostCollapseRunnables();
         setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
-        showBouncer();
+        showBouncerIfKeyguard();
         recomputeDisableFlags(shouldAnimatIconHiding() /* animate */);
 
         // Trimming will happen later if Keyguard is showing - doing it here might cause a jank in
@@ -4542,13 +4542,17 @@
         return false;
     }
 
-    protected void showBouncer() {
+    private void showBouncerIfKeyguard() {
         if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
-            mWaitingForKeyguardExit = mStatusBarKeyguardViewManager.isShowing();
-            mStatusBarKeyguardViewManager.dismiss();
+            showBouncer();
         }
     }
 
+    protected void showBouncer() {
+        mWaitingForKeyguardExit = mStatusBarKeyguardViewManager.isShowing();
+        mStatusBarKeyguardViewManager.dismiss();
+    }
+
     private void instantExpandNotificationsPanel() {
 
         // Make our window larger and the panel expanded.
@@ -4646,7 +4650,7 @@
     public void onTrackingStopped(boolean expand) {
         if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
             if (!expand && !mUnlockMethodCache.canSkipBouncer()) {
-                showBouncer();
+                showBouncerIfKeyguard();
             }
         }
     }
@@ -4741,7 +4745,7 @@
                 || !mShowLockscreenNotifications || mFalsingManager.shouldEnforceBouncer();
         if (isLockscreenPublicMode(userId) && fullShadeNeedsBouncer) {
             mLeaveOpenOnKeyguardHide = true;
-            showBouncer();
+            showBouncerIfKeyguard();
             mDraggedDownRow = row;
             mPendingRemoteInputView = null;
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/SpTexts.java b/packages/SystemUI/src/com/android/systemui/volume/ConfigurableTexts.java
similarity index 74%
rename from packages/SystemUI/src/com/android/systemui/volume/SpTexts.java
rename to packages/SystemUI/src/com/android/systemui/volume/ConfigurableTexts.java
index d8e53db..20de5bb 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/SpTexts.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ConfigurableTexts.java
@@ -25,19 +25,23 @@
 import android.widget.TextView;
 
 /**
- * Capture initial sp values for registered textviews, and update properly when configuration
- * changes.
+ * Class for updating textviews on configuration change.
  */
-public class SpTexts {
+public class ConfigurableTexts {
 
     private final Context mContext;
     private final ArrayMap<TextView, Integer> mTexts = new ArrayMap<>();
+    private final ArrayMap<TextView, Integer> mTextLabels = new ArrayMap<>();
 
-    public SpTexts(Context context) {
+    public ConfigurableTexts(Context context) {
         mContext = context;
     }
 
     public int add(final TextView text) {
+        return add(text, -1);
+    }
+
+    public int add(final TextView text, final int labelResId) {
         if (text == null) return 0;
         final Resources res = mContext.getResources();
         final float fontScale = res.getConfiguration().fontScale;
@@ -55,6 +59,7 @@
                setTextSizeH(text, sp);
             }
         });
+        mTextLabels.put(text, labelResId);
         return sp;
     }
 
@@ -67,12 +72,25 @@
         text.setTextSize(TypedValue.COMPLEX_UNIT_SP, sp);
     }
 
+    private void setTextLabelH(TextView text, int labelResId) {
+        try {
+            if (labelResId >= 0) {
+                Util.setText(text, mContext.getString(labelResId));
+            }
+        } catch (Resources.NotFoundException e) {
+            // oh well.
+        }
+    }
+
     private final Runnable mUpdateAll = new Runnable() {
         @Override
         public void run() {
             for (int i = 0; i < mTexts.size(); i++) {
                 setTextSizeH(mTexts.keyAt(i), mTexts.valueAt(i));
             }
+            for (int i = 0; i < mTextLabels.size(); i++) {
+                setTextLabelH(mTextLabels.keyAt(i), mTextLabels.valueAt(i));
+            }
         }
     };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java b/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
index c6e4356..89bc757 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
@@ -36,7 +36,7 @@
 
     private final Context mContext;
     protected final LayoutInflater mInflater;
-    private final SpTexts mSpTexts;
+    private final ConfigurableTexts mConfigurableTexts;
 
     private Callback mCallback;
     protected Object mSelectedValue;
@@ -46,7 +46,7 @@
         mContext = context;
         mInflater = LayoutInflater.from(mContext);
         setOrientation(HORIZONTAL);
-        mSpTexts = new SpTexts(mContext);
+        mConfigurableTexts = new ConfigurableTexts(mContext);
     }
 
     public void setCallback(Callback callback) {
@@ -97,15 +97,11 @@
                 fireInteraction();
             }
         });
-        mSpTexts.add(b);
+        mConfigurableTexts.add(b, labelResId);
     }
 
-    public void updateLocale() {
-        for (int i = 0; i < getChildCount(); i++) {
-            final Button b = (Button) getChildAt(i);
-            final int labelResId = (Integer) b.getTag(LABEL_RES_KEY);
-            b.setText(labelResId);
-        }
+    public void update() {
+        mConfigurableTexts.update();
     }
 
     private void fireOnSelected(boolean fromClick) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
index 42b06b2..d23ebc1 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
@@ -46,6 +46,7 @@
 import android.transition.TransitionManager;
 import android.util.DisplayMetrics;
 import android.util.Log;
+import android.util.Slog;
 import android.util.SparseBooleanArray;
 import android.view.Gravity;
 import android.view.MotionEvent;
@@ -109,7 +110,7 @@
     private ViewGroup mDialogContentView;
     private ImageButton mExpandButton;
     private final List<VolumeRow> mRows = new ArrayList<>();
-    private SpTexts mSpTexts;
+    private ConfigurableTexts mConfigurableTexts;
     private final SparseBooleanArray mDynamic = new SparseBooleanArray();
     private final KeyguardManager mKeyguard;
     private final AudioManager mAudioManager;
@@ -128,7 +129,6 @@
     private boolean mExpanded;
 
     private int mActiveStream;
-    private boolean mShowHeaders = VolumePrefs.DEFAULT_SHOW_HEADERS;
     private boolean mAutomute = VolumePrefs.DEFAULT_ENABLE_AUTOMUTE;
     private boolean mSilentMode = VolumePrefs.DEFAULT_ENABLE_SILENT_MODE;
     private State mState;
@@ -173,7 +173,7 @@
     private void initDialog() {
         mDialog = new CustomDialog(mContext);
 
-        mSpTexts = new SpTexts(mContext);
+        mConfigurableTexts = new ConfigurableTexts(mContext);
         mHovering = false;
         mShowing = false;
         mWindow = mDialog.getWindow();
@@ -294,12 +294,6 @@
         mHandler.obtainMessage(H.SET_STREAM_IMPORTANT, stream, important ? 1 : 0).sendToTarget();
     }
 
-    public void setShowHeaders(boolean showHeaders) {
-        if (showHeaders == mShowHeaders) return;
-        mShowHeaders = showHeaders;
-        mHandler.sendEmptyMessage(H.RECHECK_ALL);
-    }
-
     public void setAutomute(boolean automute) {
         if (mAutomute == automute) return;
         mAutomute = automute;
@@ -357,7 +351,6 @@
         writer.println(mExpandButtonAnimationRunning);
         writer.print("  mActiveStream: "); writer.println(mActiveStream);
         writer.print("  mDynamic: "); writer.println(mDynamic);
-        writer.print("  mShowHeaders: "); writer.println(mShowHeaders);
         writer.print("  mAutomute: "); writer.println(mAutomute);
         writer.print("  mSilentMode: "); writer.println(mSilentMode);
         writer.print("  mCollapseTime: "); writer.println(mCollapseTime);
@@ -385,11 +378,9 @@
         row.view.setTag(row);
         row.header = (TextView) row.view.findViewById(R.id.volume_row_header);
         row.header.setId(20 * row.stream);
-        mSpTexts.add(row.header);
         row.slider = (SeekBar) row.view.findViewById(R.id.volume_row_slider);
         row.slider.setOnSeekBarChangeListener(new VolumeSeekBarChangeListener(row));
         row.anim = null;
-        row.cachedShowHeaders = VolumePrefs.DEFAULT_SHOW_HEADERS;
 
         // forward events above the slider into the slider
         row.view.setOnTouchListener(new OnTouchListener() {
@@ -617,8 +608,8 @@
             final boolean isActive = row == activeRow;
             final boolean shouldBeVisible = shouldBeVisibleH(row, isActive);
             Util.setVisOrGone(row.view, shouldBeVisible);
+            Util.setVisOrGone(row.header, shouldBeVisible);
             if (row.view.isShown()) {
-                updateVolumeRowHeaderVisibleH(row);
                 updateVolumeRowSliderTintH(row, isActive);
             }
         }
@@ -731,11 +722,9 @@
             row.slider.setMax(max);
         }
 
-        // update header visible
-        updateVolumeRowHeaderVisibleH(row);
-
         // update header text
-        Util.setText(row.header, ss.name);
+        Util.setText(row.header, getStreamLabelH(ss));
+        mConfigurableTexts.add(row.header, ss.name);
 
         // update icon
         final boolean iconEnabled = (mAutomute || ss.muteSupported) && !zenMuted;
@@ -768,31 +757,31 @@
                 if (isRingVibrate) {
                     row.icon.setContentDescription(mContext.getString(
                             R.string.volume_stream_content_description_unmute,
-                            ss.name));
+                            getStreamLabelH(ss)));
                 } else {
                     if (mController.hasVibrator()) {
                         row.icon.setContentDescription(mContext.getString(
                                 R.string.volume_stream_content_description_vibrate,
-                                ss.name));
+                                getStreamLabelH(ss)));
                     } else {
                         row.icon.setContentDescription(mContext.getString(
                                 R.string.volume_stream_content_description_mute,
-                                ss.name));
+                                getStreamLabelH(ss)));
                     }
                 }
             } else {
                 if (ss.muted || mAutomute && ss.level == 0) {
                    row.icon.setContentDescription(mContext.getString(
                            R.string.volume_stream_content_description_unmute,
-                           ss.name));
+                           getStreamLabelH(ss)));
                 } else {
                     row.icon.setContentDescription(mContext.getString(
                             R.string.volume_stream_content_description_mute,
-                            ss.name));
+                            getStreamLabelH(ss)));
                 }
             }
         } else {
-            row.icon.setContentDescription(ss.name);
+            row.icon.setContentDescription(getStreamLabelH(ss));
         }
 
         // update slider
@@ -802,15 +791,6 @@
         updateVolumeRowSliderH(row, enableSlider, vlevel);
     }
 
-    private void updateVolumeRowHeaderVisibleH(VolumeRow row) {
-        final boolean dynamic = row.ss != null && row.ss.dynamic;
-        final boolean showHeaders = mExpanded && (mShowHeaders || dynamic);
-        if (row.cachedShowHeaders != showHeaders) {
-            row.cachedShowHeaders = showHeaders;
-            Util.setVisOrGone(row.header, showHeaders);
-        }
-    }
-
     private void updateVolumeRowSliderTintH(VolumeRow row, boolean isActive) {
         if (isActive && mExpanded) {
             row.slider.requestFocus();
@@ -920,6 +900,18 @@
         rescheduleTimeoutH();
     }
 
+    private String getStreamLabelH(StreamState ss) {
+        if (ss.remoteLabel != null) {
+            return ss.remoteLabel;
+        }
+        try {
+            return mContext.getString(ss.name);
+        } catch (Resources.NotFoundException e) {
+            Slog.e(TAG, "Can't find translation for stream " + ss);
+            return "";
+        }
+    }
+
     private AutoTransition getTransistion() {
         AutoTransition transition = new AutoTransition();
         transition.setDuration(mExpandButtonAnimationDuration);
@@ -995,7 +987,7 @@
                 mDensity = density;
             }
             updateWindowWidthH();
-            mSpTexts.update();
+            mConfigurableTexts.update();
             mZenFooter.onConfigurationChanged();
         }
 
@@ -1125,7 +1117,7 @@
                 if (mShowing) {
                     event.getText().add(mContext.getString(
                             R.string.volume_dialog_accessibility_shown_message,
-                            getActiveRow().ss.name));
+                            getStreamLabelH(getActiveRow().ss)));
                     return true;
                 }
             }
@@ -1253,7 +1245,6 @@
         private int cachedIconRes;
         private ColorStateList cachedSliderTint;
         private int iconState;  // from Events
-        private boolean cachedShowHeaders = VolumePrefs.DEFAULT_SHOW_HEADERS;
         private ObjectAnimator anim;  // slider progress animation for non-touch-related updates
         private int animTargetProgress;
         private int lastAudibleLevel = 1;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
index bb5632b..17dd8d6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.volume;
 
+import android.annotation.IntegerRes;
 import android.app.NotificationManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -41,6 +42,7 @@
 import android.os.Vibrator;
 import android.provider.Settings;
 import android.service.notification.Condition;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -67,19 +69,20 @@
     private static final int DYNAMIC_STREAM_START_INDEX = 100;
     private static final int VIBRATE_HINT_DURATION = 50;
 
-    private static final int[] STREAMS = {
-        AudioSystem.STREAM_ALARM,
-        AudioSystem.STREAM_BLUETOOTH_SCO,
-        AudioSystem.STREAM_DTMF,
-        AudioSystem.STREAM_MUSIC,
-        AudioSystem.STREAM_NOTIFICATION,
-        AudioSystem.STREAM_RING,
-        AudioSystem.STREAM_SYSTEM,
-        AudioSystem.STREAM_SYSTEM_ENFORCED,
-        AudioSystem.STREAM_TTS,
-        AudioSystem.STREAM_VOICE_CALL,
-        AudioSystem.STREAM_ACCESSIBILITY,
-    };
+    private static final ArrayMap<Integer, Integer> STREAMS = new ArrayMap<>();
+    static {
+        STREAMS.put(AudioSystem.STREAM_ALARM, R.string.stream_alarm);
+        STREAMS.put(AudioSystem.STREAM_BLUETOOTH_SCO, R.string.stream_bluetooth_sco);
+        STREAMS.put(AudioSystem.STREAM_DTMF, R.string.stream_dtmf);
+        STREAMS.put(AudioSystem.STREAM_MUSIC, R.string.stream_music);
+        STREAMS.put(AudioSystem.STREAM_NOTIFICATION, R.string.stream_notification);
+        STREAMS.put(AudioSystem.STREAM_RING, R.string.stream_ring);
+        STREAMS.put(AudioSystem.STREAM_SYSTEM, R.string.stream_system);
+        STREAMS.put(AudioSystem.STREAM_SYSTEM_ENFORCED, R.string.stream_system_enforced);
+        STREAMS.put(AudioSystem.STREAM_TTS, R.string.stream_tts);
+        STREAMS.put(AudioSystem.STREAM_VOICE_CALL, R.string.stream_voice_call);
+        STREAMS.put(AudioSystem.STREAM_ACCESSIBILITY, R.string.stream_accessibility);
+    }
 
     private final HandlerThread mWorkerThread;
     private final W mWorker;
@@ -92,7 +95,6 @@
     private final MediaSessions mMediaSessions;
     private final C mCallbacks = new C();
     private final State mState = new State();
-    private final String[] mStreamTitles;
     private final MediaSessionsCallbacks mMediaSessionsCallbacksW = new MediaSessionsCallbacks();
     private final Vibrator mVibrator;
     private final boolean mHasVibrator;
@@ -120,26 +122,6 @@
         mObserver = new SettingObserver(mWorker);
         mObserver.init();
         mReceiver.init();
-        final String[] titles =
-                mContext.getResources().getStringArray(R.array.volume_stream_titles);
-        if (STREAMS.length == titles.length) {
-            mStreamTitles = titles;
-        } else if (STREAMS.length > titles.length) {
-            Log.e(TAG, String.format("Missing stream titles (found %d, expected %d): "
-                    + " invalid resources for volume_stream_titles",
-                    titles.length, STREAMS.length));
-            mStreamTitles = new String[STREAMS.length];
-            System.arraycopy(titles, 0, mStreamTitles, 0, titles.length);
-            for (int i = titles.length ; i < STREAMS.length ; i++) {
-                mStreamTitles[i] = "";
-            }
-        } else { // STREAMS.length < titles.length
-            Log.e(TAG, String.format("Too many stream titles (found %d, expected %d): "
-                    + " invalid resources for volume_stream_titles",
-                    titles.length, STREAMS.length));
-            mStreamTitles = new String[STREAMS.length];
-            System.arraycopy(titles, 0, mStreamTitles, 0, STREAMS.length);
-        }
         mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
         mHasVibrator = mVibrator != null && mVibrator.hasVibrator();
     }
@@ -385,14 +367,14 @@
     }
 
     private void onGetStateW() {
-        for (int stream : STREAMS) {
+        for (int stream : STREAMS.keySet()) {
             updateStreamLevelW(stream, getAudioManagerStreamVolume(stream));
             streamStateW(stream).levelMin = getAudioManagerStreamMinVolume(stream);
             streamStateW(stream).levelMax = getAudioManagerStreamMaxVolume(stream);
             updateStreamMuteW(stream, mAudio.isStreamMute(stream));
             final StreamState ss = streamStateW(stream);
             ss.muteSupported = mAudio.isStreamAffectedByMute(stream);
-            ss.name = mStreamTitles[stream];
+            ss.name = STREAMS.get(stream);
             checkRoutedToBluetoothW(stream);
         }
         updateRingerModeExternalW(mAudio.getRingerMode());
@@ -912,8 +894,9 @@
                 ss.level = pi.getCurrentVolume();
                 changed = true;
             }
-            if (!Objects.equals(ss.name, name)) {
-                ss.name = name;
+            if (!Objects.equals(ss.remoteLabel, name)) {
+                ss.name = -1;
+                ss.remoteLabel = name;
                 changed = true;
             }
             if (changed) {
@@ -975,7 +958,8 @@
         public int levelMax;
         public boolean muted;
         public boolean muteSupported;
-        public String name;
+        public @IntegerRes int name;
+        public String remoteLabel;
         public boolean routedToBluetooth;
 
         public StreamState copy() {
@@ -987,6 +971,7 @@
             rt.muted = muted;
             rt.muteSupported = muteSupported;
             rt.name = name;
+            rt.remoteLabel = remoteLabel;
             rt.routedToBluetooth = routedToBluetooth;
             return rt;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
index 995ecae..4e4832c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
@@ -38,7 +38,7 @@
     private static final String TAG = Util.logTag(ZenFooter.class);
 
     private final Context mContext;
-    private final SpTexts mSpTexts;
+    private final ConfigurableTexts mConfigurableTexts;
 
     private ImageView mIcon;
     private TextView mSummaryLine1;
@@ -51,7 +51,7 @@
     public ZenFooter(Context context, AttributeSet attrs) {
         super(context, attrs);
         mContext = context;
-        mSpTexts = new SpTexts(mContext);
+        mConfigurableTexts = new ConfigurableTexts(mContext);
         final LayoutTransition layoutTransition = new LayoutTransition();
         layoutTransition.setDuration(new ValueAnimator().getDuration() / 2);
         setLayoutTransition(layoutTransition);
@@ -64,9 +64,9 @@
         mSummaryLine1 = (TextView) findViewById(R.id.volume_zen_summary_line_1);
         mSummaryLine2 = (TextView) findViewById(R.id.volume_zen_summary_line_2);
         mEndNowButton = (TextView) findViewById(R.id.volume_zen_end_now);
-        mSpTexts.add(mSummaryLine1);
-        mSpTexts.add(mSummaryLine2);
-        mSpTexts.add(mEndNowButton);
+        mConfigurableTexts.add(mSummaryLine1);
+        mConfigurableTexts.add(mSummaryLine2);
+        mConfigurableTexts.add(mEndNowButton, R.string.volume_zen_end_now);
     }
 
     public void init(final ZenModeController controller) {
@@ -130,8 +130,7 @@
     }
 
     public void onConfigurationChanged() {
-        Util.setText(mEndNowButton, mContext.getString(R.string.volume_zen_end_now));
-        mSpTexts.update();
+        mConfigurableTexts.update();
     }
 
     private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index 12a00e9..98e89e4 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -92,7 +92,7 @@
     private final ZenPrefs mPrefs;
     private final TransitionHelper mTransitionHelper = new TransitionHelper();
     private final Uri mForeverId;
-    private final SpTexts mSpTexts;
+    private final ConfigurableTexts mConfigurableTexts;
 
     private String mTag = TAG + "/" + Integer.toHexString(System.identityHashCode(this));
 
@@ -131,7 +131,7 @@
         mPrefs = new ZenPrefs();
         mInflater = LayoutInflater.from(mContext.getApplicationContext());
         mForeverId = Condition.newId(mContext).appendPath("forever").build();
-        mSpTexts = new SpTexts(mContext);
+        mConfigurableTexts = new ConfigurableTexts(mContext);
         mVoiceCapable = Util.isVoiceCapable(mContext);
         mZenModeConditionLayoutId = R.layout.zen_mode_condition;
         mZenModeButtonLayoutId = R.layout.zen_mode_button;
@@ -175,7 +175,6 @@
         createZenButtons();
         mZenIntroduction = findViewById(R.id.zen_introduction);
         mZenIntroductionMessage = (TextView) findViewById(R.id.zen_introduction_message);
-        mSpTexts.add(mZenIntroductionMessage);
         mZenIntroductionConfirm = findViewById(R.id.zen_introduction_confirm);
         mZenIntroductionConfirm.setOnClickListener(new OnClickListener() {
             @Override
@@ -193,7 +192,7 @@
                 }
             }
         });
-        mSpTexts.add(mZenIntroductionCustomize);
+        mConfigurableTexts.add(mZenIntroductionCustomize, R.string.zen_priority_customize_button);
 
         mZenConditions = (LinearLayout) findViewById(R.id.zen_conditions);
         mZenAlarmWarning = (TextView) findViewById(R.id.zen_alarm_warning);
@@ -204,11 +203,9 @@
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
+        mConfigurableTexts.update();
         if (mZenButtons != null) {
-            mZenButtons.updateLocale();
-        }
-        if (mZenIntroductionCustomize != null) {
-            mZenIntroductionCustomize.setText(R.string.zen_priority_customize_button);
+            mZenButtons.update();
         }
     }
 
@@ -341,10 +338,6 @@
         hideAllConditions();
     }
 
-    public void updateLocale() {
-        mZenButtons.updateLocale();
-    }
-
     private void setExitCondition(Condition exitCondition) {
         if (Objects.equals(mExitCondition, exitCondition)) return;
         mExitCondition = exitCondition;
@@ -439,9 +432,11 @@
         mZenButtons.setVisibility(mHidden ? GONE : VISIBLE);
         mZenIntroduction.setVisibility(introduction ? VISIBLE : GONE);
         if (introduction) {
-            mZenIntroductionMessage.setText(zenImportant ? R.string.zen_priority_introduction
+            mConfigurableTexts.add(mZenIntroductionMessage, zenImportant
+                    ? R.string.zen_priority_introduction
                     : mVoiceCapable ? R.string.zen_silence_introduction_voice
                     : R.string.zen_silence_introduction);
+            mConfigurableTexts.update();
             mZenIntroductionCustomize.setVisibility(zenImportant ? VISIBLE : GONE);
         }
         final String warning = computeAlarmWarningText(zenNone);
@@ -655,11 +650,11 @@
         }
         if (tag.line1 == null) {
             tag.line1 = (TextView) row.findViewById(android.R.id.text1);
-            mSpTexts.add(tag.line1);
+            mConfigurableTexts.add(tag.line1);
         }
         if (tag.line2 == null) {
             tag.line2 = (TextView) row.findViewById(android.R.id.text2);
-            mSpTexts.add(tag.line2);
+            mConfigurableTexts.add(tag.line2);
         }
         final String line1 = !TextUtils.isEmpty(condition.line1) ? condition.line1
                 : condition.summary;
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index ecf174f..e96ea19 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -44,7 +44,7 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-test \
-    mockito-target-minus-junit4 \
+    mockito-updated-target-minus-junit4 \
     SystemUI-proto \
     SystemUI-tags
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index 008580a..d1d7520 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -37,6 +37,7 @@
 
     @Before
     public void SysuiSetup() throws Exception {
+        System.setProperty("dexmaker.share_classloader", "true");
         mContext = new TestableContext(InstrumentationRegistry.getTargetContext(), this);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
index 8b99d72..c3948258 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -214,6 +214,16 @@
 
     @Test
     @UiThreadTest
+    public void testSuppressingPulse_doesntCrash() {
+        mMachine.requestState(INITIALIZED);
+
+        mMachine.requestState(DOZE);
+        mMachine.requestState(DOZE_REQUEST_PULSE);
+        mMachine.requestState(DOZE_PULSE_DONE);
+    }
+
+    @Test
+    @UiThreadTest
     public void testScreen_offInDoze() {
         mMachine.requestState(INITIALIZED);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 136e7c0..6fe7768 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -49,6 +49,11 @@
 import java.util.List;
 
 import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -282,14 +287,11 @@
         ArgumentCaptor<Boolean> dataOutArg = ArgumentCaptor.forClass(Boolean.class);
 
         Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setMobileDataIndicators(
-                    ArgumentCaptor.forClass(IconState.class).capture(),
+                    any(),
                     iconArg.capture(),
-                    ArgumentCaptor.forClass(Integer.class).capture(),
+                    anyInt(),
                     typeIconArg.capture(), dataInArg.capture(), dataOutArg.capture(),
-                    ArgumentCaptor.forClass(String.class).capture(),
-                    ArgumentCaptor.forClass(String.class).capture(),
-                    ArgumentCaptor.forClass(Boolean.class).capture(),
-                    ArgumentCaptor.forClass(Integer.class).capture());
+                    anyString(), anyString(), anyBoolean(), anyInt());
         IconState iconState = iconArg.getValue();
         assertEquals("Visibility in, quick settings", visible, iconState.visible);
         assertEquals("Signal icon in, quick settings", icon, iconState.icon);
@@ -307,21 +309,50 @@
         // TODO: Verify all fields.
         Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setMobileDataIndicators(
                 iconArg.capture(),
-                ArgumentCaptor.forClass(IconState.class).capture(),
+                any(),
                 typeIconArg.capture(),
-                ArgumentCaptor.forClass(Integer.class).capture(),
-                ArgumentCaptor.forClass(Boolean.class).capture(),
-                ArgumentCaptor.forClass(Boolean.class).capture(),
-                ArgumentCaptor.forClass(String.class).capture(),
-                ArgumentCaptor.forClass(String.class).capture(),
-                ArgumentCaptor.forClass(Boolean.class).capture(),
-                ArgumentCaptor.forClass(Integer.class).capture());
+                anyInt(), anyBoolean(), anyBoolean(), anyString(), anyString(), anyBoolean(),
+                anyInt());
         IconState iconState = iconArg.getValue();
 
         assertEquals("Data icon in status bar", typeIcon, (int) typeIconArg.getValue());
         assertEquals("Visibility in status bar", visible, iconState.visible);
     }
 
+    protected void verifyLastMobileDataIndicators(boolean visible, int icon, int typeIcon,
+            boolean qsVisible, int qsIcon, int qsTypeIcon, boolean dataIn, boolean dataOut) {
+        ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class);
+        ArgumentCaptor<Integer> typeIconArg = ArgumentCaptor.forClass(Integer.class);
+        ArgumentCaptor<IconState> qsIconArg = ArgumentCaptor.forClass(IconState.class);
+        ArgumentCaptor<Integer> qsTypeIconArg = ArgumentCaptor.forClass(Integer.class);
+        ArgumentCaptor<Boolean> dataInArg = ArgumentCaptor.forClass(Boolean.class);
+        ArgumentCaptor<Boolean> dataOutArg = ArgumentCaptor.forClass(Boolean.class);
+
+        Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setMobileDataIndicators(
+                iconArg.capture(),
+                qsIconArg.capture(),
+                typeIconArg.capture(),
+                qsTypeIconArg.capture(),
+                dataInArg.capture(),
+                dataOutArg.capture(),
+                anyString(), anyString(), anyBoolean(), anyInt());
+
+        IconState iconState = iconArg.getValue();
+
+        assertEquals("Data icon in status bar", typeIcon, (int) typeIconArg.getValue());
+        assertEquals("Signal icon in status bar", icon, iconState.icon);
+        assertEquals("Visibility in status bar", visible, iconState.visible);
+
+        iconState = qsIconArg.getValue();
+        assertEquals("Visibility in quick settings", qsVisible, iconState.visible);
+        assertEquals("Signal icon in quick settings", qsIcon, iconState.icon);
+        assertEquals("Data icon in quick settings", qsTypeIcon, (int) qsTypeIconArg.getValue());
+        assertEquals("Data direction in in quick settings", dataIn,
+                (boolean) dataInArg.getValue());
+        assertEquals("Data direction out in quick settings", dataOut,
+                (boolean) dataOutArg.getValue());
+    }
+
    protected void assertNetworkNameEquals(String expected) {
        assertEquals("Network name", expected, mMobileSignalController.getState().networkName);
    }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index 55ec572..4f961ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -29,8 +29,8 @@
 
         verifyLastMobileDataIndicators(true,
                 TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[1][DEFAULT_LEVEL],
-                TelephonyIcons.ROAMING_ICON);
-        verifyLastQsMobileDataIndicators(true,
+                TelephonyIcons.ROAMING_ICON,
+                true,
                 TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH[1][DEFAULT_LEVEL],
                 TelephonyIcons.QS_DATA_R, false, false);
     }
@@ -183,14 +183,13 @@
     private void testDataActivity(int direction, boolean in, boolean out) {
         updateDataActivity(direction);
 
-        verifyLastMobileDataIndicators(true, DEFAULT_SIGNAL_STRENGTH, DEFAULT_ICON);
-        verifyLastQsMobileDataIndicators(true, DEFAULT_QS_SIGNAL_STRENGTH,
-                DEFAULT_QS_ICON, in, out);
+        verifyLastMobileDataIndicators(true, DEFAULT_SIGNAL_STRENGTH, DEFAULT_ICON, true,
+                DEFAULT_QS_SIGNAL_STRENGTH, DEFAULT_QS_ICON, in, out);
     }
 
     private void verifyDataIndicators(int dataIcon, int qsDataIcon) {
-        verifyLastMobileDataIndicators(true, DEFAULT_SIGNAL_STRENGTH, dataIcon);
-        verifyLastQsMobileDataIndicators(true, DEFAULT_QS_SIGNAL_STRENGTH, qsDataIcon, false,
+        verifyLastMobileDataIndicators(true, DEFAULT_SIGNAL_STRENGTH, dataIcon,
+                true, DEFAULT_QS_SIGNAL_STRENGTH, qsDataIcon, false,
                 false);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index fc7f942..ed32f65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -17,6 +17,9 @@
 
 import static junit.framework.Assert.assertEquals;
 
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class NetworkControllerWifiTest extends NetworkControllerBaseTest {
@@ -78,6 +81,9 @@
         verifyLastQsWifiIcon(true, true,
                 WifiIcons.QS_WIFI_SIGNAL_STRENGTH[1][testLevel], testSsid);
 
+        // Set to different activity state first to ensure a callback happens.
+        setWifiActivity(WifiManager.DATA_ACTIVITY_IN);
+
         setWifiActivity(WifiManager.DATA_ACTIVITY_NONE);
         verifyLastQsDataDirection(false, false);
         setWifiActivity(WifiManager.DATA_ACTIVITY_IN);
@@ -148,8 +154,7 @@
         ArgumentCaptor<Boolean> outArg = ArgumentCaptor.forClass(Boolean.class);
 
         Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setWifiIndicators(
-                Mockito.anyBoolean(), Mockito.any(IconState.class), Mockito.any(IconState.class),
-                inArg.capture(), outArg.capture(), Mockito.anyString());
+                anyBoolean(), any(), any(), inArg.capture(), outArg.capture(), any());
         assertEquals("WiFi data in, in quick settings", in, (boolean) inArg.getValue());
         assertEquals("WiFi data out, in quick settings", out, (boolean) outArg.getValue());
     }
@@ -161,9 +166,7 @@
         ArgumentCaptor<String> descArg = ArgumentCaptor.forClass(String.class);
 
         Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setWifiIndicators(
-                enabledArg.capture(), Mockito.any(IconState.class),
-                iconArg.capture(), Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
+                enabledArg.capture(), any(), iconArg.capture(), anyBoolean(), anyBoolean(),
                 descArg.capture());
         IconState iconState = iconArg.getValue();
         assertEquals("WiFi enabled, in quick settings", enabled, (boolean) enabledArg.getValue());
@@ -176,8 +179,7 @@
         ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class);
 
         Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setWifiIndicators(
-                Mockito.anyBoolean(), iconArg.capture(), Mockito.any(IconState.class),
-                Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.anyString());
+                anyBoolean(), iconArg.capture(), any(), anyBoolean(), anyBoolean(), any());
         IconState iconState = iconArg.getValue();
         assertEquals("WiFi visible, in status bar", visible, iconState.visible);
         assertEquals("WiFi signal, in status bar", icon, iconState.icon);
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index ef9d8f2..ec8c67b 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -3105,6 +3105,26 @@
     // PACKAGE: The package name of the app the permission was revoked for
     ACTION_PERMISSION_REVOKE_READ_PHONE_NUMBER = 739;
 
+    // ACTION: A captive portal was detected during network validation
+    // CATEGORY: NOTIFICATION
+    // OS: N-MR2
+    NOTIFICATION_NETWORK_SIGN_IN = 740;
+
+    // ACTION: An unvalidated network without Internet was selected by the user
+    // CATEGORY: NOTIFICATION
+    // OS: N-MR2
+    NOTIFICATION_NETWORK_NO_INTERNET = 741;
+
+    // ACTION: A validated network failed revalidation and lost Internet access
+    // CATEGORY: NOTIFICATION
+    // OS: N-MR2
+    NOTIFICATION_NETWORK_LOST_INTERNET = 742;
+
+    // ACTION: The system default network switched to a different network
+    // CATEGORY: NOTIFICATION
+    // OS: N-MR2
+    NOTIFICATION_NETWORK_SWITCH = 743;
+
     // ---- End O Constants, all O constants go above this line ----
 
     // Add new aosp constants above this line.
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index df71ced..386fbc9 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -560,7 +560,7 @@
 
     @Override
     public void interrupt(int userId) {
-        CopyOnWriteArrayList<Service> services;
+        List<IAccessibilityServiceClient> interfacesToInterrupt;
         synchronized (mLock) {
             // We treat calls from a profile as if made by its parent as profiles
             // share the accessibility state of the parent. The call below
@@ -571,15 +571,24 @@
             if (resolvedUserId != mCurrentUserId) {
                 return;
             }
-            services = getUserStateLocked(resolvedUserId).mBoundServices;
+            List<Service> services = getUserStateLocked(resolvedUserId).mBoundServices;
+            int numServices = services.size();
+            interfacesToInterrupt = new ArrayList<>(numServices);
+            for (int i = 0; i < numServices; i++) {
+                Service service = services.get(i);
+                IBinder a11yServiceBinder = service.mService;
+                IAccessibilityServiceClient a11yServiceInterface = service.mServiceInterface;
+                if ((a11yServiceBinder != null) && (a11yServiceInterface != null)) {
+                    interfacesToInterrupt.add(a11yServiceInterface);
+                }
+            }
         }
-        for (int i = 0, count = services.size(); i < count; i++) {
-            Service service = services.get(i);
+        for (int i = 0, count = interfacesToInterrupt.size(); i < count; i++) {
             try {
-                service.mServiceInterface.onInterrupt();
+                interfacesToInterrupt.get(i).onInterrupt();
             } catch (RemoteException re) {
-                Slog.e(LOG_TAG, "Error during sending interrupt request to "
-                    + service.mService, re);
+                Slog.e(LOG_TAG, "Error sending interrupt request to "
+                        + interfacesToInterrupt.get(i), re);
             }
         }
     }
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index b0c5603..d3110a4 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -105,6 +105,7 @@
     private static final int MESSAGE_USER_UNLOCKED = 301;
     private static final int MESSAGE_ADD_PROXY_DELAYED = 400;
     private static final int MESSAGE_BIND_PROFILE_SERVICE = 401;
+
     private static final int MAX_SAVE_RETRIES = 3;
     private static final int MAX_ERROR_RESTART_RETRIES = 6;
 
@@ -229,7 +230,8 @@
                     } finally {
                         mBluetoothLock.readLock().unlock();
                     }
-                    Slog.d(TAG, "Airplane Mode change - current state: " + st);
+                    Slog.d(TAG, "Airplane Mode change - current state: " +
+                              BluetoothAdapter.nameForState(st));
 
                     if (isAirplaneModeOn()) {
                         // Clear registered LE apps to force shut-off
@@ -426,6 +428,10 @@
     public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
                                                 "Need BLUETOOTH permission");
+        if (callback == null) {
+          Slog.w(TAG, "registerStateChangeCallback: Callback is null!");
+          return;
+        }
         Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_STATE_CHANGE_CALLBACK);
         msg.obj = callback;
         mHandler.sendMessage(msg);
@@ -434,6 +440,10 @@
     public void unregisterStateChangeCallback(IBluetoothStateChangeCallback callback) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
                                                 "Need BLUETOOTH permission");
+        if (callback == null) {
+          Slog.w(TAG, "unregisterStateChangeCallback: Callback is null!");
+          return;
+        }
         Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK);
         msg.obj = callback;
         mHandler.sendMessage(msg);
@@ -460,7 +470,7 @@
     public int getState() {
         if ((Binder.getCallingUid() != Process.SYSTEM_UID) &&
                 (!checkIfCallerIsForegroundUser())) {
-            Slog.w(TAG, "getState(): not allowed for non-active and non system user");
+            Slog.w(TAG, "getState(): report OFF for non-active and non system user");
             return BluetoothAdapter.STATE_OFF;
         }
 
@@ -712,7 +722,8 @@
 
         if (DBG) {
             Slog.d(TAG,"enable():  mBluetooth =" + mBluetooth +
-                    " mBinding = " + mBinding + " mState = " + mState);
+                    " mBinding = " + mBinding + " mState = " +
+                    BluetoothAdapter.nameForState(mState));
         }
 
         synchronized(mReceiver) {
@@ -800,7 +811,7 @@
     public void unbindAndFinish() {
         if (DBG) {
             Slog.d(TAG,"unbindAndFinish(): " + mBluetooth +
-                " mBinding = " + mBinding);
+                " mBinding = " + mBinding + " mUnbinding = " + mUnbinding);
         }
 
         try {
@@ -816,16 +827,13 @@
                 } catch (RemoteException re) {
                     Slog.e(TAG, "Unable to unregister BluetoothCallback",re);
                 }
-
-                if (DBG) Slog.d(TAG, "Sending unbind request.");
                 mBluetoothBinder = null;
                 mBluetooth = null;
-                //Unbind
                 mContext.unbindService(mConnection);
                 mUnbinding = false;
                 mBinding = false;
             } else {
-                mUnbinding=false;
+                mUnbinding = false;
             }
             mBluetoothGatt = null;
         } finally {
@@ -1101,7 +1109,6 @@
      * Inform BluetoothAdapter instances that Adapter service is up
      */
     private void sendBluetoothServiceUpCallback() {
-        if (DBG) Slog.d(TAG,"Calling onBluetoothServiceUp callbacks");
         try {
             int n = mCallbacks.beginBroadcast();
             Slog.d(TAG,"Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
@@ -1120,7 +1127,6 @@
      * Inform BluetoothAdapter instances that Adapter service is down
      */
     private void sendBluetoothServiceDownCallback() {
-        if (DBG) Slog.d(TAG,"Calling onBluetoothServiceDown callbacks");
         try {
             int n = mCallbacks.beginBroadcast();
             Slog.d(TAG,"Broadcasting onBluetoothServiceDown() to " + n + " receivers.");
@@ -1192,34 +1198,33 @@
     }
 
     private class BluetoothServiceConnection implements ServiceConnection {
-        public void onServiceConnected(ComponentName className, IBinder service) {
-            if (DBG) Slog.d(TAG, "BluetoothServiceConnection: " + className.getClassName());
+        public void onServiceConnected(ComponentName componentName, IBinder service) {
+            String name = componentName.getClassName();
+            if (DBG) Slog.d(TAG, "BluetoothServiceConnection: " + name);
             Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED);
-            // TBD if (className.getClassName().equals(IBluetooth.class.getName())) {
-            if (className.getClassName().equals("com.android.bluetooth.btservice.AdapterService")) {
+            if (name.equals("com.android.bluetooth.btservice.AdapterService")) {
                 msg.arg1 = SERVICE_IBLUETOOTH;
-                // } else if (className.getClassName().equals(IBluetoothGatt.class.getName())) {
-            } else if (className.getClassName().equals("com.android.bluetooth.gatt.GattService")) {
+            } else if (name.equals("com.android.bluetooth.gatt.GattService")) {
                 msg.arg1 = SERVICE_IBLUETOOTHGATT;
             } else {
-                Slog.e(TAG, "Unknown service connected: " + className.getClassName());
+                Slog.e(TAG, "Unknown service connected: " + name);
                 return;
             }
             msg.obj = service;
             mHandler.sendMessage(msg);
         }
 
-        public void onServiceDisconnected(ComponentName className) {
-            // Called if we unexpected disconnected.
-            if (DBG) Slog.d(TAG, "BluetoothServiceConnection, disconnected: " +
-                           className.getClassName());
+        public void onServiceDisconnected(ComponentName componentName) {
+            // Called if we unexpectedly disconnect.
+            String name = componentName.getClassName();
+            if (DBG) Slog.d(TAG, "BluetoothServiceConnection, disconnected: " + name);
             Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED);
-            if (className.getClassName().equals("com.android.bluetooth.btservice.AdapterService")) {
+            if (name.equals("com.android.bluetooth.btservice.AdapterService")) {
                 msg.arg1 = SERVICE_IBLUETOOTH;
-            } else if (className.getClassName().equals("com.android.bluetooth.gatt.GattService")) {
+            } else if (name.equals("com.android.bluetooth.gatt.GattService")) {
                 msg.arg1 = SERVICE_IBLUETOOTHGATT;
             } else {
-                Slog.e(TAG, "Unknown service disconnected: " + className.getClassName());
+                Slog.e(TAG, "Unknown service disconnected: " + name);
                 return;
             }
             mHandler.sendMessage(msg);
@@ -1237,7 +1242,6 @@
 
         @Override
         public void handleMessage(Message msg) {
-            if (DBG) Slog.d (TAG, "Message: " + msg.what);
             switch (msg.what) {
                 case MESSAGE_GET_NAME_AND_ADDRESS:
                     if (DBG) Slog.d(TAG, "MESSAGE_GET_NAME_AND_ADDRESS");
@@ -1275,7 +1279,7 @@
 
                 case MESSAGE_ENABLE:
                     if (DBG) {
-                        Slog.d(TAG, "MESSAGE_ENABLE: mBluetooth = " + mBluetooth);
+                        Slog.d(TAG, "MESSAGE_ENABLE(" + msg.arg1 + "): mBluetooth = " + mBluetooth);
                     }
                     mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
                     mEnable = true;
@@ -1325,6 +1329,7 @@
                     break;
 
                 case MESSAGE_DISABLE:
+                    if (DBG) Slog.d(TAG, "MESSAGE_DISABLE: mBluetooth = " + mBluetooth);
                     mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
                     if (mEnable && mBluetooth != null) {
                         waitForOnOff(true, false);
@@ -1340,31 +1345,25 @@
                 case MESSAGE_REGISTER_ADAPTER:
                 {
                     IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj;
-                    boolean added = mCallbacks.register(callback);
-                    Slog.d(TAG,"Added callback: " +  (callback == null? "null": callback)  +":" +added );
-                }
+                    mCallbacks.register(callback);
                     break;
+                }
                 case MESSAGE_UNREGISTER_ADAPTER:
                 {
                     IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj;
-                    boolean removed = mCallbacks.unregister(callback);
-                    Slog.d(TAG,"Removed callback: " +  (callback == null? "null": callback)  +":" + removed);
+                    mCallbacks.unregister(callback);
                     break;
                 }
                 case MESSAGE_REGISTER_STATE_CHANGE_CALLBACK:
                 {
                     IBluetoothStateChangeCallback callback = (IBluetoothStateChangeCallback) msg.obj;
-                    if (callback != null) {
-                        mStateChangeCallbacks.register(callback);
-                    }
+                    mStateChangeCallbacks.register(callback);
                     break;
                 }
                 case MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK:
                 {
                     IBluetoothStateChangeCallback callback = (IBluetoothStateChangeCallback) msg.obj;
-                    if (callback != null) {
-                        mStateChangeCallbacks.unregister(callback);
-                    }
+                    mStateChangeCallbacks.unregister(callback);
                     break;
                 }
                 case MESSAGE_ADD_PROXY_DELAYED:
@@ -1438,13 +1437,11 @@
                         //Do enable request
                         try {
                             if (mQuietEnable == false) {
-                                if(!mBluetooth.enable()) {
+                                if (!mBluetooth.enable()) {
                                     Slog.e(TAG,"IBluetooth.enable() returned false");
                                 }
-                            }
-                            else
-                            {
-                                if(!mBluetooth.enableNoAutoConnect()) {
+                            } else {
+                                if (!mBluetooth.enableNoAutoConnect()) {
                                     Slog.e(TAG,"IBluetooth.enableNoAutoConnect() returned false");
                                 }
                             }
@@ -1462,19 +1459,14 @@
                     }
                     break;
                 }
-                case MESSAGE_TIMEOUT_BIND: {
-                    Slog.e(TAG, "MESSAGE_TIMEOUT_BIND");
-                    mBluetoothLock.writeLock().lock();
-                    mBinding = false;
-                    mBluetoothLock.writeLock().unlock();
-
-                    break;
-                }
                 case MESSAGE_BLUETOOTH_STATE_CHANGE:
                 {
                     int prevState = msg.arg1;
                     int newState = msg.arg2;
-                    if (DBG) Slog.d(TAG, "MESSAGE_BLUETOOTH_STATE_CHANGE: prevState = " + prevState + ", newState =" + newState);
+                    if (DBG) {
+                      Slog.d(TAG, "MESSAGE_BLUETOOTH_STATE_CHANGE: " + BluetoothAdapter.nameForState(prevState) + " > " +
+                        BluetoothAdapter.nameForState(newState));
+                    }
                     mState = newState;
                     bluetoothStateChangeHandler(prevState, newState);
                     // handle error state transition case from TURNING_ON to OFF
@@ -1514,7 +1506,7 @@
                 }
                 case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED:
                 {
-                    Slog.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: " + msg.arg1);
+                    Slog.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED(" + msg.arg1 + ")");
                     try {
                         mBluetoothLock.writeLock().lock();
                         if (msg.arg1 == SERVICE_IBLUETOOTH) {
@@ -1525,7 +1517,7 @@
                             mBluetoothGatt = null;
                             break;
                         } else {
-                            Slog.e(TAG, "Bad msg.arg1: " + msg.arg1);
+                            Slog.e(TAG, "Unknown argument for service disconnect!");
                             break;
                         }
                     } finally {
@@ -1562,8 +1554,7 @@
                 }
                 case MESSAGE_RESTART_BLUETOOTH_SERVICE:
                 {
-                    Slog.d(TAG, "MESSAGE_RESTART_BLUETOOTH_SERVICE:"
-                        +" Restart IBluetooth service");
+                    Slog.d(TAG, "MESSAGE_RESTART_BLUETOOTH_SERVICE");
                     /* Enable without persisting the setting as
                      it doesnt change when IBluetooth
                      service restarts */
@@ -1571,7 +1562,13 @@
                     handleEnable(mQuietEnable);
                     break;
                 }
-
+                case MESSAGE_TIMEOUT_BIND: {
+                    Slog.e(TAG, "MESSAGE_TIMEOUT_BIND");
+                    mBluetoothLock.writeLock().lock();
+                    mBinding = false;
+                    mBluetoothLock.writeLock().unlock();
+                    break;
+                }
                 case MESSAGE_TIMEOUT_UNBIND:
                 {
                     Slog.e(TAG, "MESSAGE_TIMEOUT_UNBIND");
@@ -1657,11 +1654,10 @@
                     } else if (mBinding || mBluetooth != null) {
                         Message userMsg = mHandler.obtainMessage(MESSAGE_USER_SWITCHED);
                         userMsg.arg2 = 1 + msg.arg2;
-                        // if user is switched when service is being binding
-                        // delay sending MESSAGE_USER_SWITCHED
+                        // if user is switched when service is binding retry after a delay
                         mHandler.sendMessageDelayed(userMsg, USER_SWITCHED_TIME_MS);
                         if (DBG) {
-                            Slog.d(TAG, "delay MESSAGE_USER_SWITCHED " + userMsg.arg2);
+                            Slog.d(TAG, "Retry MESSAGE_USER_SWITCHED " + userMsg.arg2);
                         }
                     }
                     break;
@@ -1762,7 +1758,7 @@
                     parentUser == foregroundUser    ||
                     callingAppId == Process.NFC_UID ||
                     callingAppId == mSystemUiUid;
-            if (DBG) {
+            if (DBG && !valid) {
                 Slog.d(TAG, "checkIfCallerIsForegroundUser: valid=" + valid
                     + " callingUser=" + callingUser
                     + " parentUser=" + parentUser
@@ -1775,7 +1771,8 @@
     }
 
     private void sendBleStateChanged(int prevState, int newState) {
-        if (DBG) Slog.d(TAG,"BLE State Change Intent: " + prevState + " -> " + newState);
+        if (DBG) Slog.d(TAG,"Sending BLE State Change: " + BluetoothAdapter.nameForState(prevState) +
+            " > " + BluetoothAdapter.nameForState(newState));
         // Send broadcast message to everyone else
         Intent intent = new Intent(BluetoothAdapter.ACTION_BLE_STATE_CHANGED);
         intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
@@ -1786,76 +1783,76 @@
 
     private void bluetoothStateChangeHandler(int prevState, int newState) {
         boolean isStandardBroadcast = true;
-        if (DBG) Slog.d(TAG, "bluetoothStateChangeHandler: " + prevState + " ->  " + newState);
-        if (prevState != newState) {
-            //Notify all proxy objects first of adapter state change
-            if (newState == BluetoothAdapter.STATE_BLE_ON ||
-                    newState == BluetoothAdapter.STATE_OFF) {
-                boolean intermediate_off = (prevState == BluetoothAdapter.STATE_TURNING_OFF
-                   && newState == BluetoothAdapter.STATE_BLE_ON);
+        if (prevState == newState) { // No change. Nothing to do.
+            return;
+        }
+        // Notify all proxy objects first of adapter state change
+        if (newState == BluetoothAdapter.STATE_BLE_ON ||
+                newState == BluetoothAdapter.STATE_OFF) {
+            boolean intermediate_off = (prevState == BluetoothAdapter.STATE_TURNING_OFF
+               && newState == BluetoothAdapter.STATE_BLE_ON);
 
-                if (newState == BluetoothAdapter.STATE_OFF) {
-                    // If Bluetooth is off, send service down event to proxy objects, and unbind
-                    if (DBG) Slog.d(TAG, "Bluetooth is complete turn off");
-                    sendBluetoothServiceDownCallback();
-                    unbindAndFinish();
-                    sendBleStateChanged(prevState, newState);
-                    // Don't broadcast as it has already been broadcast before
-                    isStandardBroadcast = false;
-
-                } else if (!intermediate_off) {
-                    // connect to GattService
-                    if (DBG) Slog.d(TAG, "Bluetooth is in LE only mode");
-                    if (mBluetoothGatt != null) {
-                        if (DBG) Slog.d(TAG, "Calling BluetoothGattServiceUp");
-                        onBluetoothGattServiceUp();
-                    } else {
-                        if (DBG) Slog.d(TAG, "Binding Bluetooth GATT service");
-                        if (mContext.getPackageManager().hasSystemFeature(
-                                                        PackageManager.FEATURE_BLUETOOTH_LE)) {
-                            Intent i = new Intent(IBluetoothGatt.class.getName());
-                            doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.CURRENT);
-                        }
-                    }
-                    sendBleStateChanged(prevState, newState);
-                    //Don't broadcase this as std intent
-                    isStandardBroadcast = false;
-
-                } else if (intermediate_off){
-                    if (DBG) Slog.d(TAG, "Intermediate off, back to LE only mode");
-                    // For LE only mode, broadcast as is
-                    sendBleStateChanged(prevState, newState);
-                    sendBluetoothStateCallback(false); // BT is OFF for general users
-                    // Broadcast as STATE_OFF
-                    newState = BluetoothAdapter.STATE_OFF;
-                    sendBrEdrDownCallback();
-                }
-            } else if (newState == BluetoothAdapter.STATE_ON) {
-                boolean isUp = (newState==BluetoothAdapter.STATE_ON);
-                sendBluetoothStateCallback(isUp);
+            if (newState == BluetoothAdapter.STATE_OFF) {
+                // If Bluetooth is off, send service down event to proxy objects, and unbind
+                if (DBG) Slog.d(TAG, "Bluetooth is complete send Service Down");
+                sendBluetoothServiceDownCallback();
+                unbindAndFinish();
                 sendBleStateChanged(prevState, newState);
-
-            } else if (newState == BluetoothAdapter.STATE_BLE_TURNING_ON ||
-                    newState == BluetoothAdapter.STATE_BLE_TURNING_OFF ) {
-                sendBleStateChanged(prevState, newState);
+                // Don't broadcast as it has already been broadcast before
                 isStandardBroadcast = false;
 
-            } else if (newState == BluetoothAdapter.STATE_TURNING_ON ||
-                    newState == BluetoothAdapter.STATE_TURNING_OFF) {
-                sendBleStateChanged(prevState, newState);
-            }
-
-            if (isStandardBroadcast) {
-                if (prevState == BluetoothAdapter.STATE_BLE_ON) {
-                    // Show prevState of BLE_ON as OFF to standard users
-                    prevState = BluetoothAdapter.STATE_OFF;
+            } else if (!intermediate_off) {
+                // connect to GattService
+                if (DBG) Slog.d(TAG, "Bluetooth is in LE only mode");
+                if (mBluetoothGatt != null) {
+                    if (DBG) Slog.d(TAG, "Calling BluetoothGattServiceUp");
+                    onBluetoothGattServiceUp();
+                } else {
+                    if (DBG) Slog.d(TAG, "Binding Bluetooth GATT service");
+                    if (mContext.getPackageManager().hasSystemFeature(
+                                                    PackageManager.FEATURE_BLUETOOTH_LE)) {
+                        Intent i = new Intent(IBluetoothGatt.class.getName());
+                        doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.CURRENT);
+                    }
                 }
-                Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
-                intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
-                intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
-                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-                mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_PERM);
+                sendBleStateChanged(prevState, newState);
+                //Don't broadcase this as std intent
+                isStandardBroadcast = false;
+
+            } else if (intermediate_off) {
+                if (DBG) Slog.d(TAG, "Intermediate off, back to LE only mode");
+                // For LE only mode, broadcast as is
+                sendBleStateChanged(prevState, newState);
+                sendBluetoothStateCallback(false); // BT is OFF for general users
+                // Broadcast as STATE_OFF
+                newState = BluetoothAdapter.STATE_OFF;
+                sendBrEdrDownCallback();
             }
+        } else if (newState == BluetoothAdapter.STATE_ON) {
+            boolean isUp = (newState == BluetoothAdapter.STATE_ON);
+            sendBluetoothStateCallback(isUp);
+            sendBleStateChanged(prevState, newState);
+
+        } else if (newState == BluetoothAdapter.STATE_BLE_TURNING_ON ||
+                newState == BluetoothAdapter.STATE_BLE_TURNING_OFF ) {
+            sendBleStateChanged(prevState, newState);
+            isStandardBroadcast = false;
+
+        } else if (newState == BluetoothAdapter.STATE_TURNING_ON ||
+                newState == BluetoothAdapter.STATE_TURNING_OFF) {
+            sendBleStateChanged(prevState, newState);
+        }
+
+        if (isStandardBroadcast) {
+            if (prevState == BluetoothAdapter.STATE_BLE_ON) {
+                // Show prevState of BLE_ON as OFF to standard users
+                prevState = BluetoothAdapter.STATE_OFF;
+            }
+            Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
+            intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
+            intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
+            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+            mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_PERM);
         }
     }
 
diff --git a/services/core/java/com/android/server/ConsumerIrService.java b/services/core/java/com/android/server/ConsumerIrService.java
index ea6812d..2ed6c77 100644
--- a/services/core/java/com/android/server/ConsumerIrService.java
+++ b/services/core/java/com/android/server/ConsumerIrService.java
@@ -29,13 +29,13 @@
 
     private static final int MAX_XMIT_TIME = 2000000; /* in microseconds */
 
-    private static native long halOpen();
-    private static native int halTransmit(long halObject, int carrierFrequency, int[] pattern);
-    private static native int[] halGetCarrierFrequencies(long halObject);
+    private static native boolean halOpen();
+    private static native int halTransmit(int carrierFrequency, int[] pattern);
+    private static native int[] halGetCarrierFrequencies();
 
     private final Context mContext;
     private final PowerManager.WakeLock mWakeLock;
-    private final long mNativeHal;
+    private final boolean mHasNativeHal;
     private final Object mHalLock = new Object();
 
     ConsumerIrService(Context context) {
@@ -45,23 +45,23 @@
         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
         mWakeLock.setReferenceCounted(true);
 
-        mNativeHal = halOpen();
+        mHasNativeHal = halOpen();
         if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CONSUMER_IR)) {
-            if (mNativeHal == 0) {
+            if (!mHasNativeHal) {
                 throw new RuntimeException("FEATURE_CONSUMER_IR present, but no IR HAL loaded!");
             }
-        } else if (mNativeHal != 0) {
+        } else if (mHasNativeHal) {
             throw new RuntimeException("IR HAL present, but FEATURE_CONSUMER_IR is not set!");
         }
     }
 
     @Override
     public boolean hasIrEmitter() {
-        return mNativeHal != 0;
+        return mHasNativeHal;
     }
 
     private void throwIfNoIrEmitter() {
-        if (mNativeHal == 0) {
+        if (!mHasNativeHal) {
             throw new UnsupportedOperationException("IR emitter not available");
         }
     }
@@ -91,7 +91,7 @@
 
         // Right now there is no mechanism to ensure fair queing of IR requests
         synchronized (mHalLock) {
-            int err = halTransmit(mNativeHal, carrierFrequency, pattern);
+            int err = halTransmit(carrierFrequency, pattern);
 
             if (err < 0) {
                 Slog.e(TAG, "Error transmitting: " + err);
@@ -109,7 +109,7 @@
         throwIfNoIrEmitter();
 
         synchronized(mHalLock) {
-            return halGetCarrierFrequencies(mNativeHal);
+            return halGetCarrierFrequencies();
         }
     }
 }
diff --git a/services/core/java/com/android/server/MasterClearReceiver.java b/services/core/java/com/android/server/MasterClearReceiver.java
index 6f8edec..26e471e 100644
--- a/services/core/java/com/android/server/MasterClearReceiver.java
+++ b/services/core/java/com/android/server/MasterClearReceiver.java
@@ -42,12 +42,21 @@
                 return;
             }
         }
+        if (Intent.ACTION_MASTER_CLEAR.equals(intent.getAction())) {
+            Slog.w(TAG, "The request uses the deprecated Intent#ACTION_MASTER_CLEAR, "
+                    + "Intent#ACTION_FACTORY_RESET should be used instead.");
+        }
+        if (intent.hasExtra(Intent.EXTRA_FORCE_MASTER_CLEAR)) {
+            Slog.w(TAG, "The request uses the deprecated Intent#EXTRA_FORCE_MASTER_CLEAR, "
+                    + "Intent#EXTRA_FORCE_FACTORY_RESET should be used instead.");
+        }
 
         final boolean shutdown = intent.getBooleanExtra("shutdown", false);
         final String reason = intent.getStringExtra(Intent.EXTRA_REASON);
         final boolean wipeExternalStorage = intent.getBooleanExtra(
                 Intent.EXTRA_WIPE_EXTERNAL_STORAGE, false);
-        final boolean forceWipe = intent.getBooleanExtra(Intent.EXTRA_FORCE_MASTER_CLEAR, false);
+        final boolean forceWipe = intent.getBooleanExtra(Intent.EXTRA_FORCE_MASTER_CLEAR, false)
+                || intent.getBooleanExtra(Intent.EXTRA_FORCE_FACTORY_RESET, false);
 
         Slog.w(TAG, "!!! FACTORY RESET !!!");
         // The reboot call is blocking, so we need to do it on another thread.
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6e69e34..9b67bd2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -17,9 +17,11 @@
 package com.android.server.am;
 
 import android.annotation.Nullable;
+import android.app.ActivityManagerInternal.PictureInPictureArguments;
 import android.app.ApplicationThreadConstants;
 import android.app.ContentProviderHolder;
 import android.app.IActivityManager;
+import android.app.RemoteAction;
 import android.app.WaitResult;
 import android.os.IDeviceIdentifiersPolicyService;
 
@@ -1378,6 +1380,7 @@
     boolean mAlwaysFinishActivities = false;
     boolean mForceResizableActivities;
     boolean mSupportsMultiWindow;
+    boolean mSupportsSplitScreenMultiWindow;
     boolean mSupportsFreeformWindowManagement;
     boolean mSupportsPictureInPicture;
     boolean mSupportsLeanbackOnly;
@@ -1563,6 +1566,7 @@
     static final int SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG_MSG = 62;
     static final int HANDLE_TRUST_STORAGE_UPDATE_MSG = 63;
     static final int REPORT_LOCKED_BOOT_COMPLETE_MSG = 64;
+    static final int NOTIFY_VR_SLEEPING_MSG = 65;
     static final int START_USER_SWITCH_FG_MSG = 712;
 
     static final int FIRST_ACTIVITY_STACK_MSG = 100;
@@ -2297,6 +2301,8 @@
                     }
                 }
                 vrService.setVrMode(vrMode, requestedPackage, userId, callingPackage);
+            } case NOTIFY_VR_SLEEPING_MSG: {
+                notifyVrManagerOfSleepState(msg.arg1 != 0);
             } break;
             case HANDLE_TRUST_STORAGE_UPDATE_MSG: {
                 synchronized (ActivityManagerService.this) {
@@ -3070,6 +3076,11 @@
                 mHandler.obtainMessage(VR_MODE_CHANGE_MSG, 0, 0, r));
     }
 
+    private void sendNotifyVrManagerOfSleepState(boolean isSleeping) {
+        mHandler.sendMessage(
+                mHandler.obtainMessage(NOTIFY_VR_SLEEPING_MSG, isSleeping ? 1 : 0, 0));
+    }
+
     private void notifyVrManagerOfSleepState(boolean isSleeping) {
         final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
         if (vrService == null) {
@@ -3673,6 +3684,15 @@
                 mNativeDebuggingApp = null;
             }
 
+            String invokeWith = null;
+            if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
+                // Debuggable apps may include a wrapper script with their library directory.
+                String wrapperFileName = app.info.nativeLibraryDir + "/wrap.sh";
+                if (new File(wrapperFileName).exists()) {
+                    invokeWith = "/system/bin/logwrapper " + wrapperFileName;
+                }
+            }
+
             String requiredAbi = (abiOverride != null) ? abiOverride : app.info.primaryCpuAbi;
             if (requiredAbi == null) {
                 requiredAbi = Build.SUPPORTED_ABIS[0];
@@ -3699,12 +3719,12 @@
                 startResult = Process.startWebView(entryPoint,
                         app.processName, uid, uid, gids, debugFlags, mountExternal,
                         app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
-                        app.info.dataDir, entryPointArgs);
+                        app.info.dataDir, null, entryPointArgs);
             } else {
                 startResult = Process.start(entryPoint,
                         app.processName, uid, uid, gids, debugFlags, mountExternal,
                         app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
-                        app.info.dataDir, entryPointArgs);
+                        app.info.dataDir, invokeWith, entryPointArgs);
             }
             checkTime(startTime, "startProcess: returned from zygote!");
             Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
@@ -7513,6 +7533,23 @@
         enterPictureInPictureMode(token, DEFAULT_DISPLAY, aspectRatio, true /* checkAspectRatio */);
     }
 
+    @Override
+    public void enterPictureInPictureModeOnMoveToBackground(IBinder token,
+            boolean enterPictureInPictureOnMoveToBg) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized(this) {
+                final ActivityRecord r = ensureValidPictureInPictureActivityLocked(
+                        "enterPictureInPictureModeOnMoveToBackground", token, -1f /* aspectRatio */,
+                        false /* checkAspectRatio */, false /* checkActivityVisibility */);
+
+                r.supportsPipOnMoveToBackground = enterPictureInPictureOnMoveToBg;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
     private void enterPictureInPictureMode(IBinder token, int displayId, float aspectRatio,
             boolean checkAspectRatio) {
         final long origId = Binder.clearCallingIdentity();
@@ -7522,7 +7559,8 @@
                         "enterPictureInPictureMode", token, aspectRatio, checkAspectRatio,
                         true /* checkActivityVisibility */);
 
-                enterPictureInPictureModeLocked(r, displayId, aspectRatio,
+                r.pictureInPictureArgs.aspectRatio = aspectRatio;
+                enterPictureInPictureModeLocked(r, displayId, r.pictureInPictureArgs,
                         true /* moveHomeStackToFront */, "enterPictureInPictureMode");
             }
         } finally {
@@ -7530,29 +7568,13 @@
         }
     }
 
-    void enterPictureInPictureModeLocked(ActivityRecord r, int displayId, float aspectRatio,
-            boolean moveHomeStackToFront, String reason) {
-        final Rect bounds = isValidPictureInPictureAspectRatio(aspectRatio)
-                ? mWindowManager.getPictureInPictureBounds(displayId, aspectRatio)
+    void enterPictureInPictureModeLocked(ActivityRecord r, int displayId,
+            PictureInPictureArguments pipArgs, boolean moveHomeStackToFront, String reason) {
+        final Rect bounds = isValidPictureInPictureAspectRatio(pipArgs.aspectRatio)
+                ? mWindowManager.getPictureInPictureBounds(displayId, pipArgs.aspectRatio)
                 : mWindowManager.getPictureInPictureDefaultBounds(displayId);
         mStackSupervisor.moveActivityToPinnedStackLocked(r, reason, bounds, moveHomeStackToFront);
-    }
-
-    @Override
-    public void enterPictureInPictureModeOnMoveToBackground(IBinder token,
-            boolean enterPictureInPictureOnMoveToBg) {
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized(this) {
-                final ActivityRecord r = ensureValidPictureInPictureActivityLocked(
-                        "requestAutoEnterPictureInPicture", token, -1f /* aspectRatio */,
-                        false /* checkAspectRatio */, false /* checkActivityVisibility */);
-
-                r.supportsPipOnMoveToBackground = enterPictureInPictureOnMoveToBg;
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
+        mWindowManager.setPictureInPictureActions(pipArgs.userActions);
     }
 
     @Override
@@ -7564,11 +7586,39 @@
                         "setPictureInPictureAspectRatio", token, aspectRatio,
                         true /* checkAspectRatio */, false /* checkActivityVisibility */);
 
+                r.pictureInPictureArgs.aspectRatio = aspectRatio;
                 if (r.getStack().getStackId() == PINNED_STACK_ID) {
                     // If the activity is already in picture-in-picture, update the pinned stack now
                     mWindowManager.setPictureInPictureAspectRatio(aspectRatio);
                 }
-                r.pictureInPictureAspectRatio = aspectRatio;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public void setPictureInPictureActions(IBinder token, ParceledListSlice actionsList) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized(this) {
+                final ActivityRecord r = ensureValidPictureInPictureActivityLocked(
+                        "setPictureInPictureActions", token, -1 /* aspectRatio */,
+                        false /* checkAspectRatio */, false /* checkActivityVisibility */);
+
+                final List<RemoteAction> actions = actionsList.getList();
+                if (actions.size() > ActivityManager.getMaxNumPictureInPictureActions()) {
+                    throw new IllegalArgumentException("setPictureInPictureActions: Invalid number"
+                            + " of picture-in-picture actions.  Only a maximum of "
+                            + ActivityManager.getMaxNumPictureInPictureActions()
+                            + " actions allowed");
+                }
+
+                r.pictureInPictureArgs.userActions = actions;
+                if (r.getStack().getStackId() == PINNED_STACK_ID) {
+                    // If the activity is already in picture-in-picture, update the pinned stack now
+                    mWindowManager.setPictureInPictureActions(actions);
+                }
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -7604,7 +7654,7 @@
 
         if (!r.canEnterPictureInPicture(checkActivityVisibility)) {
             throw new IllegalArgumentException(caller
-                    + "Current activity does not support picture-in-picture or is not "
+                    + ": Current activity does not support picture-in-picture or is not "
                     + "visible r=" + r);
         }
 
@@ -11684,7 +11734,7 @@
             startTimeTrackingFocusedActivityLocked();
             mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
             mStackSupervisor.comeOutOfSleepIfNeededLocked();
-            notifyVrManagerOfSleepState(false);
+            sendNotifyVrManagerOfSleepState(false);
             updateOomAdjLocked();
         } else if (!mSleeping && shouldSleepLocked()) {
             mSleeping = true;
@@ -11693,7 +11743,7 @@
             }
             mTopProcessState = ActivityManager.PROCESS_STATE_TOP_SLEEPING;
             mStackSupervisor.goingToSleepLocked();
-            notifyVrManagerOfSleepState(true);
+            sendNotifyVrManagerOfSleepState(true);
             updateOomAdjLocked();
 
             // Initialize the wake times of all processes.
@@ -13141,6 +13191,8 @@
                 mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
 
         final boolean supportsMultiWindow = ActivityManager.supportsMultiWindow();
+        final boolean supportsSplitScreenMultiWindow =
+                ActivityManager.supportsSplitScreenMultiWindow();
         final String debugApp = Settings.Global.getString(resolver, DEBUG_APP);
         final boolean waitForDebugger = Settings.Global.getInt(resolver, WAIT_FOR_DEBUGGER, 0) != 0;
         final boolean alwaysFinishActivities =
@@ -13176,6 +13228,7 @@
                 mSupportsFreeformWindowManagement = false;
                 mSupportsPictureInPicture = false;
             }
+            mSupportsSplitScreenMultiWindow = supportsSplitScreenMultiWindow;
             mWindowManager.setForceResizableTasks(mForceResizableActivities);
             mWindowManager.setSupportsPictureInPicture(mSupportsPictureInPicture);
             // This happens before any activities are started, so we can change global configuration
@@ -17865,6 +17918,7 @@
                 || Intent.ACTION_MEDIA_SCANNER_SCAN_FILE.equals(action)
                 || Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS.equals(action)
                 || Intent.ACTION_MASTER_CLEAR.equals(action)
+                || Intent.ACTION_FACTORY_RESET.equals(action)
                 || AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
                 || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)
                 || LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action)
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 14b843a..814b05a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -233,6 +233,8 @@
                     return runAttachAgent(pw);
                 case "supports-multiwindow":
                     return runSupportsMultiwindow(pw);
+                case "supports-split-screen-multi-window":
+                    return runSupportsSplitScreenMultiwindow(pw);
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -2300,20 +2302,36 @@
     }
 
     int runSupportsMultiwindow(PrintWriter pw) throws RemoteException {
+        final Resources res = getResources(pw);
+        if (res == null) {
+            return -1;
+        }
+        pw.println(res.getBoolean(com.android.internal.R.bool.config_supportsMultiWindow));
+        return 0;
+    }
+
+    int runSupportsSplitScreenMultiwindow(PrintWriter pw) throws RemoteException {
+        final Resources res = getResources(pw);
+        if (res == null) {
+            return -1;
+        }
+        pw.println(
+                res.getBoolean(com.android.internal.R.bool.config_supportsSplitScreenMultiWindow));
+        return 0;
+    }
+
+    private Resources getResources(PrintWriter pw) throws RemoteException {
         // system resources does not contain all the device configuration, construct it manually.
         Configuration config = mInterface.getConfiguration();
         if (config == null) {
             pw.println("Error: Activity manager has no configuration");
-            return -1;
+            return null;
         }
 
         final DisplayMetrics metrics = new DisplayMetrics();
         metrics.setToDefaults();
 
-        Resources res = new Resources(AssetManager.getSystem(), metrics, config);
-
-        pw.println(res.getBoolean(com.android.internal.R.bool.config_supportsMultiWindow));
-        return 0;
+        return new Resources(AssetManager.getSystem(), metrics, config);
     }
 
     @Override
@@ -2495,6 +2513,8 @@
             pw.println("      Rtrieve the configuration and any recent configurations of the device.");
             pw.println("  supports-multiwindow");
             pw.println("      Returns true if the device supports multiwindow.");
+            pw.println("  supports-split-screen-multi-window");
+            pw.println("      Returns true if the device supports split screen multiwindow.");
             pw.println("  suppress-resize-config-changes <true|false>");
             pw.println("      Suppresses configuration changes due to user resizing an activity/task.");
             pw.println("  set-inactive [--user <USER_ID>] <PACKAGE> true|false");
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 7dd2e84..ef19700 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -54,6 +54,7 @@
 
 import android.annotation.NonNull;
 import android.app.ActivityManager.TaskDescription;
+import android.app.ActivityManagerInternal.PictureInPictureArguments;
 import android.app.ActivityOptions;
 import android.app.PendingIntent;
 import android.app.ResultInfo;
@@ -217,13 +218,13 @@
     boolean immersive;      // immersive mode (don't interrupt if possible)
     boolean forceNewConfig; // force re-create with new config next time
     boolean supportsPipOnMoveToBackground;   // Supports automatically entering picture-in-picture
-            // when this activity is hidden. This flag is requested by the activity.
+        // when this activity is hidden. This flag is requested by the activity.
     private boolean enterPipOnMoveToBackground; // Flag to enter picture in picture when this
-            // activity is made invisible. This flag is set specifically when another task is being
-            // launched or moved to the front which may cause this activity to try and enter PiP
-            // when it is next made invisible.
-    float pictureInPictureAspectRatio; // The aspect ratio to use when auto-entering
-            // picture-in-picture
+        // activity is made invisible. This flag is set specifically when another task is being
+        // launched or moved to the front which may cause this activity to try and enter PiP
+        // when it is next made invisible.
+    PictureInPictureArguments pictureInPictureArgs = new PictureInPictureArguments();  // The PiP
+        // arguments used when deferring the entering of picture-in-picture.
     int launchCount;        // count of launches since last state
     long lastLaunchTime;    // time of last launch of this activity
     ComponentName requestedVrComponent; // the requested component for handling VR mode.
@@ -441,9 +442,10 @@
             pw.println(prefix + "resizeMode=" + ActivityInfo.resizeModeToString(info.resizeMode));
         }
         if (supportsPipOnMoveToBackground) {
-            pw.println(prefix + "supportsPipOnMoveToBackground=1 "
-                    + "enterPipOnMoveToBackground="
-                            + (enterPipOnMoveToBackground ? 1 : 0));
+            pw.println(prefix + "supportsPipOnMoveToBackground=1");
+            pw.println(prefix + "enterPipOnMoveToBackground=" +
+                    (enterPipOnMoveToBackground ? 1 : 0));
+            pictureInPictureArgs.dump(pw, prefix);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 671c84e..5bdae57 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1895,7 +1895,7 @@
             // since it will affect the focused stack's visibility and occlude
             // starting activities
             mService.enterPictureInPictureModeLocked(r, r.getDisplayId(),
-                    r.pictureInPictureAspectRatio, false /* moveHomeStackToFront */,
+                    r.pictureInPictureArgs, false /* moveHomeStackToFront */,
                     "ensureActivitiesVisibleLocked");
             return true;
         }
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index a6444bc..9e28068 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -1104,7 +1104,7 @@
     }
 
     boolean canGoInDockedStack() {
-        return isResizeable() &&
+        return isResizeable() && mService.mSupportsSplitScreenMultiWindow &&
                 !ActivityInfo.isPreserveOrientationMode(mResizeMode);
     }
 
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
index 372b2d8..d5fa26c 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
@@ -43,10 +43,10 @@
     private IpConnectivityEventBuilder() {
     }
 
-    public static byte[] serialize(int dropped, List<ConnectivityMetricsEvent> events)
+    public static byte[] serialize(int dropped, List<IpConnectivityEvent> events)
             throws IOException {
         final IpConnectivityLog log = new IpConnectivityLog();
-        log.events = toProto(events);
+        log.events = events.toArray(new IpConnectivityEvent[events.size()]);
         log.droppedEvents = dropped;
         if ((log.events.length > 0) || (dropped > 0)) {
             // Only write version number if log has some information at all.
@@ -55,7 +55,7 @@
         return IpConnectivityLog.toByteArray(log);
     }
 
-    public static IpConnectivityEvent[] toProto(List<ConnectivityMetricsEvent> eventsIn) {
+    public static List<IpConnectivityEvent> toProto(List<ConnectivityMetricsEvent> eventsIn) {
         final ArrayList<IpConnectivityEvent> eventsOut = new ArrayList<>(eventsIn.size());
         for (ConnectivityMetricsEvent in : eventsIn) {
             final IpConnectivityEvent out = toProto(in);
@@ -64,7 +64,7 @@
             }
             eventsOut.add(out);
         }
-        return eventsOut.toArray(new IpConnectivityEvent[eventsOut.size()]);
+        return eventsOut;
     }
 
     public static IpConnectivityEvent toProto(ConnectivityMetricsEvent ev) {
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
index 42f439c..445f606 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
@@ -36,14 +36,14 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.TokenBucket;
 import com.android.server.SystemService;
+import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.function.ToIntFunction;
 
-import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
-
 /** {@hide} */
 final public class IpConnectivityMetrics extends SystemService {
     private static final String TAG = IpConnectivityMetrics.class.getSimpleName();
@@ -63,6 +63,8 @@
     // Maximum size of the event buffer.
     private static final int MAXIMUM_BUFFER_SIZE = DEFAULT_BUFFER_SIZE * 10;
 
+    private static final int MAXIMUM_CONNECT_LATENCY_RECORDS = 20000;
+
     private static final int ERROR_RATE_LIMITED = -1;
 
     // Lock ensuring that concurrent manipulations of the event buffer are correct.
@@ -160,9 +162,15 @@
             initBuffer();
         }
 
+        final List<IpConnectivityEvent> protoEvents = IpConnectivityEventBuilder.toProto(events);
+
+        if (mNetdListener != null) {
+            mNetdListener.flushStatistics(protoEvents);
+        }
+
         final byte[] data;
         try {
-            data = IpConnectivityEventBuilder.serialize(dropped, events);
+            data = IpConnectivityEventBuilder.serialize(dropped, protoEvents);
         } catch (IOException e) {
             Log.e(TAG, "could not serialize events", e);
             return "";
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index 74a1f4f..f555f08 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -19,25 +19,28 @@
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.NetworkCallback;
-import android.net.Network;
 import android.net.INetdEventCallback;
+import android.net.Network;
 import android.net.NetworkRequest;
+import android.net.metrics.ConnectStats;
 import android.net.metrics.DnsEvent;
-import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.INetdEventListener;
+import android.net.metrics.IpConnectivityLog;
 import android.os.RemoteException;
+import android.text.format.DateUtils;
 import android.util.Log;
-
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
-
+import com.android.internal.util.TokenBucket;
+import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.ConnectStatistics;
+import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
 import java.io.PrintWriter;
 import java.util.Arrays;
+import java.util.List;
 import java.util.SortedMap;
 import java.util.TreeMap;
 
-
 /**
  * Implementation of the INetdEventListener interface.
  */
@@ -52,6 +55,12 @@
     // TODO: read this constant from system property
     private static final int MAX_LOOKUPS_PER_DNS_EVENT = 100;
 
+    // Rate limit connect latency logging to 1 measurement per 15 seconds (5760 / day) with maximum
+    // bursts of 5000 measurements.
+    private static final int CONNECT_LATENCY_BURST_LIMIT  = 5000;
+    private static final int CONNECT_LATENCY_FILL_RATE    = 15 * (int) DateUtils.SECOND_IN_MILLIS;
+    private static final int CONNECT_LATENCY_MAXIMUM_RECORDS = 20000;
+
     // Stores the results of a number of consecutive DNS lookups on the same network.
     // This class is not thread-safe and it is the responsibility of the service to call its methods
     // on one thread at a time.
@@ -121,6 +130,12 @@
         }
     };
 
+    @GuardedBy("this")
+    private final TokenBucket mConnectTb =
+            new TokenBucket(CONNECT_LATENCY_FILL_RATE, CONNECT_LATENCY_BURST_LIMIT);
+    @GuardedBy("this")
+    private ConnectStats mConnectStats = makeConnectStats();
+
     // Callback should only be registered/unregistered when logging is being enabled/disabled in DPM
     // by the device owner. It's DevicePolicyManager's responsibility to ensure that.
     @GuardedBy("this")
@@ -173,15 +188,30 @@
     @Override
     // Called concurrently by multiple binder threads.
     // This method must not block or perform long-running operations.
-    public synchronized void onConnectEvent(int netId, int error, int latencyMs, String ipAddr, int port,
-            int uid) throws RemoteException {
+    public synchronized void onConnectEvent(int netId, int error, int latencyMs, String ipAddr,
+            int port, int uid) throws RemoteException {
         maybeVerboseLog("onConnectEvent(%d, %d, %dms)", netId, error, latencyMs);
 
+        mConnectStats.addEvent(error, latencyMs, ipAddr);
+
         if (mNetdEventCallback != null) {
             mNetdEventCallback.onConnectEvent(ipAddr, port, System.currentTimeMillis(), uid);
         }
     }
 
+    public synchronized void flushStatistics(List<IpConnectivityEvent> events) {
+        events.add(flushConnectStats());
+        // TODO: migrate DnsEventBatch to IpConnectivityLogClass.DNSLatencies
+    }
+
+    private IpConnectivityEvent flushConnectStats() {
+        IpConnectivityEvent ev = new IpConnectivityEvent();
+        ev.setConnectStatistics(mConnectStats.toProto());
+        // TODO: add transport information
+        mConnectStats = makeConnectStats();
+        return ev;
+    }
+
     public synchronized void dump(PrintWriter writer) {
         IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
         pw.println(TAG + ":");
@@ -189,9 +219,14 @@
         for (DnsEventBatch batch : mEventBatches.values()) {
             pw.println(batch.toString());
         }
+        // TODO: also dump ConnectStats
         pw.decreaseIndent();
     }
 
+    private ConnectStats makeConnectStats() {
+        return new ConnectStats(mConnectTb, CONNECT_LATENCY_MAXIMUM_RECORDS);
+    }
+
     private static void maybeLog(String s, Object... args) {
         if (DBG) Log.d(TAG, String.format(s, args));
     }
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index 9ffa40b..63b5250 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -19,7 +19,6 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
-import android.widget.Toast;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
@@ -27,18 +26,40 @@
 import android.os.UserHandle;
 import android.telephony.TelephonyManager;
 import android.util.Slog;
-import com.android.internal.annotations.VisibleForTesting;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.widget.Toast;
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 
-import static android.net.NetworkCapabilities.*;
-
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 
 public class NetworkNotificationManager {
 
-    public static enum NotificationType { SIGN_IN, NO_INTERNET, LOST_INTERNET, NETWORK_SWITCH };
+    public static enum NotificationType {
+        LOST_INTERNET(MetricsEvent.NOTIFICATION_NETWORK_LOST_INTERNET),
+        NETWORK_SWITCH(MetricsEvent.NOTIFICATION_NETWORK_SWITCH),
+        NO_INTERNET(MetricsEvent.NOTIFICATION_NETWORK_NO_INTERNET),
+        SIGN_IN(MetricsEvent.NOTIFICATION_NETWORK_SIGN_IN);
 
-    @VisibleForTesting
-    static final String NOTIFICATION_ID = "Connectivity.Notification";
+        public final int eventId;
+
+        NotificationType(int eventId) {
+            this.eventId = eventId;
+            Holder.sIdToTypeMap.put(eventId, this);
+        }
+
+        private static class Holder {
+            private static SparseArray<NotificationType> sIdToTypeMap = new SparseArray<>();
+        }
+
+        public static NotificationType getFromId(int id) {
+            return Holder.sIdToTypeMap.get(id);
+        }
+    };
 
     private static final String TAG = NetworkNotificationManager.class.getSimpleName();
     private static final boolean DBG = true;
@@ -47,11 +68,14 @@
     private final Context mContext;
     private final TelephonyManager mTelephonyManager;
     private final NotificationManager mNotificationManager;
+    // Tracks the types of notifications managed by this instance, from creation to cancellation.
+    private final SparseIntArray mNotificationTypeMap;
 
     public NetworkNotificationManager(Context c, TelephonyManager t, NotificationManager n) {
         mContext = c;
         mTelephonyManager = t;
         mNotificationManager = n;
+        mNotificationTypeMap = new SparseIntArray();
     }
 
     // TODO: deal more gracefully with multi-transport networks.
@@ -101,8 +125,10 @@
      */
     public void showNotification(int id, NotificationType notifyType, NetworkAgentInfo nai,
             NetworkAgentInfo switchToNai, PendingIntent intent, boolean highPriority) {
-        int transportType;
-        String extraInfo;
+        final String tag = tagFor(id);
+        final int eventId = notifyType.eventId;
+        final int transportType;
+        final String extraInfo;
         if (nai != null) {
             transportType = getFirstTransportType(nai);
             extraInfo = nai.networkInfo.getExtraInfo();
@@ -115,9 +141,9 @@
         }
 
         if (DBG) {
-            Slog.d(TAG, "showNotification id=" + id + " " + notifyType
-                    + " transportType=" + getTransportName(transportType)
-                    + " extraInfo=" + extraInfo + " highPriority=" + highPriority);
+            Slog.d(TAG, String.format(
+                    "showNotification tag=%s event=%s transport=%s extraInfo=%d highPrioriy=%s",
+                    tag, nameOf(eventId), getTransportName(transportType), extraInfo, highPriority));
         }
 
         Resources r = Resources.getSystem();
@@ -185,22 +211,31 @@
 
         Notification notification = builder.build();
 
+        mNotificationTypeMap.put(id, eventId);
         try {
-            mNotificationManager.notifyAsUser(NOTIFICATION_ID, id, notification, UserHandle.ALL);
+            mNotificationManager.notifyAsUser(tag, eventId, notification, UserHandle.ALL);
         } catch (NullPointerException npe) {
             Slog.d(TAG, "setNotificationVisible: visible notificationManager error", npe);
         }
     }
 
     public void clearNotification(int id) {
+        final String tag = tagFor(id);
+        if (mNotificationTypeMap.indexOfKey(id) < 0) {
+            Slog.e(TAG, "cannot clear unknown notification with tag=" + tag);
+            return;
+        }
+        final int eventId = mNotificationTypeMap.get(id);
         if (DBG) {
-            Slog.d(TAG, "clearNotification id=" + id);
+            Slog.d(TAG, String.format("clearing notification tag=%s event=", tag, nameOf(eventId)));
         }
         try {
-            mNotificationManager.cancelAsUser(NOTIFICATION_ID, id, UserHandle.ALL);
+            mNotificationManager.cancelAsUser(tag, eventId, UserHandle.ALL);
         } catch (NullPointerException npe) {
-            Slog.d(TAG, "setNotificationVisible: cancel notificationManager error", npe);
+            Slog.d(TAG, String.format(
+                    "failed to clear notification tag=%s event=", tag, nameOf(eventId)), npe);
         }
+        mNotificationTypeMap.delete(id);
     }
 
     /**
@@ -223,4 +258,15 @@
                 R.string.network_switch_metered_toast, fromTransport, toTransport);
         Toast.makeText(mContext, text, Toast.LENGTH_LONG).show();
     }
+
+    @VisibleForTesting
+    static String tagFor(int id) {
+        return String.format("ConnectivityNotification:%d", id);
+    }
+
+    @VisibleForTesting
+    static String nameOf(int eventId) {
+        NotificationType t = NotificationType.getFromId(eventId);
+        return (t != null) ? t.name() : "UNKNOWN";
+    }
 }
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 132967c..3f0ebf2 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -46,7 +46,11 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.service.fingerprint.FingerprintActionStatsProto;
+import android.service.fingerprint.FingerprintServiceDumpProto;
+import android.service.fingerprint.FingerprintUserStatsProto;
 import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
@@ -968,7 +972,11 @@
 
             final long ident = Binder.clearCallingIdentity();
             try {
-                dumpInternal(pw);
+                if (args.length > 0 && "--proto".equals(args[0])) {
+                    dumpProto(fd);
+                } else {
+                    dumpInternal(pw);
+                }
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -1027,6 +1035,45 @@
         pw.println(dump);
     }
 
+    private void dumpProto(FileDescriptor fd) {
+        final ProtoOutputStream proto = new ProtoOutputStream(fd);
+        for (UserInfo user : UserManager.get(getContext()).getUsers()) {
+            final int userId = user.getUserHandle().getIdentifier();
+
+            final long userToken = proto.start(FingerprintServiceDumpProto.USERS);
+
+            proto.write(FingerprintUserStatsProto.USER_ID, userId);
+            proto.write(FingerprintUserStatsProto.NUM_FINGERPRINTS,
+                    mFingerprintUtils.getFingerprintsForUser(mContext, userId).size());
+
+            // Normal fingerprint authentications (e.g. lockscreen)
+            final PerformanceStats normal = mPerformanceMap.get(userId);
+            if (normal != null) {
+                final long countsToken = proto.start(FingerprintUserStatsProto.NORMAL);
+                proto.write(FingerprintActionStatsProto.ACCEPT, normal.accept);
+                proto.write(FingerprintActionStatsProto.REJECT, normal.reject);
+                proto.write(FingerprintActionStatsProto.ACQUIRE, normal.acquire);
+                proto.write(FingerprintActionStatsProto.LOCKOUT, normal.lockout);
+                proto.end(countsToken);
+            }
+
+            // Statistics about secure fingerprint transactions (e.g. to unlock password
+            // storage, make secure purchases, etc.)
+            final PerformanceStats crypto = mPerformanceMap.get(userId);
+            if (crypto != null) {
+                final long countsToken = proto.start(FingerprintUserStatsProto.CRYPTO);
+                proto.write(FingerprintActionStatsProto.ACCEPT, crypto.accept);
+                proto.write(FingerprintActionStatsProto.REJECT, crypto.reject);
+                proto.write(FingerprintActionStatsProto.ACQUIRE, crypto.acquire);
+                proto.write(FingerprintActionStatsProto.LOCKOUT, crypto.lockout);
+                proto.end(countsToken);
+            }
+
+            proto.end(userToken);
+        }
+        proto.flush();
+    }
+
     @Override
     public void onStart() {
         publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 56ba5bb..fb2b961 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -457,6 +457,7 @@
             if (mDownloadXtraDataPending == STATE_PENDING_NETWORK) {
                 xtraDownloadRequest();
             }
+            // Always on, notify HAL so it can get data it needs
             sendMessage(UPDATE_NETWORK_STATE, 0 /*arg*/, network);
         }
     };
@@ -469,6 +470,12 @@
     private final ConnectivityManager.NetworkCallback mSuplConnectivityCallback =
             new ConnectivityManager.NetworkCallback() {
         @Override
+        public void onAvailable(Network network) {
+            // Specific to a change to a SUPL enabled network becoming ready
+            sendMessage(UPDATE_NETWORK_STATE, 0 /*arg*/, network);
+        }
+
+        @Override
         public void onLost(Network network) {
             releaseSuplConnection(GPS_RELEASE_AGPS_DATA_CONN);
         }
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 8ca6086..386e78b 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -553,15 +553,21 @@
             @Override
             public NetworkStats getSummaryForAllUid(
                     NetworkTemplate template, long start, long end, boolean includeTags) {
-                @NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage);
-                final NetworkStats stats =
-                        getUidComplete().getSummary(template, start, end, accessLevel);
-                if (includeTags) {
-                    final NetworkStats tagStats = getUidTagComplete()
-                            .getSummary(template, start, end, accessLevel);
-                    stats.combineAllValues(tagStats);
+                try {
+                    @NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage);
+                    final NetworkStats stats =
+                            getUidComplete().getSummary(template, start, end, accessLevel);
+                    if (includeTags) {
+                        final NetworkStats tagStats = getUidTagComplete()
+                                .getSummary(template, start, end, accessLevel);
+                        stats.combineAllValues(tagStats);
+                    }
+                    return stats;
+                } catch (NullPointerException e) {
+                    // TODO: Track down and fix the cause of this crash and remove this catch block.
+                    Slog.wtf(TAG, "NullPointerException in getSummaryForAllUid", e);
+                    throw e;
                 }
-                return stats;
             }
 
             @Override
diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
index 7dff2c1..1e0035d 100644
--- a/services/core/java/com/android/server/notification/NotificationComparator.java
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -15,7 +15,25 @@
  */
 package com.android.server.notification;
 
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.telecom.TelecomManager;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Slog;
+
 import java.util.Comparator;
+import java.util.Objects;
 
 /**
  * Sorts notifications individually into attention-relevant order.
@@ -23,8 +41,45 @@
 public class NotificationComparator
         implements Comparator<NotificationRecord> {
 
+    private final String DEFAULT_SMS_APP_SETTING = Settings.Secure.SMS_DEFAULT_APPLICATION;
+
+    private final Context mContext;
+    private String mDefaultPhoneApp;
+    private ArrayMap<Integer, String> mDefaultSmsApp = new ArrayMap<>();
+
+    public NotificationComparator(Context context) {
+        mContext = context;
+        mContext.registerReceiver(mPhoneAppBroadcastReceiver,
+                new IntentFilter(TelecomManager.ACTION_DEFAULT_DIALER_CHANGED));
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Secure.getUriFor(DEFAULT_SMS_APP_SETTING), false, mSmsContentObserver);
+    }
+
     @Override
     public int compare(NotificationRecord left, NotificationRecord right) {
+        // First up: sufficiently important ongoing notifications of certain categories
+        boolean leftImportantOngoing = isImportantOngoing(left);
+        boolean rightImportantOngoing = isImportantOngoing(right);
+
+        if (leftImportantOngoing != rightImportantOngoing) {
+            // by ongoing, ongoing higher than non-ongoing
+            return -1 * Boolean.compare(leftImportantOngoing, rightImportantOngoing);
+        }
+
+        // Next: sufficiently import person to person communication
+        boolean leftPeople = isImportantMessaging(left);
+        boolean rightPeople = isImportantMessaging(right);
+
+        if (leftPeople && rightPeople){
+            // by contact proximity, close to far. if same proximity, check further fields.
+            if (Float.compare(left.getContactAffinity(), right.getContactAffinity()) != 0) {
+                return -1 * Float.compare(left.getContactAffinity(), right.getContactAffinity());
+            }
+        } else if (leftPeople != rightPeople) {
+            // People, messaging higher than non-messaging
+            return -1 * Boolean.compare(leftPeople, rightPeople);
+        }
+
         final int leftImportance = left.getImportance();
         final int rightImportance = right.getImportance();
         if (leftImportance != rightImportance) {
@@ -47,14 +102,112 @@
             return -1 * Integer.compare(leftPriority, rightPriority);
         }
 
-        final float leftPeople = left.getContactAffinity();
-        final float rightPeople = right.getContactAffinity();
-        if (leftPeople != rightPeople) {
-            // by contact proximity, close to far
-            return -1 * Float.compare(leftPeople, rightPeople);
-        }
-
         // then break ties by time, most recent first
         return -1 * Long.compare(left.getRankingTimeMs(), right.getRankingTimeMs());
     }
+
+    private boolean isImportantOngoing(NotificationRecord record) {
+        if (!isOngoing(record)) {
+            return false;
+        }
+
+        if (record.getImportance() < NotificationManager.IMPORTANCE_LOW) {
+            return false;
+        }
+
+        // TODO: add whitelist
+
+        return isCall(record) || isMediaNotification(record);
+    }
+
+    protected boolean isImportantMessaging(NotificationRecord record) {
+        if (record.getImportance() < NotificationManager.IMPORTANCE_LOW) {
+            return false;
+        }
+
+        Class<? extends Notification.Style> style = getNotificationStyle(record);
+        if (Notification.MessagingStyle.class.equals(style)) {
+            return true;
+        }
+
+        if (record.getContactAffinity() > ValidateNotificationPeople.NONE) {
+            return true;
+        }
+
+        if (record.getNotification().category == Notification.CATEGORY_MESSAGE
+                && isDefaultMessagingApp(record)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    private boolean isOngoing(NotificationRecord record) {
+        final int ongoingFlags =
+                Notification.FLAG_FOREGROUND_SERVICE | Notification.FLAG_ONGOING_EVENT;
+        return (record.getNotification().flags & ongoingFlags) != 0;
+    }
+
+
+    private Class<? extends Notification.Style> getNotificationStyle(NotificationRecord record) {
+        String templateClass =
+                record.getNotification().extras.getString(Notification.EXTRA_TEMPLATE);
+
+        if (!TextUtils.isEmpty(templateClass)) {
+            return Notification.getNotificationStyleClass(templateClass);
+        }
+        return null;
+    }
+
+    private boolean isMediaNotification(NotificationRecord record) {
+        return record.getNotification().extras.getParcelable(
+                Notification.EXTRA_MEDIA_SESSION) != null;
+    }
+
+    private boolean isCall(NotificationRecord record) {
+        return record.getNotification().category == Notification.CATEGORY_CALL
+                && isDefaultPhoneApp(record.sbn.getPackageName());
+    }
+
+    private boolean isDefaultPhoneApp(String pkg) {
+        if (mDefaultPhoneApp == null) {
+            final TelecomManager telecomm =
+                    (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
+            mDefaultPhoneApp = telecomm != null ? telecomm.getDefaultDialerPackage() : null;
+        }
+        return Objects.equals(pkg, mDefaultPhoneApp);
+    }
+
+    @SuppressWarnings("deprecation")
+    private boolean isDefaultMessagingApp(NotificationRecord record) {
+        final int userId = record.getUserId();
+        if (userId == UserHandle.USER_NULL || userId == UserHandle.USER_ALL) return false;
+        if (mDefaultSmsApp.get(userId) == null) {
+            mDefaultSmsApp.put(userId, Settings.Secure.getStringForUser(
+                    mContext.getContentResolver(),
+                    Settings.Secure.SMS_DEFAULT_APPLICATION, userId));
+        }
+        return Objects.equals(mDefaultSmsApp.get(userId), record.sbn.getPackageName());
+    }
+
+    private final BroadcastReceiver mPhoneAppBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            mDefaultPhoneApp =
+                    intent.getStringExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME);
+        }
+    };
+
+    private final ContentObserver mSmsContentObserver = new ContentObserver(
+            new Handler(Looper.getMainLooper())) {
+        @Override
+        public void onChange(boolean selfChange, Uri uri, int userId) {
+            if (Settings.Secure.getUriFor(DEFAULT_SMS_APP_SETTING).equals(uri)) {
+                mDefaultSmsApp.put(userId, Settings.Secure.getStringForUser(
+                        mContext.getContentResolver(),
+                        Settings.Secure.SMS_DEFAULT_APPLICATION, userId));
+
+            }
+        }
+    };
 }
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 89101a8..95718de 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -73,7 +73,7 @@
     private static final int DEFAULT_IMPORTANCE = NotificationManager.IMPORTANCE_UNSPECIFIED;
 
     private final NotificationSignalExtractor[] mSignalExtractors;
-    private final NotificationComparator mPreliminaryComparator = new NotificationComparator();
+    private final NotificationComparator mPreliminaryComparator;
     private final GlobalSortKeyComparator mFinalComparator = new GlobalSortKeyComparator();
 
     private final ArrayMap<String, Record> mRecords = new ArrayMap<>(); // pkg|uid => Record
@@ -90,6 +90,8 @@
         mRankingHandler = rankingHandler;
         mPm = pm;
 
+        mPreliminaryComparator = new NotificationComparator(mContext);
+
         final int N = extractorNames.length;
         mSignalExtractors = new NotificationSignalExtractor[N];
         for (int i = 0; i < N; i++) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 0d63fba..34c1470 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -7599,19 +7599,6 @@
                 compilerFilter = getNonProfileGuidedCompilerFilter(compilerFilter);
             }
 
-            // If the OTA updates a system app which was previously preopted to a non-preopted state
-            // the app might end up being verified at runtime. That's because by default the apps
-            // are verify-profile but for preopted apps there's no profile.
-            // Do a hacky check to ensure that if we have no profiles (a reasonable indication
-            // that before the OTA the app was preopted) the app gets compiled with a non-profile
-            // filter (by default interpret-only).
-            // Note that at this stage unused apps are already filtered.
-            if (isSystemApp(pkg) &&
-                    DexFile.isProfileGuidedCompilerFilter(compilerFilter) &&
-                    !Environment.getReferenceProfile(pkg.packageName).exists()) {
-                compilerFilter = getNonProfileGuidedCompilerFilter(compilerFilter);
-            }
-
             // checkProfiles is false to avoid merging profiles during boot which
             // might interfere with background compilation (b/28612421).
             // Unfortunately this will also means that "pm.dexopt.boot=speed-profile" will
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index 442a4f4..9222917 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -59,8 +59,9 @@
     private static List<Policy> sPolicies = new ArrayList<>();
 
     /** Path to MAC permissions on system image */
-    private static final File MAC_PERMISSIONS = new File(Environment.getRootDirectory(),
-            "/etc/security/mac_permissions.xml");
+    private static final File[] MAC_PERMISSIONS =
+    { new File(Environment.getRootDirectory(), "/etc/security/plat_mac_permissions.xml"),
+      new File(Environment.getRootDirectory(), "/etc/security/nonplat_mac_permissions.xml") };
 
     // Append privapp to existing seinfo label
     private static final String PRIVILEGED_APP_STR = ":privapp";
@@ -87,49 +88,51 @@
 
         FileReader policyFile = null;
         XmlPullParser parser = Xml.newPullParser();
-        try {
-            policyFile = new FileReader(MAC_PERMISSIONS);
-            Slog.d(TAG, "Using policy file " + MAC_PERMISSIONS);
+        for (int i = 0; i < MAC_PERMISSIONS.length; i++) {
+            try {
+                policyFile = new FileReader(MAC_PERMISSIONS[i]);
+                Slog.d(TAG, "Using policy file " + MAC_PERMISSIONS[i]);
 
-            parser.setInput(policyFile);
-            parser.nextTag();
-            parser.require(XmlPullParser.START_TAG, null, "policy");
+                parser.setInput(policyFile);
+                parser.nextTag();
+                parser.require(XmlPullParser.START_TAG, null, "policy");
 
-            while (parser.next() != XmlPullParser.END_TAG) {
-                if (parser.getEventType() != XmlPullParser.START_TAG) {
-                    continue;
+                while (parser.next() != XmlPullParser.END_TAG) {
+                    if (parser.getEventType() != XmlPullParser.START_TAG) {
+                        continue;
+                    }
+
+                    switch (parser.getName()) {
+                        case "signer":
+                            policies.add(readSignerOrThrow(parser));
+                            break;
+                        default:
+                            skip(parser);
+                    }
                 }
-
-                switch (parser.getName()) {
-                    case "signer":
-                        policies.add(readSignerOrThrow(parser));
-                        break;
-                    default:
-                        skip(parser);
-                }
+            } catch (IllegalStateException | IllegalArgumentException |
+                     XmlPullParserException ex) {
+                StringBuilder sb = new StringBuilder("Exception @");
+                sb.append(parser.getPositionDescription());
+                sb.append(" while parsing ");
+                sb.append(MAC_PERMISSIONS[i]);
+                sb.append(":");
+                sb.append(ex);
+                Slog.w(TAG, sb.toString());
+                return false;
+            } catch (IOException ioe) {
+                Slog.w(TAG, "Exception parsing " + MAC_PERMISSIONS[i], ioe);
+                return false;
+            } finally {
+                IoUtils.closeQuietly(policyFile);
             }
-        } catch (IllegalStateException | IllegalArgumentException |
-                XmlPullParserException ex) {
-            StringBuilder sb = new StringBuilder("Exception @");
-            sb.append(parser.getPositionDescription());
-            sb.append(" while parsing ");
-            sb.append(MAC_PERMISSIONS);
-            sb.append(":");
-            sb.append(ex);
-            Slog.w(TAG, sb.toString());
-            return false;
-        } catch (IOException ioe) {
-            Slog.w(TAG, "Exception parsing " + MAC_PERMISSIONS, ioe);
-            return false;
-        } finally {
-            IoUtils.closeQuietly(policyFile);
         }
 
         // Now sort the policy stanzas
         PolicyComparator policySort = new PolicyComparator();
         Collections.sort(policies, policySort);
         if (policySort.foundDuplicate()) {
-            Slog.w(TAG, "ERROR! Duplicate entries found parsing " + MAC_PERMISSIONS);
+            Slog.w(TAG, "ERROR! Duplicate entries found parsing mac_permissions.xml files");
             return false;
         }
 
diff --git a/services/core/java/com/android/server/pm/ShortcutLauncher.java b/services/core/java/com/android/server/pm/ShortcutLauncher.java
index 2af1bcb..3060840 100644
--- a/services/core/java/com/android/server/pm/ShortcutLauncher.java
+++ b/services/core/java/com/android/server/pm/ShortcutLauncher.java
@@ -16,6 +16,7 @@
 package com.android.server.pm;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.content.pm.PackageInfo;
 import android.content.pm.ShortcutInfo;
@@ -103,6 +104,9 @@
         // Nothing to do.
     }
 
+    /**
+     * Pin the given shortcuts, replacing the current pinned ones.
+     */
     public void pinShortcuts(@UserIdInt int packageUserId,
             @NonNull String packageName, @NonNull List<String> ids) {
         final ShortcutPackage packageShortcuts =
@@ -143,11 +147,39 @@
     /**
      * Return the pinned shortcut IDs for the publisher package.
      */
+    @Nullable
     public ArraySet<String> getPinnedShortcutIds(@NonNull String packageName,
             @UserIdInt int packageUserId) {
         return mPinnedShortcuts.get(PackageWithUser.of(packageUserId, packageName));
     }
 
+    /**
+     * Return true if the given shortcut is pinned by this launcher.
+     */
+    public boolean hasPinned(ShortcutInfo shortcut) {
+        final ArraySet<String> pinned =
+                getPinnedShortcutIds(shortcut.getPackage(), shortcut.getUserId());
+        return (pinned != null) && pinned.contains(shortcut.getId());
+    }
+
+    /**
+     * Additionally pin a shortcut. c.f. {@link #pinShortcuts(int, String, List)}
+     */
+    public void addPinnedShortcut(@NonNull String packageName, @UserIdInt int packageUserId,
+            String id) {
+        final ArraySet<String> pinnedSet = getPinnedShortcutIds(packageName, packageUserId);
+        final ArrayList<String> pinnedList;
+        if (pinnedSet != null) {
+            pinnedList = new ArrayList<>(pinnedSet.size() + 1);
+            pinnedList.addAll(pinnedSet);
+        } else {
+            pinnedList = new ArrayList<>(1);
+        }
+        pinnedList.add(id);
+
+        pinShortcuts(packageUserId, packageName, pinnedList);
+    }
+
     boolean cleanUpPackage(String packageName, @UserIdInt int packageUserId) {
         return mPinnedShortcuts.remove(PackageWithUser.of(packageUserId, packageName)) != null;
     }
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 2eb0778..b745062 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -179,7 +179,7 @@
         }
     }
 
-    private void ensureNotImmutable(@NonNull String id) {
+    public void ensureNotImmutable(@NonNull String id) {
         ensureNotImmutable(mShortcuts.get(id));
     }
 
@@ -706,6 +706,7 @@
             for (int i = mShortcuts.size() - 1; i >= 0; i--) {
                 final ShortcutInfo si = mShortcuts.valueAt(i);
 
+                // Disable dynamic shortcuts whose target activity is gone.
                 if (si.isDynamic()) {
                     if (!s.injectIsMainActivity(si.getActivity(), getPackageUserId())) {
                         Slog.w(TAG, String.format(
diff --git a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
new file mode 100644
index 0000000..cdb69ce
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm;
+
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.IPinItemRequest;
+import android.content.pm.LauncherApps;
+import android.content.pm.LauncherApps.PinItemRequest;
+import android.content.pm.ShortcutInfo;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+
+/**
+ * Handles {@link android.content.pm.ShortcutManager#requestPinShortcut} related tasks.
+ */
+class ShortcutRequestPinProcessor {
+    private static final String TAG = ShortcutService.TAG;
+    private static final boolean DEBUG = ShortcutService.DEBUG;
+
+    private final ShortcutService mService;
+    private final Object mLock;
+
+    /**
+     * Internal for {@link android.content.pm.LauncherApps.PinItemRequest} which receives callbacks.
+     */
+    private static class PinShortcutRequestInner extends IPinItemRequest.Stub {
+        private final ShortcutRequestPinProcessor mProcessor;
+        /** Original shortcut passed by the app. */
+        public final ShortcutInfo shortcutOriginal;
+
+        /**
+         * Cloned shortcut that's passed to the launcher.  The notable difference from
+         * {@link #shortcutOriginal} is it must not have the intent.
+         */
+        public final ShortcutInfo shortcutForLauncher;
+
+        private final IntentSender mResultIntent;
+
+        public final String launcherPackage;
+        public final int launcherUserId;
+        public final boolean preExisting;
+
+        @GuardedBy("this")
+        private boolean mAccepted;
+
+        private PinShortcutRequestInner(ShortcutRequestPinProcessor processor,
+                ShortcutInfo shortcutOriginal, ShortcutInfo shortcutForLauncher,
+                IntentSender resultIntent,
+                String launcherPackage, int launcherUserId, boolean preExisting) {
+            mProcessor = processor;
+            this.shortcutOriginal = shortcutOriginal;
+            this.shortcutForLauncher = shortcutForLauncher;
+            mResultIntent = resultIntent;
+            this.launcherPackage = launcherPackage;
+            this.launcherUserId = launcherUserId;
+            this.preExisting = preExisting;
+        }
+
+        @Override
+        public boolean isValid() {
+            // TODO When an app calls requestPinShortcut(), all pending requests should be
+            // invalidated.
+            synchronized (this) {
+                return !mAccepted;
+            }
+        }
+
+        /**
+         * Called when the launcher calls {@link PinItemRequest#accept}.
+         */
+        @Override
+        public boolean accept(Bundle options) {
+            // Make sure the options are unparcellable by the FW. (e.g. not containing unknown
+            // classes.)
+            if (options != null) {
+                try {
+                    options.size();
+                } catch (RuntimeException e) {
+                    throw new IllegalArgumentException("options cannot be unparceled", e);
+                }
+            }
+            synchronized (this) {
+                if (mAccepted) {
+                    throw new IllegalStateException("accept() called already");
+                }
+                mAccepted = true;
+            }
+            if (DEBUG) {
+                Slog.d(TAG, "Launcher accepted shortcut. ID=" + shortcutOriginal.getId()
+                        + " package=" + shortcutOriginal.getPackage()
+                        + " options=" + options);
+            }
+
+            // Pin it and send the result intent.
+            if (mProcessor.directPinShortcut(this)) {
+                mProcessor.sendResultIntent(mResultIntent);
+                return true;
+            } else {
+                return false;
+            }
+        }
+    }
+
+    public ShortcutRequestPinProcessor(ShortcutService service, Object lock) {
+        mService = service;
+        mLock = lock;
+    }
+
+    public boolean isRequestPinnedShortcutSupported(int callingUserId) {
+        return getRequestPinShortcutConfirmationActivity(callingUserId) != null;
+    }
+
+    /**
+     * Handle {@link android.content.pm.ShortcutManager#requestPinShortcut)}.
+     */
+    public boolean requestPinShortcutLocked(ShortcutInfo inShortcut, IntentSender resultIntent) {
+
+        // First, make sure the launcher supports it.
+
+        // Find the confirmation activity in the default launcher.
+        final Pair<ComponentName, Integer> confirmActivity =
+                getRequestPinShortcutConfirmationActivity(inShortcut.getUserId());
+
+        // If the launcher doesn't support it, just return a rejected result and finish.
+        if (confirmActivity == null) {
+            Log.w(TAG, "Launcher doesn't support requestPinnedShortcut(). Shortcut not created.");
+            return false;
+        }
+
+        final ComponentName launcherComponent = confirmActivity.first;
+        final String launcherPackage = confirmActivity.first.getPackageName();
+        final int launcherUserId = confirmActivity.second;
+
+        // Make sure the launcher user is unlocked. (it's always the parent profile, so should
+        // really be unlocked here though.)
+        mService.throwIfUserLockedL(launcherUserId);
+
+        // Next, validate the incoming shortcut, etc.
+
+        final ShortcutPackage ps = mService.getPackageShortcutsForPublisherLocked(
+                inShortcut.getPackage(), inShortcut.getUserId());
+
+        final ShortcutInfo existing = ps.findShortcutById(inShortcut.getId());
+        final boolean existsAlready = existing != null;
+
+        if (DEBUG) {
+            Slog.d(TAG, "requestPinnedShortcut package=" + inShortcut.getPackage()
+                    + " existsAlready=" + existsAlready
+                    + " shortcut=" + inShortcut.toInsecureString());
+        }
+
+        // This is the shortcut that'll be sent to the launcher.
+        final ShortcutInfo shortcutForLauncher;
+
+        if (existsAlready) {
+            validateExistingShortcut(existing);
+
+            // See if it's already pinned.
+            if (mService.getLauncherShortcutsLocked(
+                    launcherPackage, existing.getUserId(), launcherUserId).hasPinned(existing)) {
+                Log.i(TAG, "Launcher's already pinning shortcut " + existing.getId()
+                        + " for package " + existing.getPackage());
+                sendResultIntent(resultIntent);
+                return true;
+            }
+
+            // Pass a clone, not the original.
+            // Note this will remove the intent and icons.
+            shortcutForLauncher = existing.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
+
+            // FLAG_PINNED is still set, if it's pinned by other launchers.
+            shortcutForLauncher.clearFlags(ShortcutInfo.FLAG_PINNED);
+        } else {
+            // It doesn't exist, so it must have all mandatory fields.
+            mService.validateShortcutForPinRequest(inShortcut);
+
+            // Initialize the ShortcutInfo for pending approval.
+            inShortcut.resolveResourceStrings(mService.injectGetResourcesForApplicationAsUser(
+                    inShortcut.getPackage(), inShortcut.getUserId()));
+            if (DEBUG) {
+                Slog.d(TAG, "resolved shortcut=" + inShortcut.toInsecureString());
+            }
+            // We should strip out the intent, but should preserve the icon.
+            shortcutForLauncher = inShortcut.clone(
+                    ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER_APPROVAL);
+        }
+
+        // Create a request object.
+        final PinShortcutRequestInner inner =
+                new PinShortcutRequestInner(this, inShortcut, shortcutForLauncher, resultIntent,
+                        launcherPackage, launcherUserId, existsAlready);
+
+        final PinItemRequest outer = new PinItemRequest(PinItemRequest.REQUEST_TYPE_SHORTCUT,
+                shortcutForLauncher, inner);
+
+        return startRequestConfirmActivity(launcherComponent, launcherUserId, outer);
+    }
+
+    private void validateExistingShortcut(ShortcutInfo shortcutInfo) {
+        // Make sure it's enabled.
+        // (Because we can't always force enable it automatically as it may be a stale
+        // manifest shortcut.)
+        Preconditions.checkArgument(shortcutInfo.isEnabled(),
+                "Shortcut ID=" + shortcutInfo + " already exists but disabled.");
+
+    }
+
+    private boolean startRequestConfirmActivity(ComponentName activity, int launcherUserId,
+            PinItemRequest request) {
+        // Start the activity.
+        final Intent confirmIntent = new Intent(LauncherApps.ACTION_CONFIRM_PIN_ITEM);
+        confirmIntent.setComponent(activity);
+        confirmIntent.putExtra(LauncherApps.EXTRA_PIN_ITEM_REQUEST, request);
+        confirmIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+
+        final long token = mService.injectClearCallingIdentity();
+        try {
+            mService.mContext.startActivityAsUser(
+                    confirmIntent, UserHandle.of(launcherUserId));
+        } catch (RuntimeException e) { // ActivityNotFoundException, etc.
+            Log.e(TAG, "Unable to start activity " + activity, e);
+            return false;
+        } finally {
+            mService.injectRestoreCallingIdentity(token);
+        }
+        return true;
+    }
+
+    /**
+     * Find the activity that handles {@link LauncherApps#ACTION_CONFIRM_PIN_ITEM} in the
+     * default launcher.
+     */
+    @Nullable
+    @VisibleForTesting
+    Pair<ComponentName, Integer> getRequestPinShortcutConfirmationActivity(
+            int callingUserId) {
+        // Find the default launcher.
+        final int launcherUserId = mService.getParentOrSelfUserId(callingUserId);
+        final ComponentName defaultLauncher = mService.getDefaultLauncher(launcherUserId);
+
+        if (defaultLauncher == null) {
+            Log.e(TAG, "Default launcher not found.");
+            return null;
+        }
+        final ComponentName activity = mService.injectGetPinConfirmationActivity(
+                defaultLauncher.getPackageName(), launcherUserId);
+        return (activity == null) ? null : Pair.create(activity, launcherUserId);
+    }
+
+    public void sendResultIntent(@Nullable IntentSender intent) {
+        if (DEBUG) {
+            Slog.d(TAG, "Sending result intent.");
+        }
+        mService.injectSendIntentSender(intent);
+    }
+
+    /**
+     * The last step of the "request pin shortcut" flow.  Called when the launcher accepted a
+     * request.
+     */
+    public boolean directPinShortcut(PinShortcutRequestInner request) {
+
+        final ShortcutInfo original = request.shortcutOriginal;
+        final int appUserId = original.getUserId();
+        final String appPackageName = original.getPackage();
+        final int launcherUserId = request.launcherUserId;
+        final String launcherPackage = request.launcherPackage;
+        final String shortcutId = original.getId();
+
+        synchronized (mLock) {
+            if (!(mService.isUserUnlockedL(appUserId)
+                    && mService.isUserUnlockedL(request.launcherUserId))) {
+                Log.w(TAG, "User is locked now.");
+                return false;
+            }
+
+            final ShortcutPackage ps = mService.getPackageShortcutsForPublisherLocked(
+                    appPackageName, appUserId);
+            final ShortcutInfo current = ps.findShortcutById(shortcutId);
+
+            // The shortcut might have been changed, so we need to do the same validation again.
+            try {
+                if (current == null) {
+                    // It doesn't exist, so it must have all necessary fields.
+                    mService.validateShortcutForPinRequest(original);
+                } else {
+                    validateExistingShortcut(current);
+                }
+            } catch (RuntimeException e) {
+                Log.w(TAG, "Unable to pin shortcut: " + e.getMessage());
+                return false;
+            }
+
+            // If the shortcut doesn't exist, need to create it.
+            // First, create it as a dynamic shortcut.
+            if (current == null) {
+                if (DEBUG) {
+                    Slog.d(TAG, "Temporarily adding " + shortcutId + " as dynamic");
+                }
+                // Add as a dynamic shortcut.
+                if (original.getActivity() == null) {
+                    original.setActivity(mService.getDummyMainActivity(appPackageName));
+                }
+                ps.addOrUpdateDynamicShortcut(original);
+            }
+
+            // Pin the shortcut.
+            if (DEBUG) {
+                Slog.d(TAG, "Pinning " + shortcutId);
+            }
+
+            final ShortcutLauncher launcher = mService.getLauncherShortcutsLocked(
+                    launcherPackage, appUserId, launcherUserId);
+            launcher.attemptToRestoreIfNeededAndSave();
+            launcher.addPinnedShortcut(appPackageName, appUserId, shortcutId);
+
+            if (current == null) {
+                if (DEBUG) {
+                    Slog.d(TAG, "Removing " + shortcutId + " as dynamic");
+                }
+                ps.deleteDynamicWithId(shortcutId);
+            }
+
+            ps.adjustRanks(); // Shouldn't be needed, but just in case.
+        }
+
+        mService.verifyStates();
+        mService.packageShortcutsChanged(appPackageName, appUserId);
+
+        return true;
+    }
+}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index c5c1c0c..436a53c 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -29,6 +29,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.IntentSender.SendIntentException;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
@@ -42,8 +44,10 @@
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
 import android.content.pm.ShortcutServiceInternal;
 import android.content.pm.ShortcutServiceInternal.ShortcutChangeListener;
+import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.graphics.Bitmap;
@@ -190,6 +194,8 @@
     private static final String KEY_LOW_RAM = "lowRam";
     private static final String KEY_ICON_SIZE = "iconSize";
 
+    private static final String DUMMY_MAIN_ACTIVITY = "android.__dummy__";
+
     @VisibleForTesting
     interface ConfigConstants {
         /**
@@ -298,6 +304,8 @@
     private final UsageStatsManagerInternal mUsageStatsManagerInternal;
     private final ActivityManagerInternal mActivityManagerInternal;
 
+    private final ShortcutRequestPinProcessor mShortcutRequestPinProcessor;
+
     @GuardedBy("mLock")
     final SparseIntArray mUidState = new SparseIntArray();
 
@@ -336,8 +344,9 @@
         int IS_ACTIVITY_ENABLED = 13;
         int PACKAGE_UPDATE_CHECK = 14;
         int ASYNC_PRELOAD_USER_DELAY = 15;
+        int GET_DEFAULT_LAUNCHER = 16;
 
-        int COUNT = ASYNC_PRELOAD_USER_DELAY + 1;
+        int COUNT = GET_DEFAULT_LAUNCHER + 1;
     }
 
     private static final String[] STAT_LABELS = {
@@ -356,7 +365,8 @@
             "checkLauncherActivity",
             "isActivityEnabled",
             "packageUpdateCheck",
-            "asyncPreloadUserDelay"
+            "asyncPreloadUserDelay",
+            "getDefaultLauncher()"
     };
 
     final Object mStatLock = new Object();
@@ -417,6 +427,8 @@
         mActivityManagerInternal = Preconditions.checkNotNull(
                 LocalServices.getService(ActivityManagerInternal.class));
 
+        mShortcutRequestPinProcessor = new ShortcutRequestPinProcessor(this, mLock);
+
         if (onlyForPackageManagerApis) {
             return; // Don't do anything further.  For unit tests only.
         }
@@ -1591,12 +1603,11 @@
      * - Make sure the intent's extras are persistable, and them to set
      * {@link ShortcutInfo#mIntentPersistableExtrases}.  Also clear its extras.
      * - Clear flags.
-     *
-     * TODO Detailed unit tests
      */
-    private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate) {
+    private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate,
+            boolean forPinRequest) {
         Preconditions.checkNotNull(shortcut, "Null shortcut detected");
-        if (shortcut.getActivity() != null) {
+        if (!forPinRequest && shortcut.getActivity() != null) {
             Preconditions.checkState(
                     shortcut.getPackage().equals(shortcut.getActivity().getPackageName()),
                     "Cannot publish shortcut: activity " + shortcut.getActivity() + " does not"
@@ -1608,10 +1619,13 @@
         }
 
         if (!forUpdate) {
-            shortcut.enforceMandatoryFields(/* forPinned= */ false);
-            Preconditions.checkArgument(
-                    injectIsMainActivity(shortcut.getActivity(), shortcut.getUserId()),
-                    "Cannot publish shortcut: " + shortcut.getActivity() + " is not main activity");
+            shortcut.enforceMandatoryFields(/* forPinned= */ forPinRequest);
+            if (!forPinRequest) {
+                Preconditions.checkArgument(
+                        injectIsMainActivity(shortcut.getActivity(), shortcut.getUserId()),
+                        "Cannot publish shortcut: " + shortcut.getActivity()
+                                + " is not main activity");
+            }
         }
         if (shortcut.getIcon() != null) {
             ShortcutInfo.validateIcon(shortcut.getIcon());
@@ -1620,11 +1634,18 @@
         shortcut.replaceFlags(0);
     }
 
+    private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate) {
+        fixUpIncomingShortcutInfo(shortcut, forUpdate, /*forPinRequest=*/ false);
+    }
+
+    public void validateShortcutForPinRequest(@NonNull ShortcutInfo shortcut) {
+        fixUpIncomingShortcutInfo(shortcut, /* forUpdate= */ false, /*forPinRequest=*/ true);
+    }
+
     /**
      * When a shortcut has no target activity, set the default one from the package.
      */
     private void fillInDefaultActivity(List<ShortcutInfo> shortcuts) {
-
         ComponentName defaultActivity = null;
         for (int i = shortcuts.size() - 1; i >= 0; i--) {
             final ShortcutInfo si = shortcuts.get(i);
@@ -1834,6 +1855,31 @@
     }
 
     @Override
+    public boolean requestPinShortcut(String packageName, ShortcutInfo shortcut,
+            IntentSender resultIntent, int userId) {
+        verifyCaller(packageName, userId);
+        Preconditions.checkNotNull(shortcut);
+        Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled");
+
+        final boolean ret;
+        synchronized (mLock) {
+            throwIfUserLockedL(userId);
+
+            Preconditions.checkState(isUidForegroundLocked(injectBinderCallingUid()),
+                    "Calling application must have a foreground activity or a foreground service");
+
+            // TODO Cancel all pending requests from the caller.
+
+            // Send request to the launcher, if supported.
+            ret = mShortcutRequestPinProcessor.requestPinShortcutLocked(shortcut, resultIntent);
+        }
+
+        verifyStates();
+
+        return ret;
+    }
+
+    @Override
     public void disableShortcuts(String packageName, List shortcutIds,
             CharSequence disabledMessage, int disabledMessageResId, @UserIdInt int userId) {
         verifyCaller(packageName, userId);
@@ -2049,6 +2095,16 @@
         }
     }
 
+    @Override
+    public boolean isRequestPinShortcutSupported(int callingUserId) {
+        final long token = injectClearCallingIdentity();
+        try {
+            return mShortcutRequestPinProcessor.isRequestPinnedShortcutSupported(callingUserId);
+        } finally {
+            injectRestoreCallingIdentity(token);
+        }
+    }
+
     /**
      * Reset all throttling, for developer options and command line.  Only system/shell can call
      * it.
@@ -2113,77 +2169,22 @@
     // This method is extracted so we can directly call this method from unit tests,
     // even when hasShortcutPermission() is overridden.
     @VisibleForTesting
-    boolean hasShortcutHostPermissionInner(@NonNull String callingPackage, int userId) {
+    boolean hasShortcutHostPermissionInner(@NonNull String packageName, int userId) {
         synchronized (mLock) {
             throwIfUserLockedL(userId);
 
             final ShortcutUser user = getUserShortcutsLocked(userId);
 
-            // Always trust the in-memory cache.
+            // Always trust the cached component.
             final ComponentName cached = user.getCachedLauncher();
             if (cached != null) {
-                if (cached.getPackageName().equals(callingPackage)) {
+                if (cached.getPackageName().equals(packageName)) {
                     return true;
                 }
             }
             // If the cached one doesn't match, then go ahead
 
-            final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
-
-            // Default launcher from package manager.
-            final long startGetHomeActivitiesAsUser = injectElapsedRealtime();
-            final ComponentName defaultLauncher = mPackageManagerInternal
-                    .getHomeActivitiesAsUser(allHomeCandidates, userId);
-            logDurationStat(Stats.GET_DEFAULT_HOME, startGetHomeActivitiesAsUser);
-
-            ComponentName detected;
-            if (defaultLauncher != null) {
-                detected = defaultLauncher;
-                if (DEBUG) {
-                    Slog.v(TAG, "Default launcher from PM: " + detected);
-                }
-            } else {
-                detected = user.getLastKnownLauncher();
-
-                if (detected != null) {
-                    if (injectIsActivityEnabledAndExported(detected, userId)) {
-                        if (DEBUG) {
-                            Slog.v(TAG, "Cached launcher: " + detected);
-                        }
-                    } else {
-                        Slog.w(TAG, "Cached launcher " + detected + " no longer exists");
-                        detected = null;
-                        user.clearLauncher();
-                    }
-                }
-            }
-
-            if (detected == null) {
-                // If we reach here, that means it's the first check since the user was created,
-                // and there's already multiple launchers and there's no default set.
-                // Find the system one with the highest priority.
-                // (We need to check the priority too because of FallbackHome in Settings.)
-                // If there's no system launcher yet, then no one can access shortcuts, until
-                // the user explicitly
-                final int size = allHomeCandidates.size();
-
-                int lastPriority = Integer.MIN_VALUE;
-                for (int i = 0; i < size; i++) {
-                    final ResolveInfo ri = allHomeCandidates.get(i);
-                    if (!ri.activityInfo.applicationInfo.isSystemApp()) {
-                        continue;
-                    }
-                    if (DEBUG) {
-                        Slog.d(TAG, String.format("hasShortcutPermissionInner: pkg=%s prio=%d",
-                                ri.activityInfo.getComponentName(), ri.priority));
-                    }
-                    if (ri.priority < lastPriority) {
-                        continue;
-                    }
-                    detected = ri.activityInfo.getComponentName();
-                    lastPriority = ri.priority;
-                }
-            }
+            final ComponentName detected = getDefaultLauncher(userId);
 
             // Update the cache.
             user.setLauncher(detected);
@@ -2191,7 +2192,7 @@
                 if (DEBUG) {
                     Slog.v(TAG, "Detected launcher: " + detected);
                 }
-                return detected.getPackageName().equals(callingPackage);
+                return detected.getPackageName().equals(packageName);
             } else {
                 // Default launcher not found.
                 return false;
@@ -2199,6 +2200,80 @@
         }
     }
 
+    @Nullable
+    ComponentName getDefaultLauncher(@UserIdInt int userId) {
+        final long start = injectElapsedRealtime();
+        final long token = injectClearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                throwIfUserLockedL(userId);
+
+                final ShortcutUser user = getUserShortcutsLocked(userId);
+
+                final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
+
+                // Default launcher from package manager.
+                final long startGetHomeActivitiesAsUser = injectElapsedRealtime();
+                final ComponentName defaultLauncher = mPackageManagerInternal
+                        .getHomeActivitiesAsUser(allHomeCandidates, userId);
+                logDurationStat(Stats.GET_DEFAULT_HOME, startGetHomeActivitiesAsUser);
+
+                ComponentName detected = null;
+                if (defaultLauncher != null) {
+                    detected = defaultLauncher;
+                    if (DEBUG) {
+                        Slog.v(TAG, "Default launcher from PM: " + detected);
+                    }
+                } else {
+                    detected = user.getLastKnownLauncher();
+
+                    if (detected != null) {
+                        if (injectIsActivityEnabledAndExported(detected, userId)) {
+                            if (DEBUG) {
+                                Slog.v(TAG, "Cached launcher: " + detected);
+                            }
+                        } else {
+                            Slog.w(TAG, "Cached launcher " + detected + " no longer exists");
+                            detected = null;
+                            user.clearLauncher();
+                        }
+                    }
+                }
+
+                if (detected == null) {
+                    // If we reach here, that means it's the first check since the user was created,
+                    // and there's already multiple launchers and there's no default set.
+                    // Find the system one with the highest priority.
+                    // (We need to check the priority too because of FallbackHome in Settings.)
+                    // If there's no system launcher yet, then no one can access shortcuts, until
+                    // the user explicitly
+                    final int size = allHomeCandidates.size();
+
+                    int lastPriority = Integer.MIN_VALUE;
+                    for (int i = 0; i < size; i++) {
+                        final ResolveInfo ri = allHomeCandidates.get(i);
+                        if (!ri.activityInfo.applicationInfo.isSystemApp()) {
+                            continue;
+                        }
+                        if (DEBUG) {
+                            Slog.d(TAG, String.format("hasShortcutPermissionInner: pkg=%s prio=%d",
+                                    ri.activityInfo.getComponentName(), ri.priority));
+                        }
+                        if (ri.priority < lastPriority) {
+                            continue;
+                        }
+                        detected = ri.activityInfo.getComponentName();
+                        lastPriority = ri.priority;
+                    }
+                }
+                return detected;
+            }
+        } finally {
+            injectRestoreCallingIdentity(token);
+            logDurationStat(Stats.GET_DEFAULT_LAUNCHER, start);
+        }
+    }
+
     // === House keeping ===
 
     private void cleanUpPackageForAllLoadedUsers(String packageName, @UserIdInt int packageUserId,
@@ -3034,10 +3109,21 @@
         if (activity != null) {
             baseIntent.setComponent(activity);
         }
+        return queryActivities(baseIntent, userId, /* exportedOnly =*/ true);
+    }
 
-        final List<ResolveInfo> resolved =
-                mContext.getPackageManager().queryIntentActivitiesAsUser(
-                        baseIntent, PACKAGE_MATCH_FLAGS, userId);
+    @NonNull
+    List<ResolveInfo> queryActivities(@NonNull Intent intent, int userId,
+            boolean exportedOnly) {
+        final List<ResolveInfo> resolved;
+        final long token = injectClearCallingIdentity();
+        try {
+            resolved =
+                    mContext.getPackageManager().queryIntentActivitiesAsUser(
+                            intent, PACKAGE_MATCH_FLAGS, userId);
+        } finally {
+            injectRestoreCallingIdentity(token);
+        }
         if (resolved == null || resolved.size() == 0) {
             return EMPTY_RESOLVE_INFO;
         }
@@ -3045,7 +3131,9 @@
         if (!isInstalled(resolved.get(0).activityInfo)) {
             return EMPTY_RESOLVE_INFO;
         }
-        resolved.removeIf(ACTIVITY_NOT_EXPORTED);
+        if (exportedOnly) {
+            resolved.removeIf(ACTIVITY_NOT_EXPORTED);
+        }
         return resolved;
     }
 
@@ -3056,14 +3144,11 @@
     @Nullable
     ComponentName injectGetDefaultMainActivity(@NonNull String packageName, int userId) {
         final long start = injectElapsedRealtime();
-        final long token = injectClearCallingIdentity();
         try {
             final List<ResolveInfo> resolved =
                     queryActivities(getMainActivityIntent(), packageName, null, userId);
             return resolved.size() == 0 ? null : resolved.get(0).activityInfo.getComponentName();
         } finally {
-            injectRestoreCallingIdentity(token);
-
             logDurationStat(Stats.GET_LAUNCHER_ACTIVITY, start);
         }
     }
@@ -3073,31 +3158,36 @@
      */
     boolean injectIsMainActivity(@NonNull ComponentName activity, int userId) {
         final long start = injectElapsedRealtime();
-        final long token = injectClearCallingIdentity();
         try {
-            final List<ResolveInfo> resolved =
-                    queryActivities(getMainActivityIntent(), activity.getPackageName(),
-                            activity, userId);
+            if (DUMMY_MAIN_ACTIVITY.equals(activity.getClassName())) {
+                return true;
+            }
+            final List<ResolveInfo> resolved = queryActivities(
+                    getMainActivityIntent(), activity.getPackageName(), activity, userId);
             return resolved.size() > 0;
         } finally {
-            injectRestoreCallingIdentity(token);
-
             logDurationStat(Stats.CHECK_LAUNCHER_ACTIVITY, start);
         }
     }
 
     /**
+     * Create a dummy "main activity" component name which is used to create a dynamic shortcut
+     * with no main activity temporarily.
+     */
+    @NonNull
+    ComponentName getDummyMainActivity(@NonNull String packageName) {
+        return new ComponentName(packageName, DUMMY_MAIN_ACTIVITY);
+    }
+
+    /**
      * Return all the enabled, exported and main activities from a package.
      */
     @NonNull
     List<ResolveInfo> injectGetMainActivities(@NonNull String packageName, int userId) {
         final long start = injectElapsedRealtime();
-        final long token = injectClearCallingIdentity();
         try {
             return queryActivities(getMainActivityIntent(), packageName, null, userId);
         } finally {
-            injectRestoreCallingIdentity(token);
-
             logDurationStat(Stats.CHECK_LAUNCHER_ACTIVITY, start);
         }
     }
@@ -3109,17 +3199,33 @@
     boolean injectIsActivityEnabledAndExported(
             @NonNull ComponentName activity, @UserIdInt int userId) {
         final long start = injectElapsedRealtime();
-        final long token = injectClearCallingIdentity();
         try {
             return queryActivities(new Intent(), activity.getPackageName(), activity, userId)
                     .size() > 0;
         } finally {
-            injectRestoreCallingIdentity(token);
-
             logDurationStat(Stats.IS_ACTIVITY_ENABLED, start);
         }
     }
 
+    /**
+     * Get the {@link LauncherApps#ACTION_CONFIRM_PIN_ITEM} activity in a given package.
+     */
+    @Nullable
+    ComponentName injectGetPinConfirmationActivity(@NonNull String launcherPackageName,
+            int launcherUserId) {
+        Preconditions.checkNotNull(launcherPackageName);
+
+        final Intent confirmIntent = new Intent(LauncherApps.ACTION_CONFIRM_PIN_ITEM);
+        confirmIntent.setPackage(launcherPackageName);
+
+        final List<ResolveInfo> candidates = queryActivities(
+                confirmIntent, launcherUserId, /* exportedOnly =*/ false);
+        for (ResolveInfo ri : candidates) {
+            return ri.activityInfo.getComponentName();
+        }
+        return null;
+    }
+
     boolean injectIsSafeModeEnabled() {
         final long token = injectClearCallingIdentity();
         try {
@@ -3133,6 +3239,32 @@
         }
     }
 
+    /**
+     * If {@code userId} is of a managed profile, return the parent user ID.  Otherwise return
+     * itself.
+     */
+    int getParentOrSelfUserId(int userId) {
+        final long token = injectClearCallingIdentity();
+        try {
+            final UserInfo parent = mUserManager.getProfileParent(userId);
+            return (parent != null) ? parent.id : userId;
+        } finally {
+            injectRestoreCallingIdentity(token);
+        }
+    }
+
+    void injectSendIntentSender(IntentSender intentSender) {
+        if (intentSender == null) {
+            return;
+        }
+        try {
+            intentSender.sendIntent(mContext, /* code= */ 0, /* intent= */ null,
+                    /* onFinished=*/ null, /* handler= */ null);
+        } catch (SendIntentException e) {
+            Slog.w(TAG, "sendIntent failed().", e);
+        }
+    }
+
     // === Backup & restore ===
 
     boolean shouldBackupApp(String packageName, int userId) {
@@ -3749,6 +3881,11 @@
         }
     }
 
+    @VisibleForTesting
+    ShortcutRequestPinProcessor getShortcutRequestPinProcessorForTest() {
+        return mShortcutRequestPinProcessor;
+    }
+
     /**
      * Control whether {@link #verifyStates} should be performed.  We always perform it during unit
      * tests.
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index bac7a76..05228ec 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -2269,8 +2269,11 @@
 
     private UserInfo createUserInternal(String name, int flags, int parentId,
             String[] disallowedPackages) {
-        if (hasUserRestriction(UserManager.DISALLOW_ADD_USER, UserHandle.getCallingUserId())) {
-            Log.w(LOG_TAG, "Cannot add user. DISALLOW_ADD_USER is enabled.");
+        String restriction = ((flags & UserInfo.FLAG_MANAGED_PROFILE) != 0)
+                ? UserManager.DISALLOW_ADD_MANAGED_PROFILE
+                : UserManager.DISALLOW_ADD_USER;
+        if (hasUserRestriction(restriction, UserHandle.getCallingUserId())) {
+            Log.w(LOG_TAG, "Cannot add user. " + restriction + " is enabled.");
             return null;
         }
         return createUserInternalUnchecked(name, flags, parentId, disallowedPackages);
@@ -2541,9 +2544,16 @@
     public boolean removeUser(int userHandle) {
         Slog.i(LOG_TAG, "removeUser u" + userHandle);
         checkManageOrCreateUsersPermission("Only the system can remove users");
-        if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(
-                UserManager.DISALLOW_REMOVE_USER, false)) {
-            Log.w(LOG_TAG, "Cannot remove user. DISALLOW_REMOVE_USER is enabled.");
+
+        final boolean isManagedProfile;
+        synchronized (mUsersLock) {
+            UserInfo userInfo = getUserInfoLU(userHandle);
+            isManagedProfile = userInfo != null && userInfo.isManagedProfile();
+        }
+        String restriction = isManagedProfile
+                ? UserManager.DISALLOW_REMOVE_MANAGED_PROFILE : UserManager.DISALLOW_REMOVE_USER;
+        if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(restriction, false)) {
+            Log.w(LOG_TAG, "Cannot remove user. " + restriction + " is enabled.");
             return false;
         }
         return removeUserUnchecked(userHandle);
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 7ec3c19..e91cce1 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -75,12 +75,14 @@
             UserManager.DISALLOW_USB_FILE_TRANSFER,
             UserManager.DISALLOW_CONFIG_CREDENTIALS,
             UserManager.DISALLOW_REMOVE_USER,
+            UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
             UserManager.DISALLOW_DEBUGGING_FEATURES,
             UserManager.DISALLOW_CONFIG_VPN,
             UserManager.DISALLOW_CONFIG_TETHERING,
             UserManager.DISALLOW_NETWORK_RESET,
             UserManager.DISALLOW_FACTORY_RESET,
             UserManager.DISALLOW_ADD_USER,
+            UserManager.DISALLOW_ADD_MANAGED_PROFILE,
             UserManager.ENSURE_VERIFY_APPS,
             UserManager.DISALLOW_CONFIG_CELL_BROADCASTS,
             UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
@@ -124,6 +126,8 @@
             UserManager.DISALLOW_NETWORK_RESET,
             UserManager.DISALLOW_FACTORY_RESET,
             UserManager.DISALLOW_ADD_USER,
+            UserManager.DISALLOW_ADD_MANAGED_PROFILE,
+            UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
             UserManager.DISALLOW_CONFIG_CELL_BROADCASTS,
             UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
             UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA,
@@ -155,6 +159,13 @@
     );
 
     /**
+     * User restrictions that default to {@code true} for device owners.
+     */
+    private static final Set<String> DEFAULT_ENABLED_FOR_DEVICE_OWNERS = Sets.newArraySet(
+            UserManager.DISALLOW_ADD_MANAGED_PROFILE
+    );
+
+    /**
      * Throws {@link IllegalArgumentException} if the given restriction name is invalid.
      */
     public static boolean isValidRestriction(@NonNull String restriction) {
@@ -249,6 +260,13 @@
     }
 
     /**
+     * Returns the user restrictions that default to {@code true} for device owners.
+     */
+    public static @NonNull Set<String> getDefaultEnabledForDeviceOwner() {
+        return DEFAULT_ENABLED_FOR_DEVICE_OWNERS;
+    }
+
+    /**
      * Takes restrictions that can be set by device owner, and sort them into what should be applied
      * globally and what should be applied only on the current user.
      */
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index b058f95..9d4b51f 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -6685,9 +6685,7 @@
 
         // Navigation bar and status bar.
         getNonDecorInsetsLw(displayRotation, displayWidth, displayHeight, outInsets);
-        if (mStatusBar != null) {
-            outInsets.top = mStatusBarHeight;
-        }
+        outInsets.top = mStatusBarHeight;
     }
 
     @Override
@@ -6696,7 +6694,7 @@
         outInsets.setEmpty();
 
         // Only navigation bar
-        if (mNavigationBar != null) {
+        if (mHasNavigationBar) {
             int position = navigationBarPosition(displayWidth, displayHeight, displayRotation);
             if (position == NAV_BAR_BOTTOM) {
                 outInsets.bottom = getNavigationBarHeight(displayRotation, mUiMode);
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index f8b461e..e6bc7f4 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -390,9 +390,8 @@
                 inputMethodManagerInternal.hideCurrentInputMethod();
                 mImeHideRequested = true;
             }
-        } else if (setMinimizedDockedStack(false)) {
-            mService.mWindowPlacerLocked.performSurfacePlacement();
         }
+        setMinimizedDockedStack(false, false /* animate */);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index 0ad4e0a..34633c2 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -18,12 +18,13 @@
 
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.util.TypedValue.COMPLEX_UNIT_DIP;
-import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import android.animation.ValueAnimator;
+import android.app.RemoteAction;
+import android.content.pm.ParceledListSlice;
 import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.PointF;
@@ -41,12 +42,13 @@
 import android.view.IPinnedStackController;
 import android.view.IPinnedStackListener;
 
-import com.android.internal.os.BackgroundThread;
 import com.android.internal.policy.PipMotionHelper;
 import com.android.internal.policy.PipSnapAlgorithm;
 import com.android.server.UiThread;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Holds the common state of the pinned stack between the system and SystemUI.
@@ -70,10 +72,14 @@
     // States that affect how the PIP can be manipulated
     private boolean mInInteractiveMode;
     private boolean mIsMinimized;
+    private boolean mIsSnappingToEdge;
     private boolean mIsImeShowing;
     private int mImeHeight;
     private ValueAnimator mBoundsAnimator = null;
 
+    // The set of actions that are currently allowed on the PiP activity
+    private ArrayList<RemoteAction> mActions = new ArrayList<>();
+
     // Used to calculate stack bounds across rotations
     private final DisplayInfo mDisplayInfo = new DisplayInfo();
 
@@ -113,6 +119,7 @@
         @Override
         public void setSnapToEdge(final boolean snapToEdge) {
             mHandler.post(() -> {
+                mIsSnappingToEdge = snapToEdge;
                 mSnapAlgorithm.setSnapToEdge(snapToEdge);
             });
         }
@@ -171,6 +178,9 @@
             listener.onListenerRegistered(mCallbacks);
             mPinnedStackListener = listener;
             notifyBoundsChanged(mIsImeShowing);
+            notifyMinimizeChanged(mIsMinimized);
+            notifySnapToEdgeChanged(mIsSnappingToEdge);
+            notifyActionsChanged(mActions);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to register pinned stack listener", e);
         }
@@ -315,7 +325,16 @@
     }
 
     /**
-     * Sends a broadcast that the PIP movement bounds have changed.
+     * Sets the current set of actions.
+     */
+    void setActions(List<RemoteAction> actions) {
+        mActions.clear();
+        mActions.addAll(actions);
+        notifyActionsChanged(mActions);
+    }
+
+    /**
+     * Notifies listeners that the PIP movement bounds have changed.
      */
     private void notifyBoundsChanged(boolean adjustedForIme) {
         if (mPinnedStackListener != null) {
@@ -328,6 +347,45 @@
     }
 
     /**
+     * Notifies listeners that the PIP minimized state has changed.
+     */
+    private void notifyMinimizeChanged(boolean isMinimized) {
+        if (mPinnedStackListener != null) {
+            try {
+                mPinnedStackListener.onMinimizedStateChanged(isMinimized);
+            } catch (RemoteException e) {
+                Slog.e(TAG_WM, "Error delivering minimize changed event.", e);
+            }
+        }
+    }
+
+    /**
+     * Notifies listeners that the PIP snap-to-edge state has changed.
+     */
+    private void notifySnapToEdgeChanged(boolean isSnappingToEdge) {
+        if (mPinnedStackListener != null) {
+            try {
+                mPinnedStackListener.onSnapToEdgeStateChanged(isSnappingToEdge);
+            } catch (RemoteException e) {
+                Slog.e(TAG_WM, "Error delivering snap-to-edge changed event.", e);
+            }
+        }
+    }
+
+    /**
+     * Notifies listeners that the PIP actions have changed.
+     */
+    private void notifyActionsChanged(List<RemoteAction> actions) {
+        if (mPinnedStackListener != null) {
+            try {
+                mPinnedStackListener.onActionsChanged(new ParceledListSlice(actions));
+            } catch (RemoteException e) {
+                Slog.e(TAG_WM, "Error delivering actions changed event.", e);
+            }
+        }
+    }
+
+    /**
      * @return the bounds on the screen that the PIP can be visible in.
      */
     private void getInsetBounds(Rect outRect) {
@@ -355,5 +413,16 @@
         pw.println(prefix + "  mIsImeShowing=" + mIsImeShowing);
         pw.println(prefix + "  mInInteractiveMode=" + mInInteractiveMode);
         pw.println(prefix + "  mIsMinimized=" + mIsMinimized);
+        if (mActions.isEmpty()) {
+            pw.println(prefix + "  mActions=[]");
+        } else {
+            pw.println(prefix + "  mActions=[");
+            for (int i = 0; i < mActions.size(); i++) {
+                RemoteAction action = mActions.get(i);
+                pw.print(prefix + "    Action[" + i + "]: ");
+                action.dump("", pw);
+            }
+            pw.println(prefix + "  ]");
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 158fd2b..577e5a0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -26,6 +26,7 @@
 import android.app.ActivityManagerInternal;
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
+import android.app.RemoteAction;
 import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
@@ -3405,22 +3406,6 @@
         }
     }
 
-    public void setPictureInPictureAspectRatio(float aspectRatio) {
-        synchronized (mWindowMap) {
-            if (!mSupportsPictureInPicture) {
-                return;
-            }
-
-            final TaskStack stack = mStackIdToStack.get(PINNED_STACK_ID);
-            if (stack == null) {
-                return;
-            }
-
-            animateResizePinnedStack(getPictureInPictureBounds(
-                    stack.getDisplayContent().getDisplayId(), aspectRatio), -1);
-        }
-    }
-
     public Rect getPictureInPictureBounds(int displayId, float aspectRatio) {
         synchronized (mWindowMap) {
             if (!mSupportsPictureInPicture) {
@@ -3447,6 +3432,36 @@
     }
 
     /**
+     * Sets the current picture-in-picture aspect ratio.
+     */
+    public void setPictureInPictureAspectRatio(float aspectRatio) {
+        synchronized (mWindowMap) {
+            final TaskStack stack = mStackIdToStack.get(PINNED_STACK_ID);
+            if (!mSupportsPictureInPicture || stack == null) {
+                return;
+            }
+
+            final int displayId = stack.getDisplayContent().getDisplayId();
+            final Rect toBounds = getPictureInPictureBounds(displayId, aspectRatio);
+            animateResizePinnedStack(toBounds, -1 /* duration */);
+        }
+    }
+
+    /**
+     * Sets the current picture-in-picture actions.
+     */
+    public void setPictureInPictureActions(List<RemoteAction> actions) {
+        synchronized (mWindowMap) {
+            final TaskStack stack = mStackIdToStack.get(PINNED_STACK_ID);
+            if (!mSupportsPictureInPicture || stack == null) {
+                return;
+            }
+
+            stack.getDisplayContent().getPinnedStackController().setActions(actions);
+        }
+    }
+
+    /**
      * Place a TaskStack on a DisplayContent. Will create a new TaskStack if none is found with
      * specified stackId.
      * @param stackId The unique identifier of the new stack.
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index 50a6095..3e8e420 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -74,6 +74,7 @@
     libutils \
     android.hardware.audio.common@2.0 \
     android.hardware.gnss@1.0 \
+    android.hardware.ir@1.0 \
     android.hardware.light@2.0 \
     android.hardware.power@1.0 \
     android.hardware.thermal@1.0 \
diff --git a/services/core/jni/com_android_server_ConsumerIrService.cpp b/services/core/jni/com_android_server_ConsumerIrService.cpp
index 7104870..5906e6b 100644
--- a/services/core/jni/com_android_server_ConsumerIrService.cpp
+++ b/services/core/jni/com_android_server_ConsumerIrService.cpp
@@ -23,87 +23,70 @@
 #include <stdlib.h>
 #include <utils/misc.h>
 #include <utils/Log.h>
-#include <hardware/hardware.h>
-#include <hardware/consumerir.h>
+#include <android/hardware/ir/1.0/IConsumerIr.h>
 #include <ScopedPrimitiveArray.h>
 
+using ::android::hardware::ir::V1_0::IConsumerIr;
+using ::android::hardware::ir::V1_0::ConsumerIrFreqRange;
+using ::android::hardware::hidl_vec;
+
 namespace android {
 
-static jlong halOpen(JNIEnv* /* env */, jobject /* obj */) {
-    hw_module_t const* module;
-    consumerir_device_t *dev;
-    int err;
+static sp<IConsumerIr> mHal;
 
-    err = hw_get_module(CONSUMERIR_HARDWARE_MODULE_ID, &module);
-    if (err != 0) {
-        ALOGE("Can't open consumer IR HW Module, error: %d", err);
-        return 0;
-    }
-
-    err = module->methods->open(module, CONSUMERIR_TRANSMITTER,
-            (hw_device_t **) &dev);
-    if (err < 0) {
-        ALOGE("Can't open consumer IR transmitter, error: %d", err);
-        return 0;
-    }
-
-    return reinterpret_cast<jlong>(dev);
+static jboolean halOpen(JNIEnv* /* env */, jobject /* obj */) {
+    // TODO(b/31632518)
+    mHal = IConsumerIr::getService("consumerir");
+    return mHal != nullptr;
 }
 
-static jint halTransmit(JNIEnv *env, jobject /* obj */, jlong halObject,
-   jint carrierFrequency, jintArray pattern) {
-    int ret;
-
-    consumerir_device_t *dev = reinterpret_cast<consumerir_device_t*>(halObject);
+static jint halTransmit(JNIEnv *env, jobject /* obj */, jint carrierFrequency,
+   jintArray pattern) {
     ScopedIntArrayRO cPattern(env, pattern);
     if (cPattern.get() == NULL) {
         return -EINVAL;
     }
-    jsize patternLength = cPattern.size();
+    hidl_vec<int32_t> patternVec;
+    patternVec.setToExternal(const_cast<int32_t*>(cPattern.get()), cPattern.size());
 
-    ret = dev->transmit(dev, carrierFrequency, cPattern.get(), patternLength);
-
-    return reinterpret_cast<jint>(ret);
+    bool success = mHal->transmit(carrierFrequency, patternVec, cPattern.size());
+    return success ? 0 : -1;
 }
 
-static jintArray halGetCarrierFrequencies(JNIEnv *env, jobject /* obj */,
-    jlong halObject) {
-    consumerir_device_t *dev = reinterpret_cast<consumerir_device_t*>(halObject);
-    consumerir_freq_range_t *ranges;
+static jintArray halGetCarrierFrequencies(JNIEnv *env, jobject /* obj */) {
     int len;
+    hidl_vec<ConsumerIrFreqRange> ranges;
+    bool success;
 
-    len = dev->get_num_carrier_freqs(dev);
-    if (len <= 0)
-        return NULL;
+    auto cb = [&](bool s, hidl_vec<ConsumerIrFreqRange> vec) {
+            ranges = vec;
+            success = s;
+    };
+    mHal->getCarrierFreqs(cb);
 
-    ranges = new consumerir_freq_range_t[len];
-
-    len = dev->get_carrier_freqs(dev, len, ranges);
-    if (len <= 0) {
-        delete[] ranges;
+    if (!success) {
         return NULL;
     }
+    len = ranges.size();
 
     int i;
     ScopedIntArrayRW freqsOut(env, env->NewIntArray(len*2));
     jint *arr = freqsOut.get();
     if (arr == NULL) {
-        delete[] ranges;
         return NULL;
     }
     for (i = 0; i < len; i++) {
-        arr[i*2] = ranges[i].min;
-        arr[i*2+1] = ranges[i].max;
+        arr[i*2] = static_cast<jint>(ranges[i].min);
+        arr[i*2+1] = static_cast<jint>(ranges[i].max);
     }
 
-    delete[] ranges;
     return freqsOut.getJavaArray();
 }
 
 static const JNINativeMethod method_table[] = {
-    { "halOpen", "()J", (void *)halOpen },
-    { "halTransmit", "(JI[I)I", (void *)halTransmit },
-    { "halGetCarrierFrequencies", "(J)[I", (void *)halGetCarrierFrequencies},
+    { "halOpen", "()Z", (void *)halOpen },
+    { "halTransmit", "(I[I)I", (void *)halTransmit },
+    { "halGetCarrierFrequencies", "()[I", (void *)halGetCarrierFrequencies},
 };
 
 int register_android_server_ConsumerIrService(JNIEnv *env) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index aafc432..3281bd6 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -153,7 +153,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.JournaledFile;
 import com.android.internal.util.ParcelableString;
@@ -4806,6 +4805,20 @@
 
             long ident = mInjector.binderClearCallingIdentity();
             try {
+                final String restriction;
+                if (userHandle == UserHandle.USER_SYSTEM) {
+                    restriction = UserManager.DISALLOW_FACTORY_RESET;
+                } else if (isManagedProfile(userHandle)) {
+                    restriction = UserManager.DISALLOW_REMOVE_MANAGED_PROFILE;
+                } else {
+                    restriction = UserManager.DISALLOW_REMOVE_USER;
+                }
+                if (isAdminAffectedByRestriction(
+                        admin.info.getComponent(), restriction, userHandle)) {
+                    throw new SecurityException("Cannot wipe data. " + restriction
+                            + " restriction is set for user " + userHandle);
+                }
+
                 if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) {
                     if (!isDeviceOwner(admin.info.getComponent(), userHandle)) {
                         throw new SecurityException(
@@ -4817,34 +4830,21 @@
                         manager.wipe();
                     }
                 }
+
                 boolean wipeExtRequested = (flags & WIPE_EXTERNAL_STORAGE) != 0;
-                // If the admin is the only one who has set the restriction: force wipe, even if
-                // {@link UserManager.DISALLOW_FACTORY_RESET} is set. Reason is that the admin
-                // could remove this user restriction anyway.
-                boolean force = (userHandle == UserHandle.USER_SYSTEM)
-                        && isAdminOnlyOneWhoSetRestriction(admin,
-                                UserManager.DISALLOW_FACTORY_RESET, UserHandle.USER_SYSTEM);
                 wipeDeviceOrUserLocked(wipeExtRequested, userHandle,
-                        "DevicePolicyManager.wipeData() from " + source, force);
+                        "DevicePolicyManager.wipeData() from " + source, /*force=*/ true);
             } finally {
                 mInjector.binderRestoreCallingIdentity(ident);
             }
         }
     }
 
-    private boolean isAdminOnlyOneWhoSetRestriction(ActiveAdmin admin, String userRestriction,
-            int userId) {
-        int source = mUserManager.getUserRestrictionSource(userRestriction, UserHandle.of(userId));
-        if (isDeviceOwner(admin.info.getComponent(), userId)) {
-            return source == UserManager.RESTRICTION_SOURCE_DEVICE_OWNER;
-        } else if (isProfileOwner(admin.info.getComponent(), userId)) {
-            return source == UserManager.RESTRICTION_SOURCE_PROFILE_OWNER;
-        }
-        return false;
-    }
-
-    private void wipeDeviceOrUserLocked(boolean wipeExtRequested, final int userHandle,
-            String reason, boolean force) {
+    private void wipeDeviceOrUserLocked(
+            boolean wipeExtRequested, final int userHandle, String reason, boolean force) {
+        // TODO If split user is enabled and the device owner is set in the primary user (rather
+        // than system), we should probably trigger factory reset. Current code just remove
+        // that user (but still clears FRP...)
         if (userHandle == UserHandle.USER_SYSTEM) {
             wipeDataLocked(wipeExtRequested, reason, force);
         } else {
@@ -4857,10 +4857,12 @@
                             am.switchUser(UserHandle.USER_SYSTEM);
                         }
 
-                        boolean isManagedProfile = isManagedProfile(userHandle);
-                        if (!mUserManager.removeUser(userHandle)) {
+                        boolean userRemoved = force
+                                ? mUserManagerInternal.removeUserEvenWhenDisallowed(userHandle)
+                                : mUserManager.removeUser(userHandle);
+                        if (!userRemoved) {
                             Slog.w(LOG_TAG, "Couldn't remove user " + userHandle);
-                        } else if (isManagedProfile) {
+                        } else if (isManagedProfile(userHandle)) {
                             sendWipeProfileNotification();
                         }
                     } catch (RemoteException re) {
@@ -5948,7 +5950,8 @@
         }
         synchronized (this) {
             enforceCanSetDeviceOwnerLocked(admin, userId);
-            if (getActiveAdminUncheckedLocked(admin, userId) == null
+            final ActiveAdmin activeAdmin = getActiveAdminUncheckedLocked(admin, userId);
+            if (activeAdmin == null
                     || getUserData(userId).mRemovingAdmins.contains(admin)) {
                 throw new IllegalArgumentException("Not active admin: " + admin);
             }
@@ -5975,12 +5978,23 @@
             mOwners.writeDeviceOwner();
             updateDeviceOwnerLocked();
             setDeviceOwnerSystemPropertyLocked();
-            Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED);
+
+            // STOPSHIP(b/31952368) Also set this restriction for existing DOs on OTA to Android OC.
+            final Set<String> restrictions =
+                    UserRestrictionsUtils.getDefaultEnabledForDeviceOwner();
+            if (!restrictions.isEmpty()) {
+                for (String restriction : restrictions) {
+                    activeAdmin.ensureUserRestrictions().putBoolean(restriction, true);
+                }
+                saveUserRestrictionsLocked(userId);
+            }
 
             ident = mInjector.binderClearCallingIdentity();
             try {
                 // TODO Send to system too?
-                mContext.sendBroadcastAsUser(intent, new UserHandle(userId));
+                mContext.sendBroadcastAsUser(
+                        new Intent(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED),
+                        UserHandle.of(userId));
             } finally {
                 mInjector.binderRestoreCallingIdentity(ident);
             }
@@ -6176,6 +6190,7 @@
             mOwners.setProfileOwner(who, ownerName, userHandle);
             mOwners.writeProfileOwner(userHandle);
             Slog.i(LOG_TAG, "Profile owner set: " + who + " on user " + userHandle);
+
             return true;
         }
     }
@@ -7486,28 +7501,41 @@
     @Override
     public boolean removeUser(ComponentName who, UserHandle userHandle) {
         Preconditions.checkNotNull(who, "ComponentName is null");
-        UserHandle callingUserHandle = mInjector.binderGetCallingUserHandle();
         synchronized (this) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
         }
+
+        final int callingUserId = mInjector.userHandleGetCallingUserId();
         final long id = mInjector.binderClearCallingIdentity();
         try {
-            int restrictionSource = mUserManager.getUserRestrictionSource(
-                    UserManager.DISALLOW_REMOVE_USER, callingUserHandle);
-            if (restrictionSource != UserManager.RESTRICTION_NOT_SET
-                    && restrictionSource != UserManager.RESTRICTION_SOURCE_DEVICE_OWNER) {
+            String restriction = isManagedProfile(userHandle.getIdentifier())
+                    ? UserManager.DISALLOW_REMOVE_MANAGED_PROFILE
+                    : UserManager.DISALLOW_REMOVE_USER;
+            if (isAdminAffectedByRestriction(who, restriction, callingUserId)) {
                 Log.w(LOG_TAG, "The device owner cannot remove a user because "
-                        + "DISALLOW_REMOVE_USER is enabled, and was not set by the device "
-                        + "owner");
+                        + restriction + " is enabled, and was not set by the device owner");
                 return false;
             }
-            return mUserManagerInternal.removeUserEvenWhenDisallowed(
-                    userHandle.getIdentifier());
+            return mUserManagerInternal.removeUserEvenWhenDisallowed(userHandle.getIdentifier());
         } finally {
             mInjector.binderRestoreCallingIdentity(id);
         }
     }
 
+    private boolean isAdminAffectedByRestriction(
+            ComponentName admin, String userRestriction, int userId) {
+        switch(mUserManager.getUserRestrictionSource(userRestriction, UserHandle.of(userId))) {
+            case UserManager.RESTRICTION_NOT_SET:
+                return false;
+            case UserManager.RESTRICTION_SOURCE_DEVICE_OWNER:
+                return !isDeviceOwner(admin, userId);
+            case UserManager.RESTRICTION_SOURCE_PROFILE_OWNER:
+                return !isProfileOwner(admin, userId);
+            default:
+                return true;
+        }
+    }
+
     @Override
     public boolean switchUser(ComponentName who, UserHandle userHandle) {
         Preconditions.checkNotNull(who, "ComponentName is null");
@@ -7613,14 +7641,16 @@
 
             // Save the restriction to ActiveAdmin.
             activeAdmin.ensureUserRestrictions().putBoolean(key, enabledFromThisOwner);
-            saveSettingsLocked(userHandle);
-
-            pushUserRestrictions(userHandle);
-
-            sendChangedNotification(userHandle);
+            saveUserRestrictionsLocked(userHandle);
         }
     }
 
+    private void saveUserRestrictionsLocked(int userId) {
+        saveSettingsLocked(userId);
+        pushUserRestrictions(userId);
+        sendChangedNotification(userId);
+    }
+
     private void pushUserRestrictions(int userId) {
         synchronized (this) {
             final Bundle global;
@@ -8862,26 +8892,33 @@
         if (!hasFeatureManagedUsers()) {
             return CODE_MANAGED_USERS_NOT_SUPPORTED;
         }
-        synchronized (this) {
-            if (mOwners.hasDeviceOwner()) {
-                // STOPSHIP Only allow creating a managed profile if allowed by the device
-                // owner. http://b/31952368
-                if (mInjector.userManagerIsSplitSystemUser()) {
-                    if (callingUserId == UserHandle.USER_SYSTEM) {
-                        // Managed-profiles cannot be setup on the system user.
-                        return CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER;
-                    }
-                }
-            }
+        if (callingUserId == UserHandle.USER_SYSTEM
+                && mInjector.userManagerIsSplitSystemUser()) {
+            // Managed-profiles cannot be setup on the system user.
+            return CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER;
         }
         if (getProfileOwner(callingUserId) != null) {
             // Managed user cannot have a managed profile.
             return CODE_USER_HAS_PROFILE_OWNER;
         }
-        boolean canRemoveProfile =
-                !mUserManager.hasUserRestriction(UserManager.DISALLOW_REMOVE_USER);
         final long ident = mInjector.binderClearCallingIdentity();
         try {
+             /* STOPSHIP(b/31952368) Reinstate a check similar to this once ManagedProvisioning
+                   uses checkProvisioningPreCondition (see ag/1607846) and passes the packageName
+                   there. In isProvisioningAllowed we should check isCallerDeviceOwner, but for
+                   managed provisioning we need to check the package that is going to be set as PO
+                if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE)) {
+                    if (!isCallerDeviceOwner(callingUid)
+                            || isAdminAffectedByRestriction(mOwners.getDeviceOwnerComponent(),
+                                    UserManager.DISALLOW_ADD_MANAGED_PROFILE, callingUserId)) {
+                    // Caller is not DO or the restriction was set by the system.
+                    return false;
+                    }
+                } */
+            // TODO: Allow it if the caller is the DO? DO could just call removeUser() before
+            // provisioning, so not strictly required...
+            boolean canRemoveProfile = !mUserManager.hasUserRestriction(
+                        UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, UserHandle.of(callingUserId));
             if (!mUserManager.canAddMoreManagedProfiles(callingUserId, canRemoveProfile)) {
                 return CODE_CANNOT_ADD_MANAGED_PROFILE;
             }
@@ -9702,9 +9739,8 @@
             final int callingUserId = mInjector.userHandleGetCallingUserId();
             final boolean isCallerDeviceOwner = isDeviceOwner(callingOwner);
             final boolean isCallerManagedProfile = isManagedProfile(callingUserId);
-            if (!isCallerDeviceOwner && !isCallerManagedProfile
-                    /* STOPSHIP(b/32326223) Reinstate when setAffiliationIds is public
-                    ||   !isAffiliatedUser(callingUserId) */) {
+            if ((!isCallerDeviceOwner && !isCallerManagedProfile)
+                    || !isUserAffiliatedWithDevice(callingUserId)) {
                 return targetUsers;
             }
 
@@ -9724,8 +9760,7 @@
 
                         // Both must be the same package and be affiliated in order to bind.
                         if (callingOwnerPackage.equals(targetOwnerPackage)
-                            /* STOPSHIP(b/32326223) Reinstate when setAffiliationIds is public
-                               && isAffiliatedUser(userId)*/) {
+                               && isUserAffiliatedWithDevice(userId)) {
                             targetUsers.add(UserHandle.of(userId));
                         }
                     }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
index 8cb13da..49ae2bc3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
@@ -34,6 +34,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * A class for managing network logging.
@@ -45,17 +46,17 @@
 
     private final DevicePolicyManagerService mDpm;
     private final PackageManagerInternal mPm;
+    private final AtomicBoolean mIsLoggingEnabled = new AtomicBoolean(false);
 
     private IIpConnectivityMetrics mIpConnectivityMetrics;
     private ServiceThread mHandlerThread;
     private NetworkLoggingHandler mNetworkLoggingHandler;
-    private boolean mIsLoggingEnabled;
 
     private final INetdEventCallback mNetdEventCallback = new INetdEventCallback.Stub() {
         @Override
         public void onDnsEvent(String hostname, String[] ipAddresses, int ipAddressesCount,
                 long timestamp, int uid) {
-            if (!mIsLoggingEnabled) {
+            if (!mIsLoggingEnabled.get()) {
                 return;
             }
             DnsEvent dnsEvent = new DnsEvent(hostname, ipAddresses, ipAddressesCount,
@@ -65,7 +66,7 @@
 
         @Override
         public void onConnectEvent(String ipAddr, int port, long timestamp, int uid) {
-            if (!mIsLoggingEnabled) {
+            if (!mIsLoggingEnabled.get()) {
                 return;
             }
             ConnectEvent connectEvent = new ConnectEvent(ipAddr, port, mPm.getNameForUid(uid),
@@ -116,7 +117,7 @@
                         mDpm);
                 mNetworkLoggingHandler.scheduleBatchFinalization(
                         NetworkLoggingHandler.BATCH_FINALIZATION_TIMEOUT_MS);
-                mIsLoggingEnabled = true;
+                mIsLoggingEnabled.set(true);
                 return true;
             } else {
                 return false;
@@ -130,7 +131,7 @@
     boolean stopNetworkLogging() {
         Log.d(TAG, "Stopping network logging");
         // stop the logging regardless of whether we fail to unregister listener
-        mIsLoggingEnabled = false;
+        mIsLoggingEnabled.set(false);
         try {
             if (!checkIpConnectivityMetricsService()) {
                 // the IIpConnectivityMetrics service should have been present at this point
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 6ec25c5..be13499 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -32,6 +32,7 @@
 import android.os.Environment;
 import android.os.FactoryTest;
 import android.os.FileUtils;
+import android.os.IIncidentManager;
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.RemoteException;
@@ -1635,6 +1636,19 @@
                 }
                 traceEnd();
 
+                traceBeginAndSlog("IncidentDaemonReady");
+                try {
+                    // TODO: Switch from checkService to getService once it's always
+                    // in the build and should reliably be there.
+                    final IIncidentManager incident = IIncidentManager.Stub.asInterface(
+                            ServiceManager.checkService("incident"));
+                    if (incident != null) incident.systemRunning();
+                } catch (Throwable e) {
+                    reportWtf("Notifying incident daemon running", e);
+                }
+                traceEnd();
+                
+
                 traceEnd();  // PhaseActivityManagerReady
             }
         });
diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java
index 96c8e9f..d90a4a2 100644
--- a/services/net/java/android/net/dhcp/DhcpPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpPacket.java
@@ -26,8 +26,10 @@
  * Defines basic data and operations needed to build and use packets for the
  * DHCP protocol.  Subclasses create the specific packets used at each
  * stage of the negotiation.
+ *
+ * @hide
  */
-abstract class DhcpPacket {
+public abstract class DhcpPacket {
     protected static final String TAG = "DhcpPacket";
 
     // dhcpcd has a minimum lease of 20 seconds, but DhcpStateMachine would refuse to wake up the
diff --git a/services/net/java/android/net/ip/ConnectivityPacketTracker.java b/services/net/java/android/net/ip/ConnectivityPacketTracker.java
new file mode 100644
index 0000000..884a8a7
--- /dev/null
+++ b/services/net/java/android/net/ip/ConnectivityPacketTracker.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2016 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.net.ip;
+
+import static android.system.OsConstants.*;
+
+import android.net.NetworkUtils;
+import android.net.util.BlockingSocketReader;
+import android.net.util.ConnectivityPacketSummary;
+import android.os.Handler;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.PacketSocketAddress;
+import android.util.Log;
+import android.util.LocalLog;
+
+import libcore.io.IoBridge;
+import libcore.util.HexEncoding;
+
+import java.io.FileDescriptor;
+import java.io.InterruptedIOException;
+import java.io.IOException;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+
+
+/**
+ * Critical connectivity packet tracking daemon.
+ *
+ * Tracks ARP, DHCPv4, and IPv6 RS/RA/NS/NA packets.
+ *
+ * This class's constructor, start() and stop() methods must only be called
+ * from the same thread on which the passed in |log| is accessed.
+ *
+ * Log lines include a hexdump of the packet, which can be decoded via:
+ *
+ *     echo -n H3XSTR1NG | sed -e 's/\([0-9A-F][0-9A-F]\)/\1 /g' -e 's/^/000000 /'
+ *                       | text2pcap - -
+ *                       | tcpdump -n -vv -e -r -
+ *
+ * @hide
+ */
+public class ConnectivityPacketTracker {
+    private static final String TAG = ConnectivityPacketTracker.class.getSimpleName();
+    private static final boolean DBG = false;
+    private static final String MARK_START = "--- START ---";
+    private static final String MARK_STOP = "--- STOP ---";
+
+    private final String mTag;
+    private final Handler mHandler;
+    private final LocalLog mLog;
+    private final BlockingSocketReader mPacketListener;
+
+    public ConnectivityPacketTracker(NetworkInterface netif, LocalLog log) {
+        final String ifname;
+        final int ifindex;
+        final byte[] hwaddr;
+        final int mtu;
+
+        try {
+            ifname = netif.getName();
+            ifindex = netif.getIndex();
+            hwaddr = netif.getHardwareAddress();
+            mtu = netif.getMTU();
+        } catch (NullPointerException|SocketException e) {
+            throw new IllegalArgumentException("bad network interface", e);
+        }
+
+        mTag = TAG + "." + ifname;
+        mHandler = new Handler();
+        mLog = log;
+        mPacketListener = new PacketListener(ifindex, hwaddr, mtu);
+    }
+
+    public void start() {
+        mLog.log(MARK_START);
+        mPacketListener.start();
+    }
+
+    public void stop() {
+        mPacketListener.stop();
+        mLog.log(MARK_STOP);
+    }
+
+    private final class PacketListener extends BlockingSocketReader {
+        private final int mIfIndex;
+        private final byte mHwAddr[];
+
+        PacketListener(int ifindex, byte[] hwaddr, int mtu) {
+            super(mtu);
+            mIfIndex = ifindex;
+            mHwAddr = hwaddr;
+        }
+
+        @Override
+        protected FileDescriptor createSocket() {
+            FileDescriptor s = null;
+            try {
+                // TODO: Evaluate switching to SOCK_DGRAM and changing the
+                // BlockingSocketReader's read() to recvfrom(), so that this
+                // might work on non-ethernet-like links (via SLL).
+                s = Os.socket(AF_PACKET, SOCK_RAW, 0);
+                NetworkUtils.attachControlPacketFilter(s, ARPHRD_ETHER);
+                Os.bind(s, new PacketSocketAddress((short) ETH_P_ALL, mIfIndex));
+            } catch (ErrnoException | IOException e) {
+                logError("Failed to create packet tracking socket: ", e);
+                closeSocket(s);
+                return null;
+            }
+            return s;
+        }
+
+        @Override
+        protected void handlePacket(byte[] recvbuf, int length) {
+            final String summary = ConnectivityPacketSummary.summarize(
+                    mHwAddr, recvbuf, length);
+            if (summary == null) return;
+
+            if (DBG) Log.d(mTag, summary);
+            addLogEntry(summary +
+                        "\n[" + new String(HexEncoding.encode(recvbuf, 0, length)) + "]");
+        }
+
+        @Override
+        protected void logError(String msg, Exception e) {
+            Log.e(mTag, msg, e);
+            addLogEntry(msg + e);
+        }
+
+        private void addLogEntry(String entry) {
+            mHandler.post(() -> mLog.log(entry));
+        }
+    }
+}
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index 58b2dec..87018ec 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -374,6 +374,7 @@
     private static final int EVENT_DHCPACTION_TIMEOUT = 10;
 
     private static final int MAX_LOG_RECORDS = 500;
+    private static final int MAX_PACKET_RECORDS = 100;
 
     private static final boolean NO_CALLBACKS = false;
     private static final boolean SEND_CALLBACKS = true;
@@ -399,6 +400,7 @@
     private final WakeupMessage mDhcpActionTimeoutAlarm;
     private final AvoidBadWifiTracker mAvoidBadWifiTracker;
     private final LocalLog mLocalLog;
+    private final LocalLog mConnectivityPacketLog;
     private final MessageHandlingLogger mMsgStateLogger;
     private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
 
@@ -439,6 +441,7 @@
         mNwService = nwService;
 
         mLocalLog = new LocalLog(MAX_LOG_RECORDS);
+        mConnectivityPacketLog = new LocalLog(MAX_PACKET_RECORDS);
         mMsgStateLogger = new MessageHandlingLogger();
 
         mNetlinkTracker = new NetlinkTracker(
@@ -609,7 +612,7 @@
         }
 
         IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
-        pw.println("APF dump:");
+        pw.println(mTag + " APF dump:");
         pw.increaseIndent();
         // Thread-unsafe access to mApfFilter but just used for debugging.
         ApfFilter apfFilter = mApfFilter;
@@ -625,6 +628,12 @@
         pw.increaseIndent();
         mLocalLog.readOnlyLocalLog().dump(fd, pw, args);
         pw.decreaseIndent();
+
+        pw.println();
+        pw.println(mTag + " connectivity packet log:");
+        pw.increaseIndent();
+        mConnectivityPacketLog.readOnlyLocalLog().dump(fd, pw, args);
+        pw.decreaseIndent();
     }
 
 
@@ -1220,6 +1229,7 @@
     }
 
     class RunningState extends State {
+        private ConnectivityPacketTracker mPacketTracker;
         private boolean mDhcpActionInFlight;
 
         @Override
@@ -1232,6 +1242,9 @@
                 mCallback.setFallbackMulticastFilter(mMulticastFiltering);
             }
 
+            mPacketTracker = createPacketTracker();
+            if (mPacketTracker != null) mPacketTracker.start();
+
             if (mConfiguration.mEnableIPv6 && !startIPv6()) {
                 doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV6);
                 transitionTo(mStoppingState);
@@ -1266,6 +1279,11 @@
                 mDhcpClient.doQuit();
             }
 
+            if (mPacketTracker != null) {
+                mPacketTracker.stop();
+                mPacketTracker = null;
+            }
+
             if (mApfFilter != null) {
                 mApfFilter.shutdown();
                 mApfFilter = null;
@@ -1274,6 +1292,14 @@
             resetLinkProperties();
         }
 
+        private ConnectivityPacketTracker createPacketTracker() {
+            try {
+                return new ConnectivityPacketTracker(mNetworkInterface, mConnectivityPacketLog);
+            } catch (IllegalArgumentException e) {
+                return null;
+            }
+        }
+
         private void ensureDhcpAction() {
             if (!mDhcpActionInFlight) {
                 mCallback.onPreDhcpAction();
diff --git a/services/net/java/android/net/util/BlockingSocketReader.java b/services/net/java/android/net/util/BlockingSocketReader.java
new file mode 100644
index 0000000..12fa1e5
--- /dev/null
+++ b/services/net/java/android/net/util/BlockingSocketReader.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2016 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.net.util;
+
+import android.annotation.Nullable;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+
+import libcore.io.IoBridge;
+
+import java.io.FileDescriptor;
+import java.io.InterruptedIOException;
+import java.io.IOException;
+
+
+/**
+ * A thread that reads from a socket and passes the received packets to a
+ * subclass's handlePacket() method.  The packet receive buffer is recycled
+ * on every read call, so subclasses should make any copies they would like
+ * inside their handlePacket() implementation.
+ *
+ * All public methods may be called from any thread.
+ *
+ * @hide
+ */
+public abstract class BlockingSocketReader {
+    public static final int DEFAULT_RECV_BUF_SIZE = 2 * 1024;
+
+    private final byte[] mPacket;
+    private final Thread mThread;
+    private volatile FileDescriptor mSocket;
+    private volatile boolean mRunning;
+    private volatile long mPacketsReceived;
+
+    // Make it slightly easier for subclasses to properly close a socket
+    // without having to know this incantation.
+    public static final void closeSocket(@Nullable FileDescriptor fd) {
+        try {
+            IoBridge.closeAndSignalBlockedThreads(fd);
+        } catch (IOException ignored) {}
+    }
+
+    protected BlockingSocketReader() {
+        this(DEFAULT_RECV_BUF_SIZE);
+    }
+
+    protected BlockingSocketReader(int recvbufsize) {
+        if (recvbufsize < DEFAULT_RECV_BUF_SIZE) {
+            recvbufsize = DEFAULT_RECV_BUF_SIZE;
+        }
+        mPacket = new byte[recvbufsize];
+        mThread = new Thread(() -> { mainLoop(); });
+    }
+
+    public final boolean start() {
+        if (mSocket != null) return false;
+
+        try {
+            mSocket = createSocket();
+        } catch (Exception e) {
+            logError("Failed to create socket: ", e);
+            return false;
+        }
+
+        if (mSocket == null) return false;
+
+        mRunning = true;
+        mThread.start();
+        return true;
+    }
+
+    public final void stop() {
+        mRunning = false;
+        closeSocket(mSocket);
+        mSocket = null;
+    }
+
+    public final boolean isRunning() { return mRunning; }
+
+    public final long numPacketsReceived() { return mPacketsReceived; }
+
+    /**
+     * Subclasses MUST create the listening socket here, including setting
+     * all desired socket options, interface or address/port binding, etc.
+     */
+    protected abstract FileDescriptor createSocket();
+
+    /**
+     * Called by the main loop for every packet.  Any desired copies of
+     * |recvbuf| should be made in here, and the underlying byte array is
+     * reused across all reads.
+     */
+    protected void handlePacket(byte[] recvbuf, int length) {}
+
+    /**
+     * Called by the main loop to log errors.  In some cases |e| may be null.
+     */
+    protected void logError(String msg, Exception e) {}
+
+    /**
+     * Called by the main loop just prior to exiting.
+     */
+    protected void onExit() {}
+
+    private final void mainLoop() {
+        while (isRunning()) {
+            final int bytesRead;
+
+            try {
+                // Blocking read.
+                // TODO: See if this can be converted to recvfrom.
+                bytesRead = Os.read(mSocket, mPacket, 0, mPacket.length);
+                if (bytesRead < 1) {
+                    if (isRunning()) logError("Socket closed, exiting", null);
+                    break;
+                }
+                mPacketsReceived++;
+            } catch (ErrnoException e) {
+                if (e.errno != OsConstants.EINTR) {
+                    if (isRunning()) logError("read error: ", e);
+                    break;
+                }
+                continue;
+            } catch (IOException ioe) {
+                if (isRunning()) logError("read error: ", ioe);
+                continue;
+            }
+
+            try {
+                handlePacket(mPacket, bytesRead);
+            } catch (Exception e) {
+                logError("Unexpected exception: ", e);
+                break;
+            }
+        }
+
+        stop();
+        onExit();
+    }
+}
diff --git a/services/net/java/android/net/util/ConnectivityPacketSummary.java b/services/net/java/android/net/util/ConnectivityPacketSummary.java
new file mode 100644
index 0000000..699ba5b
--- /dev/null
+++ b/services/net/java/android/net/util/ConnectivityPacketSummary.java
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2016 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.net.util;
+
+import android.net.dhcp.DhcpPacket;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+import java.util.StringJoiner;
+
+import static android.system.OsConstants.*;
+import static android.net.util.NetworkConstants.*;
+
+
+/**
+ * Critical connectivity packet summarizing class.
+ *
+ * Outputs short descriptions of ARP, DHCPv4, and IPv6 RS/RA/NS/NA packets.
+ *
+ * @hide
+ */
+public class ConnectivityPacketSummary {
+    private static final String TAG = ConnectivityPacketSummary.class.getSimpleName();
+
+    private final byte[] mHwAddr;
+    private final byte[] mBytes;
+    private final int mLength;
+    private final ByteBuffer mPacket;
+    private final String mSummary;
+
+    public static String summarize(byte[] hwaddr, byte[] buffer) {
+        return summarize(hwaddr, buffer, buffer.length);
+    }
+
+    // Methods called herein perform some but by no means all error checking.
+    // They may throw runtime exceptions on malformed packets.
+    public static String summarize(byte[] hwaddr, byte[] buffer, int length) {
+        if ((hwaddr == null) || (hwaddr.length != ETHER_ADDR_LEN)) return null;
+        if (buffer == null) return null;
+        length = Math.min(length, buffer.length);
+        return (new ConnectivityPacketSummary(hwaddr, buffer, length)).toString();
+    }
+
+    private ConnectivityPacketSummary(byte[] hwaddr, byte[] buffer, int length) {
+        mHwAddr = hwaddr;
+        mBytes = buffer;
+        mLength = Math.min(length, mBytes.length);
+        mPacket = ByteBuffer.wrap(mBytes, 0, mLength);
+        mPacket.order(ByteOrder.BIG_ENDIAN);
+
+        final StringJoiner sj = new StringJoiner(" ");
+        // TODO: support other link-layers, or even no link-layer header.
+        parseEther(sj);
+        mSummary = sj.toString();
+    }
+
+    public String toString() {
+        return mSummary;
+    }
+
+    private void parseEther(StringJoiner sj) {
+        if (mPacket.remaining() < ETHER_HEADER_LEN) {
+            sj.add("runt:").add(asString(mPacket.remaining()));
+            return;
+        }
+
+        mPacket.position(ETHER_SRC_ADDR_OFFSET);
+        final ByteBuffer srcMac = (ByteBuffer) mPacket.slice().limit(ETHER_ADDR_LEN);
+        sj.add(ByteBuffer.wrap(mHwAddr).equals(srcMac) ? "TX" : "RX");
+        sj.add(getMacAddressString(srcMac));
+
+        mPacket.position(ETHER_DST_ADDR_OFFSET);
+        final ByteBuffer dstMac = (ByteBuffer) mPacket.slice().limit(ETHER_ADDR_LEN);
+        sj.add(">").add(getMacAddressString(dstMac));
+
+        mPacket.position(ETHER_TYPE_OFFSET);
+        final int etherType = asUint(mPacket.getShort());
+        switch (etherType) {
+            case ETHER_TYPE_ARP:
+                sj.add("arp");
+                parseARP(sj);
+                break;
+            case ETHER_TYPE_IPV4:
+                sj.add("ipv4");
+                parseIPv4(sj);
+                break;
+            case ETHER_TYPE_IPV6:
+                sj.add("ipv6");
+                parseIPv6(sj);
+                break;
+            default:
+                // Unknown ether type.
+                sj.add("ethtype").add(asString(etherType));
+                break;
+        }
+    }
+
+    private void parseARP(StringJoiner sj) {
+        if (mPacket.remaining() < ARP_PAYLOAD_LEN) {
+            sj.add("runt:").add(asString(mPacket.remaining()));
+            return;
+        }
+
+        if (asUint(mPacket.getShort()) != ARP_HWTYPE_ETHER ||
+            asUint(mPacket.getShort()) != ETHER_TYPE_IPV4 ||
+            asUint(mPacket.get()) != ETHER_ADDR_LEN ||
+            asUint(mPacket.get()) != IPV4_ADDR_LEN) {
+            sj.add("unexpected header");
+            return;
+        }
+
+        final int opCode = asUint(mPacket.getShort());
+
+        final String senderHwAddr = getMacAddressString(mPacket);
+        final String senderIPv4 = getIPv4AddressString(mPacket);
+        getMacAddressString(mPacket);  // target hardware address, unused
+        final String targetIPv4 = getIPv4AddressString(mPacket);
+
+        if (opCode == ARP_REQUEST) {
+            sj.add("who-has").add(targetIPv4);
+        } else if (opCode == ARP_REPLY) {
+            sj.add("reply").add(senderIPv4).add(senderHwAddr);
+        } else {
+            sj.add("unknown opcode").add(asString(opCode));
+        }
+    }
+
+    private void parseIPv4(StringJoiner sj) {
+        if (!mPacket.hasRemaining()) {
+            sj.add("runt");
+            return;
+        }
+
+        final int startOfIpLayer = mPacket.position();
+        final int ipv4HeaderLength = (mPacket.get(startOfIpLayer) & IPV4_IHL_MASK) * 4;
+        if (mPacket.remaining() < ipv4HeaderLength ||
+            mPacket.remaining() < IPV4_HEADER_MIN_LEN) {
+            sj.add("runt:").add(asString(mPacket.remaining()));
+            return;
+        }
+        final int startOfTransportLayer = startOfIpLayer + ipv4HeaderLength;
+
+        mPacket.position(startOfIpLayer + IPV4_FLAGS_OFFSET);
+        final int flagsAndFragment = asUint(mPacket.getShort());
+        final boolean isFragment = (flagsAndFragment & IPV4_FRAGMENT_MASK) != 0;
+
+        mPacket.position(startOfIpLayer + IPV4_PROTOCOL_OFFSET);
+        final int protocol = asUint(mPacket.get());
+
+        mPacket.position(startOfIpLayer + IPV4_SRC_ADDR_OFFSET);
+        final String srcAddr = getIPv4AddressString(mPacket);
+
+        mPacket.position(startOfIpLayer + IPV4_DST_ADDR_OFFSET);
+        final String dstAddr = getIPv4AddressString(mPacket);
+
+        sj.add(srcAddr).add(">").add(dstAddr);
+
+        mPacket.position(startOfTransportLayer);
+        if (protocol == IPPROTO_UDP) {
+            sj.add("udp");
+            if (isFragment) sj.add("fragment");
+            else parseUDP(sj);
+        } else {
+            sj.add("proto").add(asString(protocol));
+            if (isFragment) sj.add("fragment");
+        }
+    }
+
+    private void parseIPv6(StringJoiner sj) {
+        if (mPacket.remaining() < IPV6_HEADER_LEN) {
+            sj.add("runt:").add(asString(mPacket.remaining()));
+            return;
+        }
+
+        final int startOfIpLayer = mPacket.position();
+
+        mPacket.position(startOfIpLayer + IPV6_PROTOCOL_OFFSET);
+        final int protocol = asUint(mPacket.get());
+
+        mPacket.position(startOfIpLayer + IPV6_SRC_ADDR_OFFSET);
+        final String srcAddr = getIPv6AddressString(mPacket);
+        final String dstAddr = getIPv6AddressString(mPacket);
+
+        sj.add(srcAddr).add(">").add(dstAddr);
+
+        mPacket.position(startOfIpLayer + IPV6_HEADER_LEN);
+        if (protocol == IPPROTO_ICMPV6) {
+            sj.add("icmp6");
+            parseICMPv6(sj);
+        } else {
+            sj.add("proto").add(asString(protocol));
+        }
+    }
+
+    private void parseICMPv6(StringJoiner sj) {
+        if (mPacket.remaining() < ICMPV6_HEADER_MIN_LEN) {
+            sj.add("runt:").add(asString(mPacket.remaining()));
+            return;
+        }
+
+        final int icmp6Type = asUint(mPacket.get());
+        final int icmp6Code = asUint(mPacket.get());
+        mPacket.getShort();  // checksum, unused
+
+        switch (icmp6Type) {
+            case ICMPV6_ROUTER_SOLICITATION:
+                sj.add("rs");
+                parseICMPv6RouterSolicitation(sj);
+                break;
+            case ICMPV6_ROUTER_ADVERTISEMENT:
+                sj.add("ra");
+                parseICMPv6RouterAdvertisement(sj);
+                break;
+            case ICMPV6_NEIGHBOR_SOLICITATION:
+                sj.add("ns");
+                parseICMPv6NeighborMessage(sj);
+                break;
+            case ICMPV6_NEIGHBOR_ADVERTISEMENT:
+                sj.add("na");
+                parseICMPv6NeighborMessage(sj);
+                break;
+            default:
+                sj.add("type").add(asString(icmp6Type));
+                sj.add("code").add(asString(icmp6Code));
+                break;
+        }
+    }
+
+    private void parseICMPv6RouterSolicitation(StringJoiner sj) {
+        final int RESERVED = 4;
+        if (mPacket.remaining() < RESERVED) {
+            sj.add("runt:").add(asString(mPacket.remaining()));
+            return;
+        }
+
+        mPacket.position(mPacket.position() + RESERVED);
+        parseICMPv6NeighborDiscoveryOptions(sj);
+    }
+
+    private void parseICMPv6RouterAdvertisement(StringJoiner sj) {
+        final int FLAGS_AND_TIMERS = 3 * 4;
+        if (mPacket.remaining() < FLAGS_AND_TIMERS) {
+            sj.add("runt:").add(asString(mPacket.remaining()));
+            return;
+        }
+
+        mPacket.position(mPacket.position() + FLAGS_AND_TIMERS);
+        parseICMPv6NeighborDiscoveryOptions(sj);
+    }
+
+    private void parseICMPv6NeighborMessage(StringJoiner sj) {
+        final int RESERVED = 4;
+        final int minReq = RESERVED + IPV6_ADDR_LEN;
+        if (mPacket.remaining() < minReq) {
+            sj.add("runt:").add(asString(mPacket.remaining()));
+            return;
+        }
+
+        mPacket.position(mPacket.position() + RESERVED);
+        sj.add(getIPv6AddressString(mPacket));
+        parseICMPv6NeighborDiscoveryOptions(sj);
+    }
+
+    private void parseICMPv6NeighborDiscoveryOptions(StringJoiner sj) {
+        // All ND options are TLV, where T is one byte and L is one byte equal
+        // to the length of T + L + V in units of 8 octets.
+        while (mPacket.remaining() >= ICMPV6_ND_OPTION_MIN_LENGTH) {
+            final int ndType = asUint(mPacket.get());
+            final int ndLength = asUint(mPacket.get());
+            final int ndBytes = ndLength * ICMPV6_ND_OPTION_LENGTH_SCALING_FACTOR - 2;
+            if (mPacket.remaining() < ndBytes) break;
+            final int position = mPacket.position();
+
+            switch (ndType) {
+                    case ICMPV6_ND_OPTION_SLLA:
+                        sj.add("slla");
+                        sj.add(getMacAddressString(mPacket));
+                        break;
+                    case ICMPV6_ND_OPTION_TLLA:
+                        sj.add("tlla");
+                        sj.add(getMacAddressString(mPacket));
+                        break;
+                    case ICMPV6_ND_OPTION_MTU:
+                        sj.add("mtu");
+                        final short reserved = mPacket.getShort();
+                        sj.add(asString(mPacket.getInt()));
+                        break;
+                    default:
+                        // Skip.
+                        break;
+            }
+
+            mPacket.position(position + ndBytes);
+        }
+    }
+
+    private void parseUDP(StringJoiner sj) {
+        if (mPacket.remaining() < UDP_HEADER_LEN) {
+            sj.add("runt:").add(asString(mPacket.remaining()));
+            return;
+        }
+
+        final int previous = mPacket.position();
+        final int srcPort = asUint(mPacket.getShort());
+        final int dstPort = asUint(mPacket.getShort());
+        sj.add(asString(srcPort)).add(">").add(asString(dstPort));
+
+        mPacket.position(previous + UDP_HEADER_LEN);
+        if (srcPort == DHCP4_CLIENT_PORT || dstPort == DHCP4_CLIENT_PORT) {
+            sj.add("dhcp4");
+            parseDHCPv4(sj);
+        }
+    }
+
+    private void parseDHCPv4(StringJoiner sj) {
+        final DhcpPacket dhcpPacket;
+        try {
+            dhcpPacket = DhcpPacket.decodeFullPacket(mBytes, mLength, DhcpPacket.ENCAP_L2);
+            sj.add(dhcpPacket.toString());
+        } catch (DhcpPacket.ParseException e) {
+            sj.add("parse error: " + e);
+        }
+    }
+
+    private static String getIPv4AddressString(ByteBuffer ipv4) {
+        return getIpAddressString(ipv4, IPV4_ADDR_LEN);
+    }
+
+    private static String getIPv6AddressString(ByteBuffer ipv6) {
+        return getIpAddressString(ipv6, IPV6_ADDR_LEN);
+    }
+
+    private static String getIpAddressString(ByteBuffer ip, int byteLength) {
+        if (ip == null || ip.remaining() < byteLength) return "invalid";
+
+        byte[] bytes = new byte[byteLength];
+        ip.get(bytes, 0, byteLength);
+        try {
+            InetAddress addr = InetAddress.getByAddress(bytes);
+            return addr.getHostAddress();
+        } catch (UnknownHostException uhe) {
+            return "unknown";
+        }
+    }
+
+    private static String getMacAddressString(ByteBuffer mac) {
+        if (mac == null || mac.remaining() < ETHER_ADDR_LEN) return "invalid";
+
+        byte[] bytes = new byte[ETHER_ADDR_LEN];
+        mac.get(bytes, 0, bytes.length);
+        Byte[] printableBytes = new Byte[bytes.length];
+        int i = 0;
+        for (byte b : bytes) printableBytes[i++] = b;
+
+        final String MAC48_FORMAT = "%02x:%02x:%02x:%02x:%02x:%02x";
+        return String.format(MAC48_FORMAT, printableBytes);
+    }
+}
diff --git a/services/net/java/android/net/util/NetworkConstants.java b/services/net/java/android/net/util/NetworkConstants.java
new file mode 100644
index 0000000..362f757
--- /dev/null
+++ b/services/net/java/android/net/util/NetworkConstants.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2016 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.net.util;
+
+import java.nio.ByteBuffer;
+
+
+/**
+ * Networking protocol constants.
+ *
+ * Includes:
+ *     - constants that describe packet layout
+ *     - various helper functions
+ *
+ * @hide
+ */
+public final class NetworkConstants {
+    private NetworkConstants() { throw new RuntimeException("no instance permitted"); }
+
+    /**
+     * Ethernet constants.
+     *
+     * See also:
+     *     - https://tools.ietf.org/html/rfc894
+     *     - https://tools.ietf.org/html/rfc7042
+     *     - http://www.iana.org/assignments/ethernet-numbers/ethernet-numbers.xhtml
+     *     - http://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml
+     */
+    public static final int ETHER_DST_ADDR_OFFSET = 0;
+    public static final int ETHER_SRC_ADDR_OFFSET = 6;
+    public static final int ETHER_ADDR_LEN = 6;
+
+    public static final int ETHER_TYPE_OFFSET = 12;
+    public static final int ETHER_TYPE_LENGTH = 2;
+    public static final int ETHER_TYPE_ARP  = 0x0806;
+    public static final int ETHER_TYPE_IPV4 = 0x0800;
+    public static final int ETHER_TYPE_IPV6 = 0x86dd;
+
+    public static final int ETHER_HEADER_LEN = 14;
+
+    private static final byte FF = asByte(0xff);
+    public static final byte[] ETHER_ADDR_BROADCAST = {
+        FF, FF, FF, FF, FF, FF
+    };
+
+    /**
+     * ARP constants.
+     *
+     * See also:
+     *     - https://tools.ietf.org/html/rfc826
+     *     - http://www.iana.org/assignments/arp-parameters/arp-parameters.xhtml
+     */
+    public static final int ARP_PAYLOAD_LEN = 28;  // For Ethernet+IPv4.
+    public static final int ARP_REQUEST = 1;
+    public static final int ARP_REPLY   = 2;
+    public static final int ARP_HWTYPE_RESERVED_LO = 0;
+    public static final int ARP_HWTYPE_ETHER       = 1;
+    public static final int ARP_HWTYPE_RESERVED_HI = 0xffff;
+
+    /**
+     * IPv4 constants.
+     *
+     * See als:
+     *     - https://tools.ietf.org/html/rfc791
+     */
+    public static final int IPV4_HEADER_MIN_LEN = 20;
+    public static final int IPV4_IHL_MASK = 0xf;
+    public static final int IPV4_FLAGS_OFFSET = 6;
+    public static final int IPV4_FRAGMENT_MASK = 0x1fff;
+    public static final int IPV4_PROTOCOL_OFFSET = 9;
+    public static final int IPV4_SRC_ADDR_OFFSET = 12;
+    public static final int IPV4_DST_ADDR_OFFSET = 16;
+    public static final int IPV4_ADDR_LEN = 4;
+
+    /**
+     * IPv6 constants.
+     *
+     * See also:
+     *     - https://tools.ietf.org/html/rfc2460
+     */
+    public static final int IPV6_HEADER_LEN = 40;
+    public static final int IPV6_PROTOCOL_OFFSET = 6;
+    public static final int IPV6_SRC_ADDR_OFFSET = 8;
+    public static final int IPV6_DST_ADDR_OFFSET = 24;
+    public static final int IPV6_ADDR_LEN = 16;
+
+    /**
+     * ICMPv6 constants.
+     *
+     * See also:
+     *     - https://tools.ietf.org/html/rfc4443
+     *     - https://tools.ietf.org/html/rfc4861
+     */
+    public static final int ICMPV6_HEADER_MIN_LEN = 4;
+    public static final int ICMPV6_ROUTER_SOLICITATION    = 133;
+    public static final int ICMPV6_ROUTER_ADVERTISEMENT   = 134;
+    public static final int ICMPV6_NEIGHBOR_SOLICITATION  = 135;
+    public static final int ICMPV6_NEIGHBOR_ADVERTISEMENT = 136;
+
+    public static final int ICMPV6_ND_OPTION_MIN_LENGTH = 8;
+    public static final int ICMPV6_ND_OPTION_LENGTH_SCALING_FACTOR = 8;
+    public static final int ICMPV6_ND_OPTION_SLLA = 1;
+    public static final int ICMPV6_ND_OPTION_TLLA = 2;
+    public static final int ICMPV6_ND_OPTION_MTU  = 5;
+
+    /**
+     * UDP constants.
+     *
+     * See also:
+     *     - https://tools.ietf.org/html/rfc768
+     */
+    public static final int UDP_HEADER_LEN = 8;
+
+    /**
+     * DHCP(v4) constants.
+     *
+     * See also:
+     *     - https://tools.ietf.org/html/rfc2131
+     */
+    public static final int DHCP4_SERVER_PORT = 67;
+    public static final int DHCP4_CLIENT_PORT = 68;
+
+    /**
+     * Utility functions.
+     */
+    public static byte asByte(int i) { return (byte) i; }
+
+    public static String asString(int i) { return Integer.toString(i); }
+
+    public static int asUint(byte b) { return (b & 0xff); }
+    public static int asUint(short s) { return (s & 0xffff); }
+}
diff --git a/services/print/java/com/android/server/print/RemotePrintService.java b/services/print/java/com/android/server/print/RemotePrintService.java
index fa37576..fb78457 100644
--- a/services/print/java/com/android/server/print/RemotePrintService.java
+++ b/services/print/java/com/android/server/print/RemotePrintService.java
@@ -132,8 +132,6 @@
     }
 
     private void handleDestroy() {
-        throwIfDestroyed();
-
         // Stop tracking printers.
         stopTrackingAllPrinters();
 
@@ -174,7 +172,6 @@
     }
 
     private void handleOnAllPrintJobsHandled() {
-        throwIfDestroyed();
         mHasActivePrintJobs = false;
         if (!isBound()) {
             // The service is dead and neither has active jobs nor discovery
@@ -208,7 +205,6 @@
     }
 
     private void handleRequestCancelPrintJob(final PrintJobInfo printJob) {
-        throwIfDestroyed();
         if (!isBound()) {
             ensureBound();
             mPendingCommands.add(new Runnable() {
@@ -235,7 +231,6 @@
     }
 
     private void handleOnPrintJobQueued(final PrintJobInfo printJob) {
-        throwIfDestroyed();
         mHasActivePrintJobs = true;
         if (!isBound()) {
             ensureBound();
@@ -262,7 +257,6 @@
     }
 
     private void handleCreatePrinterDiscoverySession() {
-        throwIfDestroyed();
         mHasPrinterDiscoverySession = true;
         if (!isBound()) {
             ensureBound();
@@ -289,7 +283,6 @@
     }
 
     private void handleDestroyPrinterDiscoverySession() {
-        throwIfDestroyed();
         mHasPrinterDiscoverySession = false;
         if (!isBound()) {
             // The service is dead and neither has active jobs nor discovery
@@ -328,7 +321,6 @@
     }
 
     private void handleStartPrinterDiscovery(final List<PrinterId> priorityList) {
-        throwIfDestroyed();
         // Take a note that we are doing discovery.
         mDiscoveryPriorityList = new ArrayList<PrinterId>();
         if (priorityList != null) {
@@ -359,7 +351,6 @@
     }
 
     private void handleStopPrinterDiscovery() {
-        throwIfDestroyed();
         // We are not doing discovery anymore.
         mDiscoveryPriorityList = null;
         if (!isBound()) {
@@ -392,7 +383,6 @@
     }
 
     private void handleValidatePrinters(final List<PrinterId> printerIds) {
-        throwIfDestroyed();
         if (!isBound()) {
             ensureBound();
             mPendingCommands.add(new Runnable() {
@@ -419,23 +409,40 @@
     }
 
     /**
-     * Request the custom printer icon for a printer.
+     * Queue a request for a custom printer icon for a printer.
      *
      * @param printerId the id of the printer the icon should be loaded for
-     * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon()
+     * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon
      */
     public void requestCustomPrinterIcon(@NonNull PrinterId printerId) {
-        try {
-            if (isBound()) {
-                mPrintService.requestCustomPrinterIcon(printerId);
+        mHandler.obtainMessage(MyHandler.MSG_REQUEST_CUSTOM_PRINTER_ICON,
+                printerId).sendToTarget();
+    }
+
+    /**
+     * Request a custom printer icon for a printer.
+     *
+     * @param printerId the id of the printer the icon should be loaded for
+     * @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon
+     */
+    private void handleRequestCustomPrinterIcon(@NonNull PrinterId printerId) {
+        if (!isBound()) {
+            ensureBound();
+            mPendingCommands.add(() -> handleRequestCustomPrinterIcon(printerId));
+        } else {
+            if (DEBUG) {
+                Slog.i(LOG_TAG, "[user: " + mUserId + "] requestCustomPrinterIcon()");
             }
-        } catch (RemoteException re) {
-            Slog.e(LOG_TAG, "Error requesting icon for " + printerId, re);
+
+            try {
+                mPrintService.requestCustomPrinterIcon(printerId);
+            } catch (RemoteException re) {
+                Slog.e(LOG_TAG, "Error requesting icon for " + printerId, re);
+            }
         }
     }
 
     private void handleStartPrinterStateTracking(final @NonNull PrinterId printerId) {
-        throwIfDestroyed();
         // Take a note we are tracking the printer.
         if (mTrackedPrinterList == null) {
             mTrackedPrinterList = new ArrayList<PrinterId>();
@@ -467,7 +474,6 @@
     }
 
     private void handleStopPrinterStateTracking(final PrinterId printerId) {
-        throwIfDestroyed();
         // We are no longer tracking the printer.
         if (mTrackedPrinterList == null || !mTrackedPrinterList.remove(printerId)) {
             return;
@@ -581,12 +587,6 @@
         }
     }
 
-    private void throwIfDestroyed() {
-        if (mDestroyed) {
-            throw new IllegalStateException("Cannot interact with a destroyed service");
-        }
-    }
-
     private class RemoteServiceConneciton implements ServiceConnection {
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
@@ -657,6 +657,7 @@
         public static final int MSG_ON_PRINT_JOB_QUEUED = 10;
         public static final int MSG_DESTROY = 11;
         public static final int MSG_BINDER_DIED = 12;
+        public static final int MSG_REQUEST_CUSTOM_PRINTER_ICON = 13;
 
         public MyHandler(Looper looper) {
             super(looper, null, false);
@@ -665,6 +666,11 @@
         @Override
         @SuppressWarnings("unchecked")
         public void handleMessage(Message message) {
+            if (mDestroyed) {
+                Slog.w(LOG_TAG, "Not handling " + message + " as service for " + mComponentName
+                        + " is already destroyed");
+                return;
+            }
             switch (message.what) {
                 case MSG_CREATE_PRINTER_DISCOVERY_SESSION: {
                     handleCreatePrinterDiscoverySession();
@@ -719,6 +725,11 @@
                 case MSG_BINDER_DIED: {
                     handleBinderDied();
                 } break;
+
+                case MSG_REQUEST_CUSTOM_PRINTER_ICON: {
+                    PrinterId printerId = (PrinterId) message.obj;
+                    handleRequestCustomPrinterIcon(printerId);
+                } break;
             }
         }
     }
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java
new file mode 100644
index 0000000..403b65c
--- /dev/null
+++ b/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.notification;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.media.session.MediaSession;
+import android.os.Build;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.service.notification.StatusBarNotification;
+import android.telecom.TelecomManager;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NotificationComparatorTest {
+    @Mock Context mContext;
+    @Mock TelecomManager mTm;
+    @Mock RankingHandler handler;
+    @Mock PackageManager mPm;
+
+    private final String callPkg = "com.android.server.notification";
+    private final int callUid = 10;
+    private String smsPkg;
+    private final int smsUid = 11;
+    private final String pkg2 = "pkg2";
+    private final int uid2 = 1111111;
+
+    private NotificationRecord mRecordMinCall;
+    private NotificationRecord mRecordHighCall;
+    private NotificationRecord mRecordDefaultMedia;
+    private NotificationRecord mRecordEmail;
+    private NotificationRecord mRecordInlineReply;
+    private NotificationRecord mRecordSms;
+    private NotificationRecord mRecordStarredContact;
+    private NotificationRecord mRecordContact;
+    private NotificationRecord mRecordUrgent;
+    private NotificationRecord mRecordCheater;
+
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        int userId = UserHandle.myUserId();
+
+        when(mContext.getResources()).thenReturn(
+                InstrumentationRegistry.getTargetContext().getResources());
+        when(mContext.getContentResolver()).thenReturn(
+                InstrumentationRegistry.getTargetContext().getContentResolver());
+        when(mContext.getPackageManager()).thenReturn(mPm);
+        when(mContext.getSystemService(eq(Context.TELECOM_SERVICE))).thenReturn(mTm);
+        when(mTm.getDefaultDialerPackage()).thenReturn(callPkg);
+        final ApplicationInfo legacy = new ApplicationInfo();
+        legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
+        try {
+            when(mPm.getApplicationInfoAsUser(anyString(), anyInt(), anyInt())).thenReturn(legacy);
+            when(mContext.getApplicationInfo()).thenReturn(legacy);
+        } catch (PackageManager.NameNotFoundException e) {
+            // let's hope not
+        }
+
+        smsPkg = Settings.Secure.getString(mContext.getContentResolver(),
+                Settings.Secure.SMS_DEFAULT_APPLICATION);
+
+        Notification n1 = new Notification.Builder(mContext)
+                .setCategory(Notification.CATEGORY_CALL)
+                .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
+                .build();
+        mRecordMinCall = new NotificationRecord(mContext, new StatusBarNotification(callPkg,
+                callPkg, getDefaultChannel(), 1, "minCall", callUid, callUid, n1,
+                new UserHandle(userId), "", 2000));
+        mRecordMinCall.setUserImportance(NotificationManager.IMPORTANCE_MIN);
+
+        Notification n2 = new Notification.Builder(mContext)
+                .setCategory(Notification.CATEGORY_CALL)
+                .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
+                .build();
+        mRecordHighCall = new NotificationRecord(mContext, new StatusBarNotification(callPkg,
+                callPkg, getDefaultChannel(), 1, "highcall", callUid, callUid, n2,
+                new UserHandle(userId), "", 1999));
+        mRecordHighCall.setUserImportance(NotificationManager.IMPORTANCE_HIGH);
+
+        Notification n3 = new Notification.Builder(mContext)
+                .setStyle(new Notification.MediaStyle()
+                        .setMediaSession(new MediaSession.Token(null)))
+                .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
+                .build();
+        mRecordDefaultMedia = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
+                pkg2, getDefaultChannel(), 1, "media", uid2, uid2, n3, new UserHandle(userId),
+                "", 1499));
+        mRecordDefaultMedia.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT);
+
+        Notification n4 = new Notification.Builder(mContext)
+                .setStyle(new Notification.MessagingStyle("sender!")).build();
+        mRecordInlineReply = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
+                pkg2, getDefaultChannel(), 1, "inlinereply", uid2, uid2, n4, new UserHandle(userId),
+                "", 1599));
+        mRecordInlineReply.setUserImportance(NotificationManager.IMPORTANCE_HIGH);
+        mRecordInlineReply.setPackagePriority(Notification.PRIORITY_MAX);
+
+        Notification n5 = new Notification.Builder(mContext)
+                .setCategory(Notification.CATEGORY_MESSAGE).build();
+        mRecordSms = new NotificationRecord(mContext, new StatusBarNotification(smsPkg,
+                smsPkg, getDefaultChannel(), 1, "sms", smsUid, smsUid, n5, new UserHandle(userId),
+                "", 1299));
+        mRecordSms.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT);
+
+        Notification n6 = new Notification.Builder(mContext).build();
+        mRecordStarredContact = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
+                pkg2, getDefaultChannel(), 1, "starred", uid2, uid2, n6, new UserHandle(userId),
+                "", 1259));
+        mRecordStarredContact.setContactAffinity(ValidateNotificationPeople.STARRED_CONTACT);
+        mRecordStarredContact.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT);
+
+        Notification n7 = new Notification.Builder(mContext).build();
+        mRecordContact = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
+                pkg2, getDefaultChannel(), 1, "contact", uid2, uid2, n7, new UserHandle(userId),
+                "", 1259));
+        mRecordContact.setContactAffinity(ValidateNotificationPeople.VALID_CONTACT);
+        mRecordContact.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT);
+
+        Notification n8 = new Notification.Builder(mContext).build();
+        mRecordUrgent = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
+                pkg2, getDefaultChannel(), 1, "urgent", uid2, uid2, n8, new UserHandle(userId),
+                "", 1258));
+        mRecordUrgent.setUserImportance(NotificationManager.IMPORTANCE_HIGH);
+
+        Notification n9 = new Notification.Builder(mContext)
+                .setCategory(Notification.CATEGORY_MESSAGE)
+                .setFlag(Notification.FLAG_ONGOING_EVENT
+                        |Notification.FLAG_FOREGROUND_SERVICE, true)
+                .build();
+        mRecordCheater = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
+                pkg2, getDefaultChannel(), 1, "cheater", uid2, uid2, n9, new UserHandle(userId),
+                "", 9258));
+        mRecordCheater.setUserImportance(NotificationManager.IMPORTANCE_LOW);
+
+        Notification n10 = new Notification.Builder(mContext)
+                .setStyle(new Notification.InboxStyle().setSummaryText("message!")).build();
+        mRecordEmail = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
+                pkg2, getDefaultChannel(), 1, "email", uid2, uid2, n10, new UserHandle(userId),
+                "", 1599));
+        mRecordEmail.setUserImportance(NotificationManager.IMPORTANCE_HIGH);
+    }
+
+    @Test
+    public void testOrdering() throws Exception {
+        final List<NotificationRecord> expected = new ArrayList<>();
+        expected.add(mRecordHighCall);
+        expected.add(mRecordDefaultMedia);
+        expected.add(mRecordStarredContact);
+        expected.add(mRecordContact);
+        expected.add(mRecordInlineReply);
+        expected.add(mRecordSms);
+        expected.add(mRecordEmail);
+        expected.add(mRecordUrgent);
+        expected.add(mRecordCheater);
+        expected.add(mRecordMinCall);
+
+        List<NotificationRecord> actual = new ArrayList<>();
+        actual.addAll(expected);
+        Collections.shuffle(actual);
+
+        Collections.sort(actual, new NotificationComparator(mContext));
+
+        assertEquals(expected, actual);
+    }
+
+    @Test
+    public void testMessaging() throws Exception {
+        NotificationComparator comp = new NotificationComparator(mContext);
+        assertTrue(comp.isImportantMessaging(mRecordStarredContact));
+        assertTrue(comp.isImportantMessaging(mRecordContact));
+        assertTrue(comp.isImportantMessaging(mRecordInlineReply));
+        assertTrue(comp.isImportantMessaging(mRecordSms));
+        assertFalse(comp.isImportantMessaging(mRecordEmail));
+        assertFalse(comp.isImportantMessaging(mRecordCheater));
+    }
+
+    private NotificationChannel getDefaultChannel() {
+        return new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "name",
+                NotificationManager.IMPORTANCE_LOW);
+    }
+}
diff --git a/services/tests/servicestests/src/android/net/util/BlockingSocketReaderTest.java b/services/tests/servicestests/src/android/net/util/BlockingSocketReaderTest.java
new file mode 100644
index 0000000..e03350f
--- /dev/null
+++ b/services/tests/servicestests/src/android/net/util/BlockingSocketReaderTest.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2016 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.net.util;
+
+import static android.system.OsConstants.*;
+
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.StructTimeval;
+
+import libcore.io.IoBridge;
+
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketException;
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Tests for BlockingSocketReader.
+ *
+ * @hide
+ */
+public class BlockingSocketReaderTest extends TestCase {
+    static final InetAddress LOOPBACK6 = Inet6Address.getLoopbackAddress();
+    static final StructTimeval TIMEO = StructTimeval.fromMillis(500);
+
+    protected CountDownLatch mLatch;
+    protected FileDescriptor mLocalSocket;
+    protected InetSocketAddress mLocalSockName;
+    protected byte[] mLastRecvBuf;
+    protected boolean mExited;
+    protected BlockingSocketReader mReceiver;
+
+    @Override
+    public void setUp() {
+        resetLatch();
+        mLocalSocket = null;
+        mLocalSockName = null;
+        mLastRecvBuf = null;
+        mExited = false;
+
+        mReceiver = new BlockingSocketReader() {
+            @Override
+            protected FileDescriptor createSocket() {
+                FileDescriptor s = null;
+                try {
+                    s = Os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+                    Os.bind(s, LOOPBACK6, 0);
+                    mLocalSockName = (InetSocketAddress) Os.getsockname(s);
+                    Os.setsockoptTimeval(s, SOL_SOCKET, SO_SNDTIMEO, TIMEO);
+                } catch (ErrnoException|SocketException e) {
+                    closeSocket(s);
+                    fail();
+                    return null;
+                }
+
+                mLocalSocket = s;
+                return s;
+            }
+
+            @Override
+            protected void handlePacket(byte[] recvbuf, int length) {
+                mLastRecvBuf = Arrays.copyOf(recvbuf, length);
+                mLatch.countDown();
+            }
+
+            @Override
+            protected void onExit() {
+                mExited = true;
+                mLatch.countDown();
+            }
+        };
+    }
+
+    @Override
+    public void tearDown() {
+        if (mReceiver != null) mReceiver.stop();
+        mReceiver = null;
+    }
+
+    void resetLatch() { mLatch = new CountDownLatch(1); }
+
+    void waitForActivity() throws Exception {
+        assertTrue(mLatch.await(500, TimeUnit.MILLISECONDS));
+        resetLatch();
+    }
+
+    void sendPacket(byte[] contents) throws Exception {
+        final DatagramSocket sender = new DatagramSocket();
+        sender.connect(mLocalSockName);
+        sender.send(new DatagramPacket(contents, contents.length));
+        sender.close();
+    }
+
+    public void testBasicWorking() throws Exception {
+        assertTrue(mReceiver.start());
+        assertTrue(mLocalSockName != null);
+        assertEquals(LOOPBACK6, mLocalSockName.getAddress());
+        assertTrue(0 < mLocalSockName.getPort());
+        assertTrue(mLocalSocket != null);
+        assertFalse(mExited);
+
+        final byte[] one = "one 1".getBytes("UTF-8");
+        sendPacket(one);
+        waitForActivity();
+        assertEquals(1, mReceiver.numPacketsReceived());
+        assertTrue(Arrays.equals(one, mLastRecvBuf));
+        assertFalse(mExited);
+
+        final byte[] two = "two 2".getBytes("UTF-8");
+        sendPacket(two);
+        waitForActivity();
+        assertEquals(2, mReceiver.numPacketsReceived());
+        assertTrue(Arrays.equals(two, mLastRecvBuf));
+        assertFalse(mExited);
+
+        mReceiver.stop();
+        waitForActivity();
+        assertEquals(2, mReceiver.numPacketsReceived());
+        assertTrue(Arrays.equals(two, mLastRecvBuf));
+        assertTrue(mExited);
+    }
+}
diff --git a/services/tests/servicestests/src/android/net/util/ConnectivityPacketSummaryTest.java b/services/tests/servicestests/src/android/net/util/ConnectivityPacketSummaryTest.java
new file mode 100644
index 0000000..766e5c0
--- /dev/null
+++ b/services/tests/servicestests/src/android/net/util/ConnectivityPacketSummaryTest.java
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2016 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.net.util;
+
+import static android.net.util.NetworkConstants.*;
+
+import libcore.util.HexEncoding;
+
+import junit.framework.TestCase;
+
+
+/**
+ * Tests for ConnectivityPacketSummary.
+ *
+ * @hide
+ */
+public class ConnectivityPacketSummaryTest extends TestCase {
+    private static final byte[] MYHWADDR = {
+        asByte(0x80), asByte(0x7a), asByte(0xbf), asByte(0x6f), asByte(0x48), asByte(0xf3)
+    };
+
+    private String getSummary(String hexBytes) {
+        hexBytes = hexBytes.replaceAll("\\s+", "");
+        final byte[] bytes = HexEncoding.decode(hexBytes.toCharArray(), false);
+        return ConnectivityPacketSummary.summarize(MYHWADDR, bytes);
+    }
+
+    public void testParseICMPv6DADProbe() {
+        final String packet =
+                // Ethernet
+                "3333FF6F48F3 807ABF6F48F3 86DD" +
+                // IPv6
+                "600000000018 3A FF" +
+                "00000000000000000000000000000000" +
+                "FF0200000000000000000001FF6F48F3" +
+                // ICMPv6
+                "87 00 A8E7" +
+                "00000000" +
+                "FE80000000000000827ABFFFFE6F48F3";
+
+        final String expected =
+                "TX 80:7a:bf:6f:48:f3 > 33:33:ff:6f:48:f3 ipv6" +
+                " :: > ff02::1:ff6f:48f3 icmp6" +
+                " ns fe80::827a:bfff:fe6f:48f3";
+
+        assertEquals(expected, getSummary(packet));
+    }
+
+    public void testParseICMPv6RS() {
+        final String packet =
+                // Ethernet
+                "333300000002 807ABF6F48F3 86DD" +
+                // IPv6
+                "600000000010 3A FF" +
+                "FE80000000000000827ABFFFFE6F48F3" +
+                "FF020000000000000000000000000002" +
+                // ICMPv6 RS
+                "85 00 6973" +
+                "00000000" +
+                "01 01 807ABF6F48F3";
+
+        final String expected =
+                "TX 80:7a:bf:6f:48:f3 > 33:33:00:00:00:02 ipv6" +
+                " fe80::827a:bfff:fe6f:48f3 > ff02::2 icmp6" +
+                " rs slla 80:7a:bf:6f:48:f3";
+
+        assertEquals(expected, getSummary(packet));
+    }
+
+    public void testParseICMPv6RA() {
+        final String packet =
+                // Ethernet
+                "807ABF6F48F3 100E7E263FC1 86DD" +
+                // IPv6
+                "600000000068 3A FF" +
+                "FE80000000000000FA000004FD000001" +
+                "FE80000000000000827ABFFFFE6F48F3" +
+                // ICMPv6 RA
+                "86 00 8141" +
+                "40 00 0E10" +
+                "00000000" +
+                "00000000" +
+                "01 01 00005E000265" +
+                "05 01 0000000005DC" +
+                "19 05 000000000E10" +
+                "      20014860486000000000000000008844" +
+                "      20014860486000000000000000008888" +
+                "03 04 40 C0" +
+                "      00278D00" +
+                "      00093A80" +
+                "      00000000" +
+                "      2401FA000004FD000000000000000000";
+
+        final String expected =
+                "RX 10:0e:7e:26:3f:c1 > 80:7a:bf:6f:48:f3 ipv6" +
+                " fe80::fa00:4:fd00:1 > fe80::827a:bfff:fe6f:48f3 icmp6" +
+                " ra slla 00:00:5e:00:02:65 mtu 1500";
+
+        assertEquals(expected, getSummary(packet));
+    }
+
+    public void testParseICMPv6NS() {
+        final String packet =
+                // Ethernet
+                  "807ABF6F48F3 100E7E263FC1 86DD" +
+                  // IPv6
+                  "6C0000000020 3A FF" +
+                  "FE80000000000000FA000004FD000001" +
+                  "FF0200000000000000000001FF01C146" +
+                  // ICMPv6 NS
+                  "87 00 8AD4" +
+                  "00000000" +
+                  "2401FA000004FD0015EA6A5C7B01C146" +
+                  "01 01 00005E000265";
+
+        final String expected =
+                "RX 10:0e:7e:26:3f:c1 > 80:7a:bf:6f:48:f3 ipv6" +
+                " fe80::fa00:4:fd00:1 > ff02::1:ff01:c146 icmp6" +
+                " ns 2401:fa00:4:fd00:15ea:6a5c:7b01:c146 slla 00:00:5e:00:02:65";
+
+        assertEquals(expected, getSummary(packet));
+    }
+
+    public void testParseICMPv6NA() {
+        final String packet =
+                // Ethernet
+                "00005E000265 807ABF6F48F3 86DD" +
+                "600000000020 3A FF" +
+                "2401FA000004FD0015EA6A5C7B01C146" +
+                "FE80000000000000FA000004FD000001" +
+                "88 00 E8126" +
+                "0000000" +
+                "2401FA000004FD0015EA6A5C7B01C146" +
+                "02 01 807ABF6F48F3";
+
+        final String expected =
+                "TX 80:7a:bf:6f:48:f3 > 00:00:5e:00:02:65 ipv6" +
+                " 2401:fa00:4:fd00:15ea:6a5c:7b01:c146 > fe80::fa00:4:fd00:1 icmp6" +
+                " na 2401:fa00:4:fd00:15ea:6a5c:7b01:c146 tlla 80:7a:bf:6f:48:f3";
+
+        assertEquals(expected, getSummary(packet));
+    }
+
+    public void testParseARPRequest() {
+        final String packet =
+                // Ethernet
+                  "FFFFFFFFFFFF 807ABF6F48F3 0806" +
+                  // ARP
+                  "0001 0800 06 04" +
+                  // Request
+                  "0001" +
+                  "807ABF6F48F3 64706ADB" +
+                  "000000000000 64706FFD";
+
+        final String expected =
+                "TX 80:7a:bf:6f:48:f3 > ff:ff:ff:ff:ff:ff arp" +
+                " who-has 100.112.111.253";
+
+        assertEquals(expected, getSummary(packet));
+    }
+
+    public void testParseARPReply() {
+        final String packet =
+                // Ethernet
+                  "807ABF6F48F3 288A1CA8DFC1 0806" +
+                  // ARP
+                  "0001 0800 06 04" +
+                  // Reply
+                  "0002" +
+                  "288A1CA8DFC1 64706FFD"+
+                  "807ABF6F48F3 64706ADB" +
+                  // Ethernet padding to packet min size.
+                  "0000000000000000000000000000";
+
+        final String expected =
+                "RX 28:8a:1c:a8:df:c1 > 80:7a:bf:6f:48:f3 arp" +
+                " reply 100.112.111.253 28:8a:1c:a8:df:c1";
+
+        assertEquals(expected, getSummary(packet));
+    }
+
+    public void testParseDHCPv4Discover() {
+        final String packet =
+                // Ethernet
+                "FFFFFFFFFFFF 807ABF6F48F3 0800" +
+                // IPv4
+                "451001580000400040113986" +
+                "00000000" +
+                "FFFFFFFF" +
+                // UDP
+                "0044 0043" +
+                "0144 5559" +
+                // DHCPv4
+                "01 01 06 00" +
+                "79F7ACA4" +
+                "0000 0000" +
+                "00000000" +
+                "00000000" +
+                "00000000" +
+                "00000000" +
+                "807ABF6F48F300000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "63 82 53 63" +
+                "35 01 01" +
+                "3D 07 01807ABF6F48F3" +
+                "39 02 05DC" +
+                "3C 12 616E64726F69642D646863702D372E312E32" +
+                "0C 18 616E64726F69642D36623030366333313333393835343139" +
+                "37 0A 01 03 06 0F 1A 1C 33 3A 3B 2B" +
+                "FF" +
+                "00";
+
+        final String expectedPrefix =
+                "TX 80:7a:bf:6f:48:f3 > ff:ff:ff:ff:ff:ff ipv4" +
+                " 0.0.0.0 > 255.255.255.255 udp" +
+                " 68 > 67 dhcp4" +
+                " 80:7a:bf:6f:48:f3 DISCOVER";
+
+        assertTrue(getSummary(packet).startsWith(expectedPrefix));
+    }
+
+    public void testParseDHCPv4Offer() {
+        final String packet =
+                // Ethernet
+                "807ABF6F48F3 288A1CA8DFC1 0800" +
+                // IPv4
+                "4500013D4D2C0000401188CB" +
+                "64706FFD" +
+                "64706ADB" +
+                // UDP
+                "0043 0044" +
+                "0129 371D" +
+                // DHCPv4
+                "02 01 06 01" +
+                "79F7ACA4" +
+                "0000 0000" +
+                "00000000" +
+                "64706ADB" +
+                "00000000" +
+                "00000000" +
+                "807ABF6F48F300000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "63 82 53 63" +
+                "35 01 02" +
+                "36 04 AC188A0B" +
+                "33 04 00000708" +
+                "01 04 FFFFF000" +
+                "03 04 64706FFE" +
+                "06 08 08080808" +
+                "      08080404" +
+                "FF0001076165313A363636FF";
+
+        final String expectedPrefix =
+                "RX 28:8a:1c:a8:df:c1 > 80:7a:bf:6f:48:f3 ipv4" +
+                " 100.112.111.253 > 100.112.106.219 udp" +
+                " 67 > 68 dhcp4" +
+                " 80:7a:bf:6f:48:f3 OFFER";
+
+        assertTrue(getSummary(packet).startsWith(expectedPrefix));
+    }
+
+    public void testParseDHCPv4Request() {
+        final String packet =
+                // Ethernet
+                "FFFFFFFFFFFF 807ABF6F48F3 0800" +
+                // IPv4
+                "45100164000040004011397A" +
+                "00000000" +
+                "FFFFFFFF" +
+                // UDP
+                "0044 0043" +
+                "0150 E5C7" +
+                // DHCPv4
+                "01 01 06 00" +
+                "79F7ACA4" +
+                "0001 0000" +
+                "00000000" +
+                "00000000" +
+                "00000000" +
+                "00000000" +
+                "807ABF6F48F300000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "63 82 53 63" +
+                "35 01 03" +
+                "3D 07 01807ABF6F48F3" +
+                "32 04 64706ADB" +
+                "36 04 AC188A0B" +
+                "39 02 05DC" +
+                "3C 12 616E64726F69642D646863702D372E312E32" +
+                "0C 18 616E64726F69642D36623030366333313333393835343139" +
+                "37 0A 01 03 06 0F 1A 1C 33 3A 3B 2B" +
+                "FF" +
+                "00";
+
+        final String expectedPrefix =
+                "TX 80:7a:bf:6f:48:f3 > ff:ff:ff:ff:ff:ff ipv4" +
+                " 0.0.0.0 > 255.255.255.255 udp" +
+                " 68 > 67 dhcp4" +
+                " 80:7a:bf:6f:48:f3 REQUEST";
+
+        assertTrue(getSummary(packet).startsWith(expectedPrefix));
+    }
+
+    public void testParseDHCPv4Ack() {
+        final String packet =
+                // Ethernet
+                "807ABF6F48F3 288A1CA8DFC1 0800" +
+                // IPv4
+                "4500013D4D3B0000401188BC" +
+                "64706FFD" +
+                "64706ADB" +
+                // UDP
+                "0043 0044" +
+                "0129 341C" +
+                // DHCPv4
+                "02 01 06 01" +
+                "79F7ACA4" +
+                "0001 0000" +
+                "00000000" +
+                "64706ADB" +
+                "00000000" +
+                "00000000" +
+                "807ABF6F48F300000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "0000000000000000000000000000000000000000000000000000000000000000" +
+                "63 82 53 63" +
+                "35 01 05" +
+                "36 04 AC188A0B" +
+                "33 04 00000708" +
+                "01 04 FFFFF000" +
+                "03 04 64706FFE" +
+                "06 08 08080808" +
+                "      08080404" +
+                "FF0001076165313A363636FF";
+
+        final String expectedPrefix =
+                "RX 28:8a:1c:a8:df:c1 > 80:7a:bf:6f:48:f3 ipv4" +
+                " 100.112.111.253 > 100.112.106.219 udp" +
+                " 67 > 68 dhcp4" +
+                " 80:7a:bf:6f:48:f3 ACK";
+
+        assertTrue(getSummary(packet).startsWith(expectedPrefix));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 0c00886..48c9853 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -49,6 +49,7 @@
 import com.android.internal.R;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
+import com.android.server.pm.UserRestrictionsUtils;
 
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
@@ -1190,6 +1191,22 @@
         assertTrue(dpm.setDeviceOwner(admin1, "owner-name",
                 UserHandle.USER_SYSTEM));
 
+        // Check that the user restrictions that are enabled by default are set. Then unset them.
+        String[] defaultRestrictions = UserRestrictionsUtils
+                        .getDefaultEnabledForDeviceOwner().toArray(new String[0]);
+        DpmTestUtils.assertRestrictions(
+                DpmTestUtils.newRestrictions(defaultRestrictions),
+                dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
+        );
+        DpmTestUtils.assertRestrictions(
+                DpmTestUtils.newRestrictions(defaultRestrictions),
+                dpm.getUserRestrictions(admin1)
+        );
+
+        for (String restriction : defaultRestrictions) {
+            dpm.clearUserRestriction(admin1, restriction);
+        }
+
         DpmTestUtils.assertRestrictions(
                 DpmTestUtils.newRestrictions(),
                 dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
@@ -2188,7 +2205,7 @@
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
                 DevicePolicyManager.CODE_OK);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
-                DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
+                DevicePolicyManager.CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER);
         assertCheckProvisioningPreCondition(
                 DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
                 DevicePolicyManager.CODE_OK);
@@ -2226,7 +2243,7 @@
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE,
                 DevicePolicyManager.CODE_OK);
         assertCheckProvisioningPreCondition(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE,
-                DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE);
+                DevicePolicyManager.CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER);
         assertCheckProvisioningPreCondition(
                 DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE,
                 DevicePolicyManager.CODE_OK);
@@ -2368,7 +2385,9 @@
         when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
                 .thenReturn(true);
         when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(true);
-        when(mContext.userManager.hasUserRestriction(UserManager.DISALLOW_REMOVE_USER))
+        when(mContext.userManager.hasUserRestriction(
+                UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
+                UserHandle.of(DpmMockContext.CALLER_USER_HANDLE)))
                 .thenReturn(true);
         when(mContext.userManager.canAddMoreManagedProfiles(DpmMockContext.CALLER_USER_HANDLE,
                 false /* we can't remove a managed profile */)).thenReturn(false);
@@ -2664,8 +2683,26 @@
         final int ANOTHER_USER_ID = 36;
         mContext.addUser(ANOTHER_USER_ID, 0);
 
+        // Since the managed profile is not affiliated, they should not be allowed to talk to each
+        // other.
+        targetUsers = dpm.getBindDeviceAdminTargetUsers(admin1);
+        MoreAsserts.assertEmpty(targetUsers);
+
+        mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
+        targetUsers = dpm.getBindDeviceAdminTargetUsers(admin1);
+        MoreAsserts.assertEmpty(targetUsers);
+
+        // Setting affiliation ids
+        final List<String> userAffiliationIds = Arrays.asList("some.affiliation-id");
+        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+        dpm.setAffiliationIds(admin1, userAffiliationIds);
+
+        mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
+        dpm.setAffiliationIds(admin1, userAffiliationIds);
+
         // Calling from device owner admin, the result list should just contain the managed
         // profile user id.
+        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         targetUsers = dpm.getBindDeviceAdminTargetUsers(admin1);
         MoreAsserts.assertContentsInAnyOrder(targetUsers, UserHandle.of(MANAGED_PROFILE_USER_ID));
 
@@ -2674,6 +2711,18 @@
         mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
         targetUsers = dpm.getBindDeviceAdminTargetUsers(admin1);
         MoreAsserts.assertContentsInAnyOrder(targetUsers, UserHandle.SYSTEM);
+
+        // Changing affiliation ids in one
+        dpm.setAffiliationIds(admin1, Arrays.asList("some-different-affiliation-id"));
+
+        // Since the managed profile is not affiliated any more, they should not be allowed to talk
+        // to each other.
+        targetUsers = dpm.getBindDeviceAdminTargetUsers(admin1);
+        MoreAsserts.assertEmpty(targetUsers);
+
+        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+        targetUsers = dpm.getBindDeviceAdminTargetUsers(admin1);
+        MoreAsserts.assertEmpty(targetUsers);
     }
 
     public void testGetBindDeviceAdminTargetUsers_differentPackage() throws Exception {
@@ -2688,8 +2737,16 @@
                 new ComponentName("another.package", "whatever.class");
         addManagedProfile(adminDifferentPackage, MANAGED_PROFILE_ADMIN_UID, admin2);
 
+        // Setting affiliation ids
+        final List<String> userAffiliationIds = Arrays.asList("some-affiliation-id");
+        dpm.setAffiliationIds(admin1, userAffiliationIds);
+
+        mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
+        dpm.setAffiliationIds(adminDifferentPackage, userAffiliationIds);
+
         // Calling from device owner admin, we should get zero bind device admin target users as
         // their packages are different.
+        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         List<UserHandle> targetUsers = dpm.getBindDeviceAdminTargetUsers(admin1);
         MoreAsserts.assertEmpty(targetUsers);
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 99af9e8..6bc4c19 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -46,6 +46,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.IntentSender;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ILauncherApps;
@@ -70,10 +71,12 @@
 import android.os.Looper;
 import android.os.PersistableBundle;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.test.InstrumentationTestCase;
 import android.test.mock.MockContext;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Pair;
 
@@ -123,6 +126,7 @@
     protected static final String[] EMPTY_STRINGS = new String[0]; // Just for readability.
 
     protected static final String MAIN_ACTIVITY_CLASS = "MainActivity";
+    protected static final String PIN_CONFIRM_ACTIVITY_CLASS = "PinConfirmActivity";
 
     // public for mockito
     public class BaseContext extends MockContext {
@@ -161,6 +165,11 @@
         public void unregisterReceiver(BroadcastReceiver receiver) {
             // ignore.
         }
+
+        @Override
+        public void startActivityAsUser(Intent intent, UserHandle user) {
+            // ignore, use spy to intercept it.
+        }
     }
 
     /** Context used in the client side */
@@ -201,6 +210,10 @@
         public XmlResourceParser injectXmlMetaData(ActivityInfo activityInfo, String key) {
             return BaseShortcutManagerTest.this.injectXmlMetaData(activityInfo, key);
         }
+
+        public void sendIntentSender(IntentSender intent) {
+            // Placeholder for spying.
+        }
     }
 
     /** ShortcutService with injection override methods. */
@@ -304,6 +317,15 @@
         }
 
         @Override
+        ComponentName getDefaultLauncher(@UserIdInt int userId) {
+            final ComponentName activity = mDefaultLauncher.get(userId);
+            if (activity != null) {
+                return activity;
+            }
+            return super.getDefaultLauncher(userId);
+        }
+
+        @Override
         PackageInfo injectPackageInfoWithUninstalled(String packageName, @UserIdInt int userId,
                 boolean getSignatures) {
             return getInjectedPackageInfo(packageName, userId, getSignatures);
@@ -375,6 +397,12 @@
         }
 
         @Override
+        ComponentName injectGetPinConfirmationActivity(@NonNull String launcherPackageName,
+                int launcherUserId) {
+            return mPinConfirmActivityFetcher.apply(launcherPackageName, launcherUserId);
+        }
+
+        @Override
         boolean injectIsActivityEnabledAndExported(ComponentName activity, @UserIdInt int userId) {
             assertNotNull(activity);
             return mEnabledActivityChecker.test(activity, userId);
@@ -413,6 +441,11 @@
         }
 
         @Override
+        void injectSendIntentSender(IntentSender intent) {
+            mContext.sendIntentSender(intent);
+        }
+
+        @Override
         void wtf(String message, Throwable th) {
             // During tests, WTF is fatal.
             fail(message + "  exception: " + th + "\n" + Log.getStackTraceString(th));
@@ -583,7 +616,7 @@
 
     protected static final UserInfo USER_INFO_0 = withProfileGroupId(
             new UserInfo(USER_0, "user0",
-                    UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY | UserInfo.FLAG_INITIALIZED), 10);
+                    UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY | UserInfo.FLAG_INITIALIZED), 0);
 
     protected static final UserInfo USER_INFO_10 =
             new UserInfo(USER_10, "user10", UserInfo.FLAG_INITIALIZED);
@@ -593,19 +626,24 @@
 
     protected static final UserInfo USER_INFO_P0 = withProfileGroupId(
             new UserInfo(USER_P0, "userP0",
-                    UserInfo.FLAG_MANAGED_PROFILE), 10);
+                    UserInfo.FLAG_MANAGED_PROFILE), 0);
 
     protected BiPredicate<String, Integer> mDefaultLauncherChecker =
             (callingPackage, userId) ->
             LAUNCHER_1.equals(callingPackage) || LAUNCHER_2.equals(callingPackage)
             || LAUNCHER_3.equals(callingPackage) || LAUNCHER_4.equals(callingPackage);
 
+    private final Map<Integer, ComponentName> mDefaultLauncher = new ArrayMap<>();
+
     protected BiPredicate<ComponentName, Integer> mMainActivityChecker =
             (activity, userId) -> true;
 
     protected BiFunction<String, Integer, ComponentName> mMainActivityFetcher =
             (packageName, userId) -> new ComponentName(packageName, MAIN_ACTIVITY_CLASS);
 
+    protected BiFunction<String, Integer, ComponentName> mPinConfirmActivityFetcher =
+            (packageName, userId) -> new ComponentName(packageName, PIN_CONFIRM_ACTIVITY_CLASS);
+
     protected BiPredicate<ComponentName, Integer> mEnabledActivityChecker
             = (activity, userId) -> true; // all activities are enabled.
 
@@ -722,6 +760,19 @@
                     return b(mRunningUsers.get(userId)) && b(mUnlockedUsers.get(userId));
                 }));
 
+        when(mMockUserManager.getProfileParent(anyInt()))
+                .thenAnswer(new AnswerWithSystemCheck<>(inv -> {
+                    final int userId = (Integer) inv.getArguments()[0];
+                    final UserInfo ui = mUserInfos.get(userId);
+                    assertNotNull(ui);
+                    if (ui.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID) {
+                        return null;
+                    }
+                    final UserInfo parent = mUserInfos.get(ui.profileGroupId);
+                    assertNotNull(parent);
+                    return parent;
+                }));
+
         when(mMockActivityManagerInternal.getUidProcessState(anyInt())).thenReturn(
                 ActivityManager.PROCESS_STATE_CACHED_EMPTY);
 
@@ -1098,10 +1149,31 @@
         return mInjectedClientPackage;
     }
 
+    /**
+     * This controls {@link ShortcutService#hasShortcutHostPermission(String, int)}, but
+     * not {@link ShortcutService#getDefaultLauncher(int)}.  To control the later, use
+     * {@link #setDefaultLauncher(int, ComponentName)}.
+     */
     protected void setDefaultLauncherChecker(BiPredicate<String, Integer> p) {
         mDefaultLauncherChecker = p;
     }
 
+    /**
+     * Set the default launcher.  This will update {@link #mDefaultLauncherChecker} set by
+     * {@link #setDefaultLauncherChecker} too.
+     */
+    protected void setDefaultLauncher(int userId, ComponentName launcherActivity) {
+        mDefaultLauncher.put(userId, launcherActivity);
+
+        final BiPredicate<String, Integer> oldChecker = mDefaultLauncherChecker;
+        mDefaultLauncherChecker = (checkPackageName, checkUserId) -> {
+            if ((checkUserId == userId) && (launcherActivity !=  null)) {
+                return launcherActivity.getPackageName().equals(checkPackageName);
+            }
+            return oldChecker.test(checkPackageName, checkUserId);
+        };
+    }
+
     protected void runWithCaller(String packageName, int userId, Runnable r) {
         final String previousPackage = mInjectedClientPackage;
         final int previousUserId = UserHandle.getUserId(mInjectedCallingUid);
@@ -1218,6 +1290,13 @@
     }
 
     /**
+     * Make a shortcut with an ID only.
+     */
+    protected ShortcutInfo makeShortcutIdOnly(String id) {
+        return new ShortcutInfo.Builder(mClientContext, id).build();
+    }
+
+    /**
      * Make a shortcut with an ID.
      */
     protected ShortcutInfo makeShortcut(String id) {
@@ -1226,12 +1305,19 @@
                 makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
     }
 
+    @Deprecated // Title was renamed to short label.
     protected ShortcutInfo makeShortcutWithTitle(String id, String title) {
         return makeShortcut(
                 id, title, /* activity =*/ null, /* icon =*/ null,
                 makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
     }
 
+    protected ShortcutInfo makeShortcutWithShortLabel(String id, String shortLabel) {
+        return makeShortcut(
+                id, shortLabel, /* activity =*/ null, /* icon =*/ null,
+                makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
+    }
+
     /**
      * Make a shortcut with an ID and timestamp.
      */
@@ -1624,6 +1710,13 @@
         return getLauncherShortcuts(launcher, userId, ShortcutQuery.FLAG_GET_PINNED);
     }
 
+    protected List<ShortcutInfo> getShortcutAsLauncher(int targetUserId) {
+        final ShortcutQuery q = new ShortcutQuery();
+        q.setQueryFlags(ShortcutQuery.FLAG_MATCH_DYNAMIC | ShortcutQuery.FLAG_MATCH_DYNAMIC
+                | ShortcutQuery.FLAG_MATCH_PINNED);
+        return mLauncherApps.getShortcuts(q, UserHandle.of(targetUserId));
+    }
+
     protected ShortcutInfo getShortcutInfoAsLauncher(String packageName, String shortcutId,
             int userId) {
         final List<ShortcutInfo> infoList =
@@ -1897,7 +1990,8 @@
     public static List<ShortcutInfo> assertAllHaveIcon(
             List<ShortcutInfo> actualShortcuts) {
         for (ShortcutInfo s : actualShortcuts) {
-            assertTrue("ID " + s.getId() + " has no icon ", s.hasIconFile() || s.hasIconResource());
+            assertTrue("ID " + s.getId() + " has no icon ",
+                    s.hasIconFile() || s.hasIconResource() || s.getIcon() != null);
         }
         return actualShortcuts;
     }
@@ -1959,4 +2053,31 @@
         return ri(PACKAGE_FALLBACK_LAUNCHER, PACKAGE_FALLBACK_LAUNCHER_NAME, true,
                 PACKAGE_FALLBACK_LAUNCHER_PRIORITY);
     }
+
+    protected void makeCallerForeground() {
+        try {
+            mService.mUidObserver.onUidStateChanged(
+                    mInjectedCallingUid, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+    }
+
+    protected void makeCallerBackground() {
+        try {
+            mService.mUidObserver.onUidStateChanged(
+                    mInjectedCallingUid, ActivityManager.PROCESS_STATE_TOP_SLEEPING);
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+    }
+
+    protected void publishManifestShortcutsAsCaller(int resId) {
+        addManifestShortcutResource(
+                new ComponentName(getCallingPackage(), ShortcutActivity.class.getName()),
+                resId);
+        updatePackageVersion(getCallingPackage(), 1);
+        mService.mPackageMonitor.onReceive(getTestContext(),
+                genPackageAddIntent(getCallingPackage(), getCallingUserId()));
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest6.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest6.java
index ba4dbc1..3684ca0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest6.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest6.java
@@ -31,7 +31,8 @@
 import java.util.List;
 
 /**
- * Tests for {@link ShortcutService#hasShortcutHostPermissionInner}.
+ * Tests for {@link ShortcutService#hasShortcutHostPermissionInner}, which includes
+ * {@link ShortcutService#getDefaultLauncher}.
  */
 @SmallTest
 public class ShortcutManagerTest6 extends BaseShortcutManagerTest {
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
new file mode 100644
index 0000000..fbf0ed2
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
@@ -0,0 +1,1228 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm;
+
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertExpectException;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertForLauncherCallbackNoThrow;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.assertWith;
+import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.list;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.LauncherApps;
+import android.content.pm.LauncherApps.PinItemRequest;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
+import android.graphics.drawable.Icon;
+import android.os.UserHandle;
+import android.test.MoreAsserts;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Pair;
+
+import com.android.frameworks.servicestests.R;
+
+import org.mockito.ArgumentCaptor;
+
+/**
+ * Tests for {@link ShortcutManager#requestPinShortcut} and relevant APIs.
+ *
+ m FrameworksServicesTests &&
+ adb install \
+ -r -g ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
+ adb shell am instrument -e class com.android.server.pm.ShortcutManagerTest8 \
+ -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
+
+ * TODO for CTS
+ * - Foreground check.
+ * - Reading icons from requested shortcuts.
+ * - Invalid pre-approved token.
+ */
+@SmallTest
+public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
+    private ShortcutRequestPinProcessor mProcessor;
+
+    @Override
+    protected void initService() {
+        super.initService();
+        mProcessor = mService.getShortcutRequestPinProcessorForTest();
+    }
+
+    @Override
+    protected void setCaller(String packageName, int userId) {
+        super.setCaller(packageName, userId);
+
+        // Note during this test, assume all callers are in the foreground by default.
+        makeCallerForeground();
+    }
+
+    public void testGetParentOrSelfUserId() {
+        assertEquals(USER_0, mService.getParentOrSelfUserId(USER_0));
+        assertEquals(USER_10, mService.getParentOrSelfUserId(USER_10));
+        assertEquals(USER_11, mService.getParentOrSelfUserId(USER_11));
+        assertEquals(USER_0, mService.getParentOrSelfUserId(USER_P0));
+    }
+
+    public void testIsRequestPinShortcutSupported() {
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+        setDefaultLauncher(USER_10, mMainActivityFetcher.apply(LAUNCHER_2, USER_10));
+
+        Pair<ComponentName, Integer> actual;
+        // User 0
+        actual = mProcessor.getRequestPinShortcutConfirmationActivity(USER_0);
+
+        assertEquals(LAUNCHER_1, actual.first.getPackageName());
+        assertEquals(PIN_CONFIRM_ACTIVITY_CLASS, actual.first.getClassName());
+        assertEquals(USER_0, (int) actual.second);
+
+        // User 10
+        actual = mProcessor.getRequestPinShortcutConfirmationActivity(USER_10);
+
+        assertEquals(LAUNCHER_2, actual.first.getPackageName());
+        assertEquals(PIN_CONFIRM_ACTIVITY_CLASS, actual.first.getClassName());
+        assertEquals(USER_10, (int) actual.second);
+
+        // User P0 -> managed profile, return user-0's launcher.
+        actual = mProcessor.getRequestPinShortcutConfirmationActivity(USER_P0);
+
+        assertEquals(LAUNCHER_1, actual.first.getPackageName());
+        assertEquals(PIN_CONFIRM_ACTIVITY_CLASS, actual.first.getClassName());
+        assertEquals(USER_0, (int) actual.second);
+
+        // Check from the public API.
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            assertTrue(mManager.isRequestPinShortcutSupported());
+        });
+        runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+            assertTrue(mManager.isRequestPinShortcutSupported());
+        });
+        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+            assertTrue(mManager.isRequestPinShortcutSupported());
+        });
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertTrue(mManager.isRequestPinShortcutSupported());
+        });
+
+        // Now, USER_0's launcher no longer has a confirm activity.
+        mPinConfirmActivityFetcher = (packageName, userId) ->
+                !LAUNCHER_2.equals(packageName)
+                        ? null : new ComponentName(packageName, PIN_CONFIRM_ACTIVITY_CLASS);
+
+        // User 10 -- still has confirm activity.
+        actual = mProcessor.getRequestPinShortcutConfirmationActivity(USER_10);
+
+        assertEquals(LAUNCHER_2, actual.first.getPackageName());
+        assertEquals(PIN_CONFIRM_ACTIVITY_CLASS, actual.first.getClassName());
+        assertEquals(USER_10, (int) actual.second);
+
+        // But user-0 and user p0 no longer has a confirmation activity.
+        assertNull(mProcessor.getRequestPinShortcutConfirmationActivity(USER_0));
+        assertNull(mProcessor.getRequestPinShortcutConfirmationActivity(USER_P0));
+
+        // Check from the public API.
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            assertFalse(mManager.isRequestPinShortcutSupported());
+        });
+        runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+            assertFalse(mManager.isRequestPinShortcutSupported());
+        });
+        runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+            assertTrue(mManager.isRequestPinShortcutSupported());
+        });
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertFalse(mManager.isRequestPinShortcutSupported());
+        });
+    }
+
+    public void testRequestPinShortcut_notSupported() {
+        // User-0's launcher has no confirmation activity.
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+
+        mPinConfirmActivityFetcher = (packageName, userId) ->
+                !LAUNCHER_2.equals(packageName)
+                        ? null : new ComponentName(packageName, PIN_CONFIRM_ACTIVITY_CLASS);
+
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            ShortcutInfo s1 = makeShortcut("s1");
+
+            assertFalse(mManager.requestPinShortcut(s1,
+                    /*PendingIntent=*/ null));
+
+            verify(mServiceContext, times(0))
+                    .startActivityAsUser(any(Intent.class), any(UserHandle.class));
+            verify(mServiceContext, times(0))
+                    .sendIntentSender(any(IntentSender.class));
+        });
+
+        runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+            ShortcutInfo s1 = makeShortcut("s1");
+
+            assertFalse(mManager.requestPinShortcut(s1,
+                    /*PendingIntent=*/ null));
+
+            verify(mServiceContext, times(0))
+                    .startActivityAsUser(any(Intent.class), any(UserHandle.class));
+            verify(mServiceContext, times(0))
+                    .sendIntentSender(any(IntentSender.class));
+        });
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            ShortcutInfo s1 = makeShortcut("s1");
+
+            assertFalse(mManager.requestPinShortcut(s1,
+                    /*PendingIntent=*/ null));
+
+            verify(mServiceContext, times(0))
+                    .startActivityAsUser(any(Intent.class), any(UserHandle.class));
+            verify(mServiceContext, times(0))
+                    .sendIntentSender(any(IntentSender.class));
+        });
+    }
+
+    private void assertPinItemRequestIntent(Intent actualIntent, String expectedPackage) {
+        assertEquals(LauncherApps.ACTION_CONFIRM_PIN_ITEM, actualIntent.getAction());
+        assertEquals(expectedPackage, actualIntent.getComponent().getPackageName());
+        assertEquals(PIN_CONFIRM_ACTIVITY_CLASS,
+                actualIntent.getComponent().getClassName());
+        assertEquals(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK,
+                actualIntent.getFlags());
+    }
+
+    public void testNotForeground() {
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            makeCallerBackground();
+
+            assertExpectException(IllegalStateException.class, "foreground activity", () -> {
+                assertTrue(mManager.requestPinShortcut(makeShortcut("s1"),
+                        /* resultIntent= */ null));
+            });
+
+            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
+            verify(mServiceContext, times(0)).startActivityAsUser(
+                    any(Intent.class), any(UserHandle.class));
+        });
+    }
+
+    private void assertPinItemRequest(PinItemRequest actualRequest) {
+        assertNotNull(actualRequest);
+
+        assertEquals(PinItemRequest.REQUEST_TYPE_SHORTCUT, actualRequest.getRequestType());
+    }
+
+    /**
+     * Basic flow:
+     * - Launcher supports the feature.
+     * - Shortcut doesn't pre-exist.
+     */
+    private void checkRequestPinShortcut(@Nullable PendingIntent resultIntent) {
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+        setDefaultLauncher(USER_10, mMainActivityFetcher.apply(LAUNCHER_2, USER_10));
+
+        final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            ShortcutInfo s1 = makeShortcutWithIcon("s1", res32x32);
+
+            assertTrue(mManager.requestPinShortcut(s1,
+                    resultIntent == null ? null : resultIntent.getIntentSender()));
+
+            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
+
+            // Shortcut shouldn't be registered yet.
+            assertWith(getCallerShortcuts())
+                    .isEmpty();
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            // Check the intent passed to startActivityAsUser().
+            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
+
+            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
+
+            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
+
+            // Check the request object.
+            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
+
+            assertPinItemRequest(request);
+
+            assertWith(request.getShortcutInfo())
+                    .haveIds("s1")
+                    .areAllOrphan()
+                    .areAllWithNoIntent();
+
+            assertAllHaveIcon(list(request.getShortcutInfo()));
+
+            // Accept the request.
+            assertForLauncherCallbackNoThrow(mLauncherApps,
+                    () -> assertTrue(request.accept()))
+                    .assertCallbackCalledForPackageAndUser(CALLING_PACKAGE_1, HANDLE_USER_P0)
+                    .haveIds("s1");
+        });
+
+        // This method is always called, even with PI == null.
+        if (resultIntent == null) {
+            verify(mServiceContext, times(1)).sendIntentSender(eq(null));
+        } else {
+            verify(mServiceContext, times(1)).sendIntentSender(any(IntentSender.class));
+        }
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertWith(getCallerShortcuts())
+                    .haveIds("s1")
+                    .areAllNotDynamic()
+                    .areAllEnabled()
+                    .areAllPinned()
+                    .areAllWithIntent();
+        });
+    }
+
+    public void testRequestPinShortcut() {
+        checkRequestPinShortcut(/* resultIntent=*/ null);
+    }
+
+    public void testRequestPinShortcut_withCallback() {
+        final PendingIntent resultIntent =
+                PendingIntent.getActivity(getTestContext(), 0, new Intent(), 0);
+
+        checkRequestPinShortcut(resultIntent);
+    }
+
+    public void testRequestPinShortcut_dynamicExists() {
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+
+        final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            // Create dynamic shortcut
+            ShortcutInfo s1 = makeShortcutWithIcon("s1", res32x32);
+            assertTrue(mManager.setDynamicShortcuts(list(s1)));
+
+            assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("s1"),
+                    /* resultIntent=*/ null));
+
+            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
+
+            assertWith(getCallerShortcuts())
+                    .haveIds("s1")
+                    .areAllDynamic()
+                    .areAllNotPinned();
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            // Check the intent passed to startActivityAsUser().
+            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
+
+            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
+
+            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
+
+            // Check the request object.
+            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
+
+            assertPinItemRequest(request);
+
+            assertWith(request.getShortcutInfo())
+                    .haveIds("s1")
+                    .areAllDynamic()
+                    .areAllNotPinned()
+                    .areAllWithNoIntent();
+
+            assertAllHaveIcon(list(request.getShortcutInfo()));
+
+            // Accept the request.
+            assertTrue(request.accept());
+        });
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertWith(getCallerShortcuts())
+                    .haveIds("s1")
+                    .areAllDynamic()
+                    .areAllEnabled()
+                    .areAllPinned();
+        });
+    }
+
+    public void testRequestPinShortcut_manifestExists() {
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            publishManifestShortcutsAsCaller(R.xml.shortcut_1);
+
+            assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("ms1"),
+                    /* resultIntent=*/ null));
+
+            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
+
+            assertWith(getCallerShortcuts())
+                    .haveIds("ms1")
+                    .areAllManifest()
+                    .areAllNotPinned();
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            // Check the intent passed to startActivityAsUser().
+            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
+
+            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
+
+            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
+
+            // Check the request object.
+            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
+
+            assertPinItemRequest(request);
+
+            assertWith(request.getShortcutInfo())
+                    .haveIds("ms1")
+                    .areAllManifest()
+                    .areAllNotPinned()
+                    .areAllWithNoIntent();
+
+            assertAllHaveIcon(list(request.getShortcutInfo()));
+
+            // Accept the request.
+            assertTrue(request.accept());
+        });
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertWith(getCallerShortcuts())
+                    .haveIds("ms1")
+                    .areAllManifest()
+                    .areAllEnabled()
+                    .areAllPinned();
+        });
+    }
+
+    public void testRequestPinShortcut_dynamicExists_alreadyPinned() {
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"))));
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1"), HANDLE_USER_P0);
+        });
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertWith(getCallerShortcuts())
+                    .haveIds("s1")
+                    .areAllDynamic()
+                    .areAllPinned();
+
+            assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("s1"),
+                    /* resultIntent=*/ null));
+
+            // The intent should be sent right away.
+            verify(mServiceContext, times(1)).sendIntentSender(any(IntentSender.class));
+        });
+    }
+
+    public void testRequestPinShortcut_manifestExists_alreadyPinned() {
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            publishManifestShortcutsAsCaller(R.xml.shortcut_1);
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("ms1"), HANDLE_USER_P0);
+        });
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertWith(getCallerShortcuts())
+                    .haveIds("ms1")
+                    .areAllManifest()
+                    .areAllPinned();
+
+            assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("ms1"),
+                    /* resultIntent=*/ null));
+
+            // The intent should be sent right away.
+            verify(mServiceContext, times(1)).sendIntentSender(any(IntentSender.class));
+        });
+    }
+
+    public void testRequestPinShortcut_wasDynamic_alreadyPinned() {
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"))));
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1"), HANDLE_USER_P0);
+        });
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            mManager.removeAllDynamicShortcuts();
+            assertWith(getCallerShortcuts())
+                    .haveIds("s1")
+                    .areAllNotDynamic()
+                    .areAllEnabled()
+                    .areAllPinned();
+
+            assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("s1"),
+                    /* resultIntent=*/ null));
+
+            // The intent should be sent right away.
+            verify(mServiceContext, times(1)).sendIntentSender(any(IntentSender.class));
+        });
+    }
+
+    public void testRequestPinShortcut_wasDynamic_disabled_alreadyPinned() {
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"))));
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1"), HANDLE_USER_P0);
+        });
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            mManager.disableShortcuts(list("s1"));
+
+            assertWith(getCallerShortcuts())
+                    .haveIds("s1")
+                    .areAllNotDynamic()
+                    .areAllDisabled()
+                    .areAllPinned();
+
+            assertExpectException(IllegalArgumentException.class, "exists but disabled", () -> {
+                mManager.requestPinShortcut(makeShortcutIdOnly("s1"),
+                        /* resultIntent=*/ null);
+            });
+
+            // Shouldn't be called.
+            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
+        });
+    }
+
+    public void testRequestPinShortcut_wasManifest_alreadyPinned() {
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            publishManifestShortcutsAsCaller(R.xml.shortcut_1);
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("ms1"), HANDLE_USER_P0);
+        });
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            publishManifestShortcutsAsCaller(R.xml.shortcut_0);
+
+            assertWith(getCallerShortcuts())
+                    .haveIds("ms1")
+                    .areAllNotManifest()
+                    .areAllDisabled()
+                    .areAllPinned();
+
+            assertExpectException(IllegalArgumentException.class, "exists but disabled", () -> {
+                mManager.requestPinShortcut(makeShortcutIdOnly("ms1"),
+                        /* resultIntent=*/ null);
+            });
+
+            // Shouldn't be called.
+            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
+        });
+    }
+
+    public void testRequestPinShortcut_dynamicExists_alreadyPinnedByAnother() {
+        // Initially all launchers have the shortcut permission, until we call setDefaultLauncher().
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"))));
+        });
+
+        runWithCaller(LAUNCHER_2, USER_0, () -> {
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1"), HANDLE_USER_P0);
+        });
+
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertWith(getCallerShortcuts())
+                    .haveIds("s1")
+                    .areAllDynamic()
+                    .areAllPinned();
+
+            // The shortcut is already pinned, but not by the current launcher, so it'll still
+            // invoke the whole flow.
+            assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("s1"),
+                    /* resultIntent=*/ null));
+
+            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            // Check the intent passed to startActivityAsUser().
+            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
+
+            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
+
+            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
+
+            // Check the request object.
+            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
+
+            assertPinItemRequest(request);
+
+            assertWith(request.getShortcutInfo())
+                    .haveIds("s1")
+                    .areAllDynamic()
+                    .areAllNotPinned() // Note it's not pinned by this launcher.
+                    .areAllWithNoIntent();
+
+            // Accept the request.
+            assertTrue(request.accept());
+        });
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertWith(getCallerShortcuts())
+                    .haveIds("s1")
+                    .areAllDynamic()
+                    .areAllEnabled()
+                    .areAllPinned();
+        });
+    }
+
+    public void testRequestPinShortcut_manifestExists_alreadyPinnedByAnother() {
+        // Initially all launchers have the shortcut permission, until we call setDefaultLauncher().
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            publishManifestShortcutsAsCaller(R.xml.shortcut_1);
+        });
+
+        runWithCaller(LAUNCHER_2, USER_0, () -> {
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("ms1"), HANDLE_USER_P0);
+        });
+
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertWith(getCallerShortcuts())
+                    .haveIds("ms1")
+                    .areAllManifest()
+                    .areAllPinned();
+
+            // The shortcut is already pinned, but not by the current launcher, so it'll still
+            // invoke the whole flow.
+            assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("ms1"),
+                    /* resultIntent=*/ null));
+
+            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            // Check the intent passed to startActivityAsUser().
+            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
+
+            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
+
+            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
+
+            // Check the request object.
+            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
+
+            assertPinItemRequest(request);
+
+            assertWith(request.getShortcutInfo())
+                    .haveIds("ms1")
+                    .areAllManifest()
+                    .areAllNotPinned() // Note it's not pinned by this launcher.
+                    .areAllWithNoIntent();
+
+            // Accept the request.
+            assertTrue(request.accept());
+        });
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertWith(getCallerShortcuts())
+                    .haveIds("ms1")
+                    .areAllManifest()
+                    .areAllEnabled()
+                    .areAllPinned();
+        });
+    }
+
+    /**
+     * The launcher already has a pinned shortuct.  The new one should be added, not replace
+     * the existing one.
+     */
+    public void testRequestPinShortcut_launcherAlreadyHasPinned() {
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"), makeShortcut("s2"))));
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s2"), HANDLE_USER_P0);
+        });
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("s1"),
+                    /* resultIntent=*/ null));
+
+            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            // Check the intent passed to startActivityAsUser().
+            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
+
+            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
+
+            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
+
+            // Check the request object.
+            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
+
+            assertPinItemRequest(request);
+
+            assertWith(request.getShortcutInfo())
+                    .haveIds("s1")
+                    .areAllDynamic()
+                    .areAllNotPinned()
+                    .areAllWithNoIntent();
+
+            // Accept the request.
+            assertTrue(request.accept());
+
+            assertWith(getShortcutAsLauncher(USER_P0))
+                    .haveIds("s1", "s2")
+                    .areAllDynamic()
+                    .areAllEnabled()
+                    .areAllPinned();
+        });
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertWith(getCallerShortcuts())
+                    .haveIds("s1", "s2")
+                    .areAllDynamic()
+                    .areAllEnabled()
+                    .areAllPinned();
+        });
+    }
+
+    /**
+     * When trying to pin an existing shortcut, the new fields shouldn't override existing fields.
+     */
+    public void testRequestPinShortcut_dynamicExists_titleWontChange() {
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+
+        final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            // Create dynamic shortcut
+            ShortcutInfo s1 = makeShortcutWithIcon("s1", res32x32);
+            assertTrue(mManager.setDynamicShortcuts(list(s1)));
+
+            assertTrue(mManager.requestPinShortcut(makeShortcutWithShortLabel("s1", "xxx"),
+                    /* resultIntent=*/ null));
+
+            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
+
+            assertWith(getCallerShortcuts())
+                    .haveIds("s1")
+                    .areAllDynamic()
+                    .areAllNotPinned();
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            // Check the intent passed to startActivityAsUser().
+            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
+
+            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
+
+            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
+
+            // Check the request object.
+            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
+
+            assertPinItemRequest(request);
+
+            assertWith(request.getShortcutInfo())
+                    .haveIds("s1")
+                    .areAllDynamic()
+                    .areAllNotPinned()
+                    .areAllWithNoIntent();
+
+            assertAllHaveIcon(list(request.getShortcutInfo()));
+
+            // Accept the request.
+            assertTrue(request.accept());
+        });
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertWith(getCallerShortcuts())
+                    .haveIds("s1")
+                    .areAllDynamic()
+                    .areAllEnabled()
+                    .areAllPinned()
+                    .forShortcutWithId("s1", (si) -> {
+                        // Still the original title.
+                        assertEquals("Title-s1", si.getShortLabel());
+                    });
+        });
+    }
+
+    /**
+     * When trying to pin an existing shortcut, the new fields shouldn't override existing fields.
+     */
+    public void testRequestPinShortcut_manifestExists_titleWontChange() {
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            publishManifestShortcutsAsCaller(R.xml.shortcut_1);
+
+            assertTrue(mManager.requestPinShortcut(makeShortcutWithShortLabel("ms1", "xxx"),
+                    /* resultIntent=*/ null));
+
+            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
+
+            assertWith(getCallerShortcuts())
+                    .haveIds("ms1")
+                    .areAllManifest()
+                    .areAllNotPinned();
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            // Check the intent passed to startActivityAsUser().
+            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
+
+            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
+
+            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
+
+            // Check the request object.
+            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
+
+            assertPinItemRequest(request);
+
+            assertWith(request.getShortcutInfo())
+                    .haveIds("ms1")
+                    .areAllManifest()
+                    .areAllNotPinned()
+                    .areAllWithNoIntent();
+
+            assertAllHaveIcon(list(request.getShortcutInfo()));
+
+            // Accept the request.
+            assertTrue(request.accept());
+        });
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertWith(getCallerShortcuts())
+                    .haveIds("ms1")
+                    .areAllManifest()
+                    .areAllEnabled()
+                    .areAllPinned()
+                    .forShortcutWithId("ms1", (si) -> {
+                        // Still the original title.
+                        // Title should be something like:
+                        // "string-com.android.test.1-user:20-res:2131034112/en"
+                        MoreAsserts.assertContainsRegex("^string-", si.getShortLabel().toString());
+                    });
+        });
+    }
+
+    /**
+     * The dynamic shortcut existed, but before accepting(), it's removed.  Because the request
+     * has a partial shortcut, accept() should fail.
+     */
+    public void testRequestPinShortcut_dynamicExists_thenRemoved_error() {
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            // Create dynamic shortcut
+            ShortcutInfo s1 = makeShortcut("s1");
+            assertTrue(mManager.setDynamicShortcuts(list(s1)));
+
+            assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("s1"),
+                    /* resultIntent=*/ null));
+
+            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
+
+            mManager.removeAllDynamicShortcuts();
+
+            assertWith(getCallerShortcuts())
+                    .isEmpty();
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            // Check the intent passed to startActivityAsUser().
+            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
+
+            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
+
+            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
+
+            // Check the request object.
+            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
+
+            assertPinItemRequest(request);
+
+            assertWith(request.getShortcutInfo())
+                    .haveIds("s1")
+                    .areAllDynamic()
+                    .areAllNotPinned()
+                    .areAllWithNoIntent();
+
+            // Accept the request -> should fail.
+            assertForLauncherCallbackNoThrow(mLauncherApps,
+                    () -> assertFalse(request.accept()))
+                    .assertNoCallbackCalled();
+        });
+
+        // Intent shouldn't be sent.
+        verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertWith(getCallerShortcuts())
+                    .isEmpty();
+        });
+    }
+
+    /**
+     * The dynamic shortcut existed, but before accepting(), it's removed.  Because the request
+     * has all the mandatory fields, we can go ahead and still publish it.
+     */
+    public void testRequestPinShortcut_dynamicExists_thenRemoved_okay() {
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            // Create dynamic shortcut
+            ShortcutInfo s1 = makeShortcut("s1");
+            assertTrue(mManager.setDynamicShortcuts(list(s1)));
+
+            assertTrue(mManager.requestPinShortcut(makeShortcutWithShortLabel("s1", "new"),
+                    /* resultIntent=*/ null));
+
+            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
+
+            mManager.removeAllDynamicShortcuts();
+
+            assertWith(getCallerShortcuts())
+                    .isEmpty();
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            // Check the intent passed to startActivityAsUser().
+            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
+
+            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
+
+            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
+
+            // Check the request object.
+            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
+
+            assertPinItemRequest(request);
+
+            assertWith(request.getShortcutInfo())
+                    .haveIds("s1")
+                    .areAllDynamic()
+                    .areAllNotPinned()
+                    .areAllWithNoIntent();
+
+            // Accept the request -> should fail.
+            assertTrue(request.accept());
+        });
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertWith(getCallerShortcuts())
+                    .haveIds("s1")
+                    .areAllFloating()
+                    .forShortcutWithId("s1", si -> {
+                        assertEquals("new", si.getShortLabel());
+                    });
+        });
+    }
+
+    /**
+     * The manifest shortcut existed, but before accepting(), it's removed.  Because the request
+     * has a partial shortcut, accept() should fail.
+     */
+    public void testRequestPinShortcut_manifestExists_thenRemoved_error() {
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            publishManifestShortcutsAsCaller(R.xml.shortcut_1);
+
+            assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("ms1"),
+                    /* resultIntent=*/ null));
+
+            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
+
+            publishManifestShortcutsAsCaller(R.xml.shortcut_0);
+
+            assertWith(getCallerShortcuts())
+                    .isEmpty();
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            // Check the intent passed to startActivityAsUser().
+            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
+
+            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
+
+            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
+
+            // Check the request object.
+            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
+
+            assertPinItemRequest(request);
+
+            assertWith(request.getShortcutInfo())
+                    .haveIds("ms1")
+                    .areAllManifest()
+                    .areAllNotPinned()
+                    .areAllWithNoIntent();
+
+            // Accept the request -> should fail.
+            assertForLauncherCallbackNoThrow(mLauncherApps,
+                    () -> assertFalse(request.accept()))
+                    .assertNoCallbackCalled();
+        });
+
+        // Intent shouldn't be sent.
+        verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertWith(getCallerShortcuts())
+                    .isEmpty();
+        });
+    }
+
+    /**
+     * The manifest shortcut existed, but before accepting(), it's removed.  Because the request
+     * has all the mandatory fields, we can go ahead and still publish it.
+     */
+    public void testRequestPinShortcut_manifestExists_thenRemoved_okay() {
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            publishManifestShortcutsAsCaller(R.xml.shortcut_1);
+
+            assertTrue(mManager.requestPinShortcut(makeShortcutWithShortLabel("ms1", "new"),
+                    /* resultIntent=*/ null));
+
+            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
+
+            publishManifestShortcutsAsCaller(R.xml.shortcut_0);
+
+            assertWith(getCallerShortcuts())
+                    .isEmpty();
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            // Check the intent passed to startActivityAsUser().
+            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
+
+            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
+
+            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
+
+            // Check the request object.
+            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
+
+            assertPinItemRequest(request);
+
+            assertWith(request.getShortcutInfo())
+                    .haveIds("ms1")
+                    .areAllManifest()
+                    .areAllNotPinned()
+                    .areAllWithNoIntent();
+
+
+            // Accept the request -> should fail.
+            assertTrue(request.accept());
+        });
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertWith(getCallerShortcuts())
+                    .haveIds("ms1")
+                    .areAllMutable() // Note it's no longer immutable.
+                    .areAllFloating()
+                    .forShortcutWithId("ms1", si -> {
+                        assertEquals("new", si.getShortLabel());
+                    });
+        });
+    }
+
+    /**
+     * The dynamic shortcut existed, but before accepting(), it's removed.  Because the request
+     * has a partial shortcut, accept() should fail.
+     */
+    public void testRequestPinShortcut_dynamicExists_thenDisabled_error() {
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            ShortcutInfo s1 = makeShortcut("s1");
+            assertTrue(mManager.setDynamicShortcuts(list(s1)));
+
+            assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("s1"),
+                    /* resultIntent=*/ null));
+
+            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
+        });
+
+        // Then, pin by another launcher and disable it.
+        // We have to pin it here so that disable() won't remove it.
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_2, USER_0));
+        runWithCaller(LAUNCHER_2, USER_0, () -> {
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1"), HANDLE_USER_P0);
+        });
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            mManager.disableShortcuts(list("s1"));
+            assertWith(getCallerShortcuts())
+                    .haveIds("s1")
+                    .areAllDisabled();
+        });
+
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            // Check the intent passed to startActivityAsUser().
+            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
+
+            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
+
+            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
+
+            // Check the request object.
+            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
+
+            assertPinItemRequest(request);
+
+            assertWith(request.getShortcutInfo())
+                    .haveIds("s1")
+                    .areAllDynamic()
+                    .areAllNotPinned()
+                    .areAllWithNoIntent();
+
+            // Accept the request -> should fail.
+            assertForLauncherCallbackNoThrow(mLauncherApps,
+                    () -> assertFalse(request.accept()))
+                    .assertNoCallbackCalled();
+
+            // Note s1 is floating and pinned by another launcher, so it shouldn't be
+            // visible here.
+            assertWith(getShortcutAsLauncher(USER_P0))
+                    .isEmpty();
+        });
+
+        // Intent shouldn't be sent.
+        verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertWith(getCallerShortcuts())
+                    .haveIds("s1")
+                    .areAllDisabled();
+        });
+    }
+
+    /**
+     * The manifest shortcut existed, but before accepting(), it's removed.  Because the request
+     * has a partial shortcut, accept() should fail.
+     */
+    public void testRequestPinShortcut_manifestExists_thenDisabled_error() {
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            publishManifestShortcutsAsCaller(R.xml.shortcut_1);
+
+            assertTrue(mManager.requestPinShortcut(makeShortcutIdOnly("ms1"),
+                    /* resultIntent=*/ null));
+
+            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
+        });
+
+        // Then, pin by another launcher and disable it.
+        // We have to pin it here so that disable() won't remove it.
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_2, USER_0));
+        runWithCaller(LAUNCHER_2, USER_0, () -> {
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("ms1"), HANDLE_USER_P0);
+        });
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            publishManifestShortcutsAsCaller(R.xml.shortcut_0);
+            assertWith(getCallerShortcuts())
+                    .haveIds("ms1")
+                    .areAllDisabled();
+        });
+
+        setDefaultLauncher(USER_0, mMainActivityFetcher.apply(LAUNCHER_1, USER_0));
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            // Check the intent passed to startActivityAsUser().
+            final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
+
+            verify(mServiceContext).startActivityAsUser(intent.capture(), eq(HANDLE_USER_0));
+
+            assertPinItemRequestIntent(intent.getValue(), mInjectedClientPackage);
+
+            // Check the request object.
+            final PinItemRequest request = mLauncherApps.getPinItemRequest(intent.getValue());
+
+            assertPinItemRequest(request);
+
+            assertWith(request.getShortcutInfo())
+                    .haveIds("ms1")
+                    .areAllManifest()
+                    .areAllNotPinned()
+                    .areAllWithNoIntent();
+
+            // Accept the request -> should fail.
+            assertForLauncherCallbackNoThrow(mLauncherApps,
+                    () -> assertFalse(request.accept()))
+                    .assertNoCallbackCalled();
+
+            // Note ms1 is floating and pinned by another launcher, so it shouldn't be
+            // visible here.
+            assertWith(getShortcutAsLauncher(USER_P0))
+                    .isEmpty();
+        });
+
+        // Intent shouldn't be sent.
+        verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+            assertWith(getCallerShortcuts())
+                    .haveIds("ms1")
+                    .areAllDisabled();
+        });
+    }
+
+    // TODO More tests:
+
+    // Cancel previous pending request and release memory?
+
+    // Check the launcher callback too.
+
+    // Missing fields -- pre and post, both.
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 40d8ac0..9b2c94e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -241,15 +241,14 @@
         }
     }
 
-    // Make sure createProfile would fail if we have DISALLOW_ADD_USER.
+    // Make sure createUser would fail if we have DISALLOW_ADD_USER.
     @MediumTest
-    public void testCreateProfileForUser_disallowAddUser() throws Exception {
+    public void testCreateUser_disallowAddUser() throws Exception {
         final int primaryUserId = mUserManager.getPrimaryUser().id;
         final UserHandle primaryUserHandle = new UserHandle(primaryUserId);
         mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, true, primaryUserHandle);
         try {
-            UserInfo userInfo = createProfileForUser("Managed",
-                    UserInfo.FLAG_MANAGED_PROFILE, primaryUserId);
+            UserInfo userInfo = createUser("SecondaryUser", /*flags=*/ 0);
             assertNull(userInfo);
         } finally {
             mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, false,
@@ -257,17 +256,51 @@
         }
     }
 
-    // Make sure createProfileEvenWhenDisallowedForUser bypass DISALLOW_ADD_USER.
+    // Make sure createProfile would fail if we have DISALLOW_ADD_MANAGED_PROFILE.
+    @MediumTest
+    public void testCreateProfileForUser_disallowAddManagedProfile() throws Exception {
+        final int primaryUserId = mUserManager.getPrimaryUser().id;
+        final UserHandle primaryUserHandle = new UserHandle(primaryUserId);
+        mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, true,
+                primaryUserHandle);
+        try {
+            UserInfo userInfo = createProfileForUser("Managed",
+                    UserInfo.FLAG_MANAGED_PROFILE, primaryUserId);
+            assertNull(userInfo);
+        } finally {
+            mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, false,
+                    primaryUserHandle);
+        }
+    }
+
+    // Make sure createProfileEvenWhenDisallowedForUser bypass DISALLOW_ADD_MANAGED_PROFILE.
     @MediumTest
     public void testCreateProfileForUserEvenWhenDisallowed() throws Exception {
         final int primaryUserId = mUserManager.getPrimaryUser().id;
         final UserHandle primaryUserHandle = new UserHandle(primaryUserId);
-        mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, true, primaryUserHandle);
+        mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, true,
+                primaryUserHandle);
         try {
             UserInfo userInfo = createProfileEvenWhenDisallowedForUser("Managed",
                     UserInfo.FLAG_MANAGED_PROFILE, primaryUserId);
             assertNotNull(userInfo);
         } finally {
+            mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, false,
+                    primaryUserHandle);
+        }
+    }
+
+    // createProfile succeeds even if DISALLOW_ADD_USER is set
+    @MediumTest
+    public void testCreateProfileForUser_disallowAddUser() throws Exception {
+        final int primaryUserId = mUserManager.getPrimaryUser().id;
+        final UserHandle primaryUserHandle = new UserHandle(primaryUserId);
+        mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, true, primaryUserHandle);
+        try {
+            UserInfo userInfo = createProfileForUser("Managed",
+                    UserInfo.FLAG_MANAGED_PROFILE, primaryUserId);
+            assertNotNull(userInfo);
+        } finally {
             mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, false,
                     primaryUserHandle);
         }
diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
index 6e74deb..a664f21 100644
--- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
+++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
@@ -729,6 +729,10 @@
         return new ShortcutListAsserter(list);
     }
 
+    public static ShortcutListAsserter assertWith(ShortcutInfo... list) {
+        return assertWith(list(list));
+    }
+
     /**
      * New style assertion that allows chained calls.
      */
@@ -886,6 +890,30 @@
             return this;
         }
 
+        public ShortcutListAsserter areAllFloating() {
+            forAllShortcuts(s -> assertTrue("id=" + s.getId(),
+                    s.isPinned() && !s.isDeclaredInManifest() && !s.isDynamic()));
+            return this;
+        }
+
+        public ShortcutListAsserter areAllNotFloating() {
+            forAllShortcuts(s -> assertTrue("id=" + s.getId(),
+                    !(s.isPinned() && !s.isDeclaredInManifest() && !s.isDynamic())));
+            return this;
+        }
+
+        public ShortcutListAsserter areAllOrphan() {
+            forAllShortcuts(s -> assertTrue("id=" + s.getId(),
+                    !s.isPinned() && !s.isDeclaredInManifest() && !s.isDynamic()));
+            return this;
+        }
+
+        public ShortcutListAsserter areAllNotOrphan() {
+            forAllShortcuts(s -> assertTrue("id=" + s.getId(),
+                    s.isPinned() || s.isDeclaredInManifest() || s.isDynamic()));
+            return this;
+        }
+
         public ShortcutListAsserter areAllWithKeyFieldsOnly() {
             forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.hasKeyFieldsOnly()));
             return this;
@@ -906,6 +934,16 @@
             return this;
         }
 
+        public ShortcutListAsserter areAllWithIntent() {
+            forAllShortcuts(s -> assertNotNull("id=" + s.getId(), s.getIntent()));
+            return this;
+        }
+
+        public ShortcutListAsserter areAllWithNoIntent() {
+            forAllShortcuts(s -> assertNull("id=" + s.getId(), s.getIntent()));
+            return this;
+        }
+
         public ShortcutListAsserter forAllShortcuts(Consumer<ShortcutInfo> sa) {
             boolean found = false;
             for (int i = 0; i < mList.size(); i++) {
@@ -1064,6 +1102,16 @@
         return asserter;
     }
 
+    public static LauncherCallbackAsserter assertForLauncherCallbackNoThrow(
+            LauncherApps launcherApps, Runnable body) {
+        try {
+            return assertForLauncherCallback(launcherApps, body);
+        } catch (InterruptedException e) {
+            fail("Caught InterruptedException");
+            return null; // Never happens.
+        }
+    }
+
     public static void retryUntil(BooleanSupplier checker, String message) {
         retryUntil(checker, message, 30);
     }
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index f541f70..cb32d1f 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -121,6 +121,23 @@
         endTime = timeStamp;
     }
 
+    void updateChooserCounts(String packageName, String category, String action) {
+        UsageStats usageStats = getOrCreateUsageStats(packageName);
+        if (usageStats.mChooserCounts == null) {
+            usageStats.mChooserCounts = new ArrayMap<>();
+        }
+        ArrayMap<String, Integer> chooserCounts;
+        final int idx = usageStats.mChooserCounts.indexOfKey(action);
+        if (idx < 0) {
+            chooserCounts = new ArrayMap<>();
+            usageStats.mChooserCounts.put(action, chooserCounts);
+        } else {
+            chooserCounts = usageStats.mChooserCounts.valueAt(idx);
+        }
+        int currentCount = chooserCounts.getOrDefault(category, 0);
+        chooserCounts.put(category, currentCount + 1);
+    }
+
     void updateConfigurationStats(Configuration config, long timeStamp) {
         if (activeConfiguration != null) {
             ConfigurationStats activeStats = configurations.get(activeConfiguration);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index 849262e..1b28db7 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -17,6 +17,7 @@
 package com.android.server.usage;
 
 import android.app.usage.TimeSparseArray;
+import android.app.usage.UsageStats;
 import android.app.usage.UsageStatsManager;
 import android.os.Build;
 import android.util.AtomicFile;
@@ -502,6 +503,12 @@
             pruneFilesOlderThan(mIntervalDirs[UsageStatsManager.INTERVAL_DAILY],
                     mCal.getTimeInMillis());
 
+            mCal.setTimeInMillis(currentTimeMillis);
+            mCal.addDays(-14);
+            for (int i = 0; i < mIntervalDirs.length; ++i) {
+                pruneChooserCountsOlderThan(mIntervalDirs[i], mCal.getTimeInMillis());
+            }
+
             // We must re-index our file list or we will be trying to read
             // deleted files.
             indexFilesLocked();
@@ -531,6 +538,43 @@
         }
     }
 
+    private static void pruneChooserCountsOlderThan(File dir, long expiryTime) {
+        File[] files = dir.listFiles();
+        if (files != null) {
+            for (File f : files) {
+                String path = f.getPath();
+                if (path.endsWith(BAK_SUFFIX)) {
+                    f = new File(path.substring(0, path.length() - BAK_SUFFIX.length()));
+                }
+
+                long beginTime;
+                try {
+                    beginTime = UsageStatsXml.parseBeginTime(f);
+                } catch (IOException e) {
+                    beginTime = 0;
+                }
+
+                if (beginTime < expiryTime) {
+                    try {
+                        final AtomicFile af = new AtomicFile(f);
+                        final IntervalStats stats = new IntervalStats();
+                        UsageStatsXml.read(af, stats);
+                        final int pkgCount = stats.packageStats.size();
+                        for (int i = 0; i < pkgCount; i++) {
+                            UsageStats pkgStats = stats.packageStats.valueAt(i);
+                            if (pkgStats.mChooserCounts != null) {
+                                pkgStats.mChooserCounts.clear();
+                            }
+                        }
+                        UsageStatsXml.write(af, stats);
+                    } catch (IOException e) {
+                        Slog.e(TAG, "Failed to delete chooser counts from usage stats file", e);
+                    }
+                }
+            }
+        }
+    }
+
     /**
      * Update the stats in the database. They may not be written to disk immediately.
      */
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 515370f..4bfc3df 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -1379,6 +1379,31 @@
             }
             UsageStatsService.this.dump(args, pw);
         }
+
+        @Override
+        public void reportChooserSelection(String packageName, int userId, String contentType,
+                                           String[] annotations, String action) {
+            if (packageName == null) {
+                Slog.w(TAG, "Event report user selecting a null package");
+                return;
+            }
+
+            UsageEvents.Event event = new UsageEvents.Event();
+            event.mPackage = packageName;
+
+            // This will later be converted to system time.
+            event.mTimeStamp = SystemClock.elapsedRealtime();
+
+            event.mEventType = Event.CHOOSER_ACTION;
+
+            event.mAction = action;
+
+            event.mContentType = contentType;
+
+            event.mContentAnnotations = annotations;
+
+            mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
+        }
     }
 
     /**
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index 03cee9c..96f3305 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -26,6 +26,7 @@
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStats;
 import android.content.res.Configuration;
+import android.util.ArrayMap;
 
 import java.io.IOException;
 import java.net.ProtocolException;
@@ -37,6 +38,11 @@
     private static final String PACKAGES_TAG = "packages";
     private static final String PACKAGE_TAG = "package";
 
+    private static final String CHOOSER_COUNT_TAG = "chosen_action";
+    private static final String CATEGORY_TAG = "category";
+    private static final String NAME = "name";
+    private static final String COUNT = "count";
+
     private static final String CONFIGURATIONS_TAG = "configurations";
     private static final String CONFIG_TAG = "config";
 
@@ -59,7 +65,7 @@
     private static final String TIME_ATTR = "time";
 
     private static void loadUsageStats(XmlPullParser parser, IntervalStats statsOut)
-            throws IOException {
+            throws XmlPullParserException, IOException {
         final String pkg = parser.getAttributeValue(null, PACKAGE_ATTR);
         if (pkg == null) {
             throw new ProtocolException("no " + PACKAGE_ATTR + " attribute present");
@@ -72,6 +78,51 @@
                 parser, LAST_TIME_ACTIVE_ATTR);
         stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
         stats.mLastEvent = XmlUtils.readIntAttribute(parser, LAST_EVENT_ATTR);
+        int eventCode;
+        while ((eventCode = parser.next()) != XmlPullParser.END_DOCUMENT) {
+            final String tag = parser.getName();
+            if (eventCode == XmlPullParser.END_TAG && tag.equals(PACKAGE_TAG)) {
+                break;
+            }
+            if (eventCode != XmlPullParser.START_TAG) {
+                continue;
+            }
+            if (tag.equals(CHOOSER_COUNT_TAG)) {
+                String action = XmlUtils.readStringAttribute(parser, NAME);
+                loadChooserCounts(parser, stats, action);
+            }
+        }
+    }
+
+    private static void loadChooserCounts(
+            XmlPullParser parser, UsageStats usageStats, String action)
+            throws XmlPullParserException, IOException {
+        if (action == null) {
+            return;
+        }
+        if (usageStats.mChooserCounts == null) {
+            usageStats.mChooserCounts = new ArrayMap<>();
+        }
+        if (!usageStats.mChooserCounts.containsKey(action)) {
+            ArrayMap<String, Integer> counts = new ArrayMap<>();
+            usageStats.mChooserCounts.put(action, counts);
+        }
+
+        int eventCode;
+        while ((eventCode = parser.next()) != XmlPullParser.END_DOCUMENT) {
+            final String tag = parser.getName();
+            if (eventCode == XmlPullParser.END_TAG && tag.equals(CHOOSER_COUNT_TAG)) {
+                break;
+            }
+            if (eventCode != XmlPullParser.START_TAG) {
+                continue;
+            }
+            if (tag.equals(CATEGORY_TAG)) {
+                String category = XmlUtils.readStringAttribute(parser, NAME);
+                int count = XmlUtils.readIntAttribute(parser, COUNT);
+                usageStats.mChooserCounts.get(action).put(category, count);
+            }
+        }
     }
 
     private static void loadConfigStats(XmlPullParser parser, IntervalStats statsOut)
@@ -135,10 +186,45 @@
         XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, usageStats.mPackageName);
         XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, usageStats.mTotalTimeInForeground);
         XmlUtils.writeIntAttribute(xml, LAST_EVENT_ATTR, usageStats.mLastEvent);
-
+        writeChooserCounts(xml, usageStats);
         xml.endTag(null, PACKAGE_TAG);
     }
 
+    private static void writeChooserCounts(XmlSerializer xml, final UsageStats usageStats)
+            throws IOException {
+        if (usageStats == null || usageStats.mChooserCounts == null ||
+                usageStats.mChooserCounts.keySet().isEmpty()) {
+            return;
+        }
+        final int chooserCountSize = usageStats.mChooserCounts.size();
+        for (int i = 0; i < chooserCountSize; i++) {
+            final String action = usageStats.mChooserCounts.keyAt(i);
+            final ArrayMap<String, Integer> counts = usageStats.mChooserCounts.valueAt(i);
+            if (action == null || counts == null || counts.isEmpty()) {
+                continue;
+            }
+            xml.startTag(null, CHOOSER_COUNT_TAG);
+            XmlUtils.writeStringAttribute(xml, NAME, action);
+            writeCountsForAction(xml, counts);
+            xml.endTag(null, CHOOSER_COUNT_TAG);
+        }
+    }
+
+    private static void writeCountsForAction(XmlSerializer xml, ArrayMap<String, Integer> counts)
+            throws IOException {
+        final int countsSize = counts.size();
+        for (int i = 0; i < countsSize; i++) {
+            String key = counts.keyAt(i);
+            int count = counts.valueAt(i);
+            if (count > 0) {
+                xml.startTag(null, CATEGORY_TAG);
+                XmlUtils.writeStringAttribute(xml, NAME, key);
+                XmlUtils.writeIntAttribute(xml, COUNT, count);
+                xml.endTag(null, CATEGORY_TAG);
+            }
+        }
+    }
+
     private static void writeConfigStats(XmlSerializer xml, final IntervalStats stats,
             final ConfigurationStats configStats, boolean isActive) throws IOException {
         xml.startTag(null, CONFIG_TAG);
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 59e4c80..ba770ef 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -183,6 +183,15 @@
         for (IntervalStats stats : mCurrentStats) {
             if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE) {
                 stats.updateConfigurationStats(newFullConfig, event.mTimeStamp);
+            } else if (event.mEventType == UsageEvents.Event.CHOOSER_ACTION) {
+                stats.updateChooserCounts(event.mPackage, event.mContentType, event.mAction);
+                String[] annotations = event.mContentAnnotations;
+                if (annotations != null) {
+                    for (String annotation : annotations) {
+                        // TODO(kanlig): update with confidences of annotations.
+                        stats.updateChooserCounts(event.mPackage, annotation, event.mAction);
+                    }
+                }
             } else {
                 stats.update(event.mPackage, event.mTimeStamp, event.mEventType);
             }
@@ -520,6 +529,32 @@
         }
         pw.decreaseIndent();
 
+        pw.println();
+        pw.increaseIndent();
+        pw.println("ChooserCounts");
+        pw.increaseIndent();
+        for (UsageStats usageStats : pkgStats.values()) {
+            pw.printPair("package", usageStats.mPackageName);
+            if (usageStats.mChooserCounts != null) {
+                final int chooserCountSize = usageStats.mChooserCounts.size();
+                for (int i = 0; i < chooserCountSize; i++) {
+                    final String action = usageStats.mChooserCounts.keyAt(i);
+                    final ArrayMap<String, Integer> counts = usageStats.mChooserCounts.valueAt(i);
+                    final int annotationSize = counts.size();
+                    for (int j = 0; j < annotationSize; j++) {
+                        final String key = counts.keyAt(j);
+                        final int count = counts.valueAt(j);
+                        if (count != 0) {
+                            pw.printPair("ChooserCounts", action + ":" + key + " is " +
+                                    Integer.toString(count));
+                            pw.println();
+                        }
+                    }
+                }
+            }
+            pw.println();
+        }
+
         pw.println("configurations");
         pw.increaseIndent();
         final ArrayMap<Configuration, ConfigurationStats> configStats = stats.configurations;
@@ -593,6 +628,8 @@
                 return "USER_INTERACTION";
             case UsageEvents.Event.SHORTCUT_INVOCATION:
                 return "SHORTCUT_INVOCATION";
+            case UsageEvents.Event.CHOOSER_ACTION:
+                return "CHOOSER_ACTION";
             default:
                 return "UNKNOWN";
         }
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 581dbe3..db7a31a 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -606,8 +606,12 @@
                 } else {
                     Slog.e(TAG, "nativeGetAccessoryStrings failed");
                 }
-            } else if (!mConnected && !enteringAccessoryMode) {
-                notifyAccessoryModeExit();
+            } else {
+                if (!enteringAccessoryMode) {
+                    notifyAccessoryModeExit();
+                } else if (DEBUG) {
+                    Slog.v(TAG, "Debouncing accessory mode exit");
+                }
             }
         }
 
@@ -813,6 +817,9 @@
                     break;
                 }
                 case MSG_ACCESSORY_MODE_ENTER_TIMEOUT: {
+                    if (DEBUG) {
+                        Slog.v(TAG, "Accessory mode enter timeout: " + mConnected);
+                    }
                     if (!mConnected) {
                         notifyAccessoryModeExit();
                     }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 5e28734..939a3b8 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1146,6 +1146,14 @@
     public static final String KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT =
             "network_notification_delay_int";
 
+    /**
+     * Indicates whether the carrier supports 3gpp call forwarding MMI codes while roaming. If
+     * false, the user will be notified that call forwarding is not available when the MMI code
+     * fails.
+     */
+    public static final String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL =
+        "support_3gpp_call_forwarding_while_roaming_bool";
+
     /** The default value for every variable. */
     private final static PersistableBundle sDefaults;
 
@@ -1349,6 +1357,7 @@
         sDefaults.putBoolean(KEY_PERSIST_LPP_MODE_BOOL, false);
         sDefaults.putStringArray(KEY_CARRIER_WIFI_STRING_ARRAY, null);
         sDefaults.putInt(KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT, -1);
+        sDefaults.putBoolean(KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL, true);
     }
 
     /**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 918ef5e..7797271 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1852,21 +1852,14 @@
     public static final int SIM_STATE_NETWORK_LOCKED = 4;
     /** SIM card state: Ready */
     public static final int SIM_STATE_READY = 5;
-    /** SIM card state: SIM Card is NOT READY
-     *@hide
-     */
+    /** SIM card state: SIM Card is NOT READY */
     public static final int SIM_STATE_NOT_READY = 6;
-    /** SIM card state: SIM Card Error, permanently disabled
-     *@hide
-     */
+    /** SIM card state: SIM Card Error, permanently disabled */
     public static final int SIM_STATE_PERM_DISABLED = 7;
-    /** SIM card state: SIM Card Error, present but faulty
-     *@hide
-     */
+    /** SIM card state: SIM Card Error, present but faulty */
     public static final int SIM_STATE_CARD_IO_ERROR = 8;
     /** SIM card state: SIM Card restricted, present but not usable due to
      * carrier restrictions.
-     *@hide
      */
     public static final int SIM_STATE_CARD_RESTRICTED = 9;
 
@@ -1912,6 +1905,7 @@
      * @see #SIM_STATE_NOT_READY
      * @see #SIM_STATE_PERM_DISABLED
      * @see #SIM_STATE_CARD_IO_ERROR
+     * @see #SIM_STATE_CARD_RESTRICTED
      */
     public int getSimState() {
         int slotIdx = getDefaultSim();
@@ -1949,8 +1943,8 @@
      * @see #SIM_STATE_NOT_READY
      * @see #SIM_STATE_PERM_DISABLED
      * @see #SIM_STATE_CARD_IO_ERROR
+     * @see #SIM_STATE_CARD_RESTRICTED
      */
-    /** {@hide} */
     public int getSimState(int slotIdx) {
         int simState = SubscriptionManager.getSimStateForSlotIdx(slotIdx);
         return simState;
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
index 48d3159..f9cac43 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
@@ -369,7 +369,8 @@
 
     static void verifySerialization(String want, ConnectivityMetricsEvent... input) {
         try {
-            byte[] got = IpConnectivityEventBuilder.serialize(0, Arrays.asList(input));
+            byte[] got = IpConnectivityEventBuilder.serialize(0,
+                    IpConnectivityEventBuilder.toProto(Arrays.asList(input)));
             IpConnectivityLog log = IpConnectivityLog.parseFrom(got);
             assertEquals(want, log.toString());
         } catch (Exception e) {
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index 60628ec..e9257fa 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -292,10 +292,5 @@
     }
 
     static final Comparator<ConnectivityMetricsEvent> EVENT_COMPARATOR =
-        new Comparator<ConnectivityMetricsEvent>() {
-            @Override
-            public int compare(ConnectivityMetricsEvent ev1, ConnectivityMetricsEvent ev2) {
-                return (int) (ev1.timestamp - ev2.timestamp);
-            }
-        };
+        Comparator.comparingLong((ev) -> ev.timestamp);
 }
diff --git a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
index 2bb62bb..75d2f1a 100644
--- a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
@@ -16,25 +16,35 @@
 
 package com.android.server.connectivity;
 
-import android.net.ConnectivityManager.NetworkCallback;
 import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
 import android.net.Network;
+import android.net.metrics.ConnectStats;
 import android.net.metrics.DnsEvent;
 import android.net.metrics.INetdEventListener;
 import android.net.metrics.IpConnectivityLog;
 import android.os.RemoteException;
+import android.system.OsConstants;
 import android.test.suitebuilder.annotation.SmallTest;
-
+import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import java.util.OptionalInt;
+import java.util.stream.IntStream;
 import junit.framework.TestCase;
 import org.junit.Before;
 import org.junit.Test;
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertTrue;
-
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.eq;
@@ -42,13 +52,6 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
-import java.io.FileOutputStream;
-import java.io.PrintWriter;
-import java.util.Arrays;
-import java.util.List;
-import java.util.OptionalInt;
-import java.util.stream.IntStream;
-
 public class NetdEventListenerServiceTest extends TestCase {
 
     // TODO: read from NetdEventListenerService after this constant is read from system property
@@ -68,45 +71,48 @@
         }
     }
 
+    private static final String EXAMPLE_IPV4 = "192.0.2.1";
+    private static final String EXAMPLE_IPV6 = "2001:db8:1200::2:1";
+
     NetdEventListenerService mNetdEventListenerService;
 
     @Mock ConnectivityManager mCm;
     @Mock IpConnectivityLog mLog;
     ArgumentCaptor<NetworkCallback> mCallbackCaptor;
-    ArgumentCaptor<DnsEvent> mEvCaptor;
+    ArgumentCaptor<DnsEvent> mDnsEvCaptor;
 
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mCallbackCaptor = ArgumentCaptor.forClass(NetworkCallback.class);
-        mEvCaptor = ArgumentCaptor.forClass(DnsEvent.class);
+        mDnsEvCaptor = ArgumentCaptor.forClass(DnsEvent.class);
         mNetdEventListenerService = new NetdEventListenerService(mCm, mLog);
 
         verify(mCm, times(1)).registerNetworkCallback(any(), mCallbackCaptor.capture());
     }
 
     @SmallTest
-    public void testOneBatch() throws Exception {
+    public void testOneDnsBatch() throws Exception {
         log(105, LATENCIES);
         log(106, Arrays.copyOf(LATENCIES, BATCH_SIZE - 1)); // one lookup short of a batch event
 
-        verifyLoggedEvents(new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES));
+        verifyLoggedDnsEvents(new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES));
 
         log(106, Arrays.copyOfRange(LATENCIES, BATCH_SIZE - 1, BATCH_SIZE));
 
-        mEvCaptor = ArgumentCaptor.forClass(DnsEvent.class); // reset argument captor
-        verifyLoggedEvents(
+        mDnsEvCaptor = ArgumentCaptor.forClass(DnsEvent.class); // reset argument captor
+        verifyLoggedDnsEvents(
             new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
             new DnsEvent(106, EVENT_TYPES, RETURN_CODES, LATENCIES));
     }
 
     @SmallTest
-    public void testSeveralBatches() throws Exception {
+    public void testSeveralDmsBatches() throws Exception {
         log(105, LATENCIES);
         log(106, LATENCIES);
         log(105, LATENCIES);
         log(107, LATENCIES);
 
-        verifyLoggedEvents(
+        verifyLoggedDnsEvents(
             new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
             new DnsEvent(106, EVENT_TYPES, RETURN_CODES, LATENCIES),
             new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
@@ -114,7 +120,7 @@
     }
 
     @SmallTest
-    public void testBatchAndNetworkLost() throws Exception {
+    public void testDnsBatchAndNetworkLost() throws Exception {
         byte[] eventTypes = Arrays.copyOf(EVENT_TYPES, 20);
         byte[] returnCodes = Arrays.copyOf(RETURN_CODES, 20);
         int[] latencies = Arrays.copyOf(LATENCIES, 20);
@@ -124,14 +130,14 @@
         mCallbackCaptor.getValue().onLost(new Network(105));
         log(105, LATENCIES);
 
-        verifyLoggedEvents(
+        verifyLoggedDnsEvents(
             new DnsEvent(105, eventTypes, returnCodes, latencies),
             new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
             new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES));
     }
 
     @SmallTest
-    public void testConcurrentBatchesAndDumps() throws Exception {
+    public void testConcurrentDnsBatchesAndDumps() throws Exception {
         final long stop = System.currentTimeMillis() + 100;
         final PrintWriter pw = new PrintWriter(new FileOutputStream("/dev/null"));
         new Thread() {
@@ -142,27 +148,121 @@
             }
         }.start();
 
-        logAsync(105, LATENCIES);
-        logAsync(106, LATENCIES);
-        logAsync(107, LATENCIES);
+        logDnsAsync(105, LATENCIES);
+        logDnsAsync(106, LATENCIES);
+        logDnsAsync(107, LATENCIES);
 
-        verifyLoggedEvents(500,
+        verifyLoggedDnsEvents(500,
             new DnsEvent(105, EVENT_TYPES, RETURN_CODES, LATENCIES),
             new DnsEvent(106, EVENT_TYPES, RETURN_CODES, LATENCIES),
             new DnsEvent(107, EVENT_TYPES, RETURN_CODES, LATENCIES));
     }
 
     @SmallTest
-    public void testConcurrentBatchesAndNetworkLoss() throws Exception {
-        logAsync(105, LATENCIES);
+    public void testConcurrentDnsBatchesAndNetworkLoss() throws Exception {
+        logDnsAsync(105, LATENCIES);
         Thread.sleep(10L);
-        // call onLost() asynchronously to logAsync's onDnsEvent() calls.
+        // call onLost() asynchronously to logDnsAsync's onDnsEvent() calls.
         mCallbackCaptor.getValue().onLost(new Network(105));
 
         // do not verify unpredictable batch
         verify(mLog, timeout(500).times(1)).log(any());
     }
 
+    @SmallTest
+    public void testConnectLogging() throws Exception {
+        final int OK = 0;
+        Thread[] logActions = {
+            // ignored
+            connectEventAction(OsConstants.EALREADY, 0, EXAMPLE_IPV4),
+            connectEventAction(OsConstants.EALREADY, 0, EXAMPLE_IPV6),
+            connectEventAction(OsConstants.EINPROGRESS, 0, EXAMPLE_IPV4),
+            connectEventAction(OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6),
+            connectEventAction(OsConstants.EINPROGRESS, 0, EXAMPLE_IPV6),
+            // valid latencies
+            connectEventAction(OK, 110, EXAMPLE_IPV4),
+            connectEventAction(OK, 23, EXAMPLE_IPV4),
+            connectEventAction(OK, 45, EXAMPLE_IPV4),
+            connectEventAction(OK, 56, EXAMPLE_IPV4),
+            connectEventAction(OK, 523, EXAMPLE_IPV6),
+            connectEventAction(OK, 214, EXAMPLE_IPV6),
+            connectEventAction(OK, 67, EXAMPLE_IPV6),
+            // errors
+            connectEventAction(OsConstants.EPERM, 0, EXAMPLE_IPV4),
+            connectEventAction(OsConstants.EPERM, 0, EXAMPLE_IPV4),
+            connectEventAction(OsConstants.EAGAIN, 0, EXAMPLE_IPV4),
+            connectEventAction(OsConstants.EACCES, 0, EXAMPLE_IPV4),
+            connectEventAction(OsConstants.EACCES, 0, EXAMPLE_IPV4),
+            connectEventAction(OsConstants.EACCES, 0, EXAMPLE_IPV6),
+            connectEventAction(OsConstants.EADDRINUSE, 0, EXAMPLE_IPV4),
+            connectEventAction(OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV4),
+            connectEventAction(OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6),
+            connectEventAction(OsConstants.ETIMEDOUT, 0, EXAMPLE_IPV6),
+            connectEventAction(OsConstants.ECONNREFUSED, 0, EXAMPLE_IPV4),
+        };
+
+        for (Thread t : logActions) {
+            t.start();
+        }
+        for (Thread t : logActions) {
+            t.join();
+        }
+
+        List<IpConnectivityEvent> events = new ArrayList<>();
+        mNetdEventListenerService.flushStatistics(events);
+
+        IpConnectivityEvent got = events.get(0);
+        String want = String.join("\n",
+                "time_ms: 0",
+                "transport: 0",
+                "connect_statistics <",
+                "  connect_count: 12",
+                "  errnos_counters <",
+                "    key: 1",
+                "    value: 2",
+                "  >",
+                "  errnos_counters <",
+                "    key: 11",
+                "    value: 1",
+                "  >",
+                "  errnos_counters <",
+                "    key: 13",
+                "    value: 3",
+                "  >",
+                "  errnos_counters <",
+                "    key: 98",
+                "    value: 1",
+                "  >",
+                "  errnos_counters <",
+                "    key: 110",
+                "    value: 3",
+                "  >",
+                "  errnos_counters <",
+                "    key: 111",
+                "    value: 1",
+                "  >",
+                "  ipv6_addr_count: 6",
+                "  latencies_ms: 23",
+                "  latencies_ms: 45",
+                "  latencies_ms: 56",
+                "  latencies_ms: 67",
+                "  latencies_ms: 110",
+                "  latencies_ms: 214",
+                "  latencies_ms: 523",
+                ">\n");
+        verifyConnectEvent(want, got);
+    }
+
+    Thread connectEventAction(int error, int latencyMs, String ipAddr) {
+        return new Thread(() -> {
+            try {
+                mNetdEventListenerService.onConnectEvent(100, error, latencyMs, ipAddr, 80, 1);
+            } catch (Exception e) {
+                fail(e.toString());
+            }
+        });
+    }
+
     void log(int netId, int[] latencies) {
         try {
             for (int l : latencies) {
@@ -174,7 +274,7 @@
         }
     }
 
-    void logAsync(int netId, int[] latencies) {
+    void logDnsAsync(int netId, int[] latencies) {
         new Thread() {
             public void run() {
                 log(netId, latencies);
@@ -182,15 +282,15 @@
         }.start();
     }
 
-    void verifyLoggedEvents(DnsEvent... expected) {
-        verifyLoggedEvents(0, expected);
+    void verifyLoggedDnsEvents(DnsEvent... expected) {
+        verifyLoggedDnsEvents(0, expected);
     }
 
-    void verifyLoggedEvents(int wait, DnsEvent... expectedEvents) {
-        verify(mLog, timeout(wait).times(expectedEvents.length)).log(mEvCaptor.capture());
-        for (DnsEvent got : mEvCaptor.getAllValues()) {
+    void verifyLoggedDnsEvents(int wait, DnsEvent... expectedEvents) {
+        verify(mLog, timeout(wait).times(expectedEvents.length)).log(mDnsEvCaptor.capture());
+        for (DnsEvent got : mDnsEvCaptor.getAllValues()) {
             OptionalInt index = IntStream.range(0, expectedEvents.length)
-                    .filter(i -> eventsEqual(expectedEvents[i], got))
+                    .filter(i -> dnsEventsEqual(expectedEvents[i], got))
                     .findFirst();
             // Don't match same expected event more than once.
             index.ifPresent(i -> expectedEvents[i] = null);
@@ -199,11 +299,22 @@
     }
 
     /** equality function for DnsEvent to avoid overriding equals() and hashCode(). */
-    static boolean eventsEqual(DnsEvent expected, DnsEvent got) {
+    static boolean dnsEventsEqual(DnsEvent expected, DnsEvent got) {
         return (expected == got) || ((expected != null) && (got != null)
                 && (expected.netId == got.netId)
                 && Arrays.equals(expected.eventTypes, got.eventTypes)
                 && Arrays.equals(expected.returnCodes, got.returnCodes)
                 && Arrays.equals(expected.latenciesMs, got.latenciesMs));
     }
+
+    static void verifyConnectEvent(String expected, IpConnectivityEvent got) {
+        try {
+            Arrays.sort(got.getConnectStatistics().latenciesMs);
+            Arrays.sort(got.getConnectStatistics().errnosCounters,
+                    Comparator.comparingInt((p) -> p.key));
+            assertEquals(expected, got.toString());
+        } catch (Exception e) {
+            fail(e.toString());
+        }
+    }
 }
diff --git a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
index 813e928..98073ce 100644
--- a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
@@ -48,8 +48,6 @@
 
 public class NetworkNotificationManagerTest extends TestCase {
 
-    static final String NOTIFICATION_ID = NetworkNotificationManager.NOTIFICATION_ID;
-
     static final NetworkCapabilities CELL_CAPABILITIES = new NetworkCapabilities();
     static final NetworkCapabilities WIFI_CAPABILITIES = new NetworkCapabilities();
     static {
@@ -108,11 +106,11 @@
         }
 
         for (int i = 0; i < ids.size(); i++) {
-            final int expectedId = NETWORK_ID_BASE + i;
-            verify(mNotificationManager, times(1))
-                    .notifyAsUser(eq(NOTIFICATION_ID), eq(expectedId), any(), any());
-            verify(mNotificationManager, times(1))
-                    .cancelAsUser(eq(NOTIFICATION_ID), eq(expectedId), any());
+            final int id = ids.get(i);
+            final int eventId = types.get(i).eventId;
+            final String tag = NetworkNotificationManager.tagFor(id);
+            verify(mNotificationManager, times(1)).notifyAsUser(eq(tag), eq(eventId), any(), any());
+            verify(mNotificationManager, times(1)).cancelAsUser(eq(tag), eq(eventId), any());
         }
     }
 
@@ -125,8 +123,9 @@
 
         mManager.showNotification(102, NO_INTERNET, mWifiNai, mCellNai, null, false);
 
-        verify(mNotificationManager, times(1))
-                .notifyAsUser(eq(NOTIFICATION_ID), eq(102), any(), any());
+        final int eventId = NO_INTERNET.eventId;
+        final String tag = NetworkNotificationManager.tagFor(102);
+        verify(mNotificationManager, times(1)).notifyAsUser(eq(tag), eq(eventId), any(), any());
     }
 
     @SmallTest
diff --git a/tools/incident_report/Android.mk b/tools/incident_report/Android.mk
new file mode 100644
index 0000000..ed89bd6
--- /dev/null
+++ b/tools/incident_report/Android.mk
@@ -0,0 +1,39 @@
+#
+# 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.
+#
+LOCAL_PATH:= $(call my-dir)
+
+# ==========================================================
+# Build the host executable: protoc-gen-javastream
+# ==========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := incident_report
+
+LOCAL_C_INCLUDES := \
+    external/protobuf/src
+
+LOCAL_SRC_FILES := \
+    generic_message.cpp \
+    main.cpp \
+    printer.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+    libplatformprotos \
+    libprotobuf-cpp-full
+
+include $(BUILD_HOST_EXECUTABLE)
+
+
diff --git a/tools/incident_report/formatter.cpp b/tools/incident_report/formatter.cpp
new file mode 100644
index 0000000..944348f
--- /dev/null
+++ b/tools/incident_report/formatter.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2016 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 "proto_format.h"
+
+#include <string.h>
+
+extern int const PROTO_FORMAT_STRING_POOL_SIZE;
+extern int const PROTO_FORMAT_ENUM_LABELS_LENGTH;
+extern int const PROTO_FORMAT_MESSAGES_LENGTH;
+extern int const PROTO_FORMAT_FIELDS_LENGTH;
+
+extern char const PROTO_FORMAT_STRING_POOL[];
+extern ProtoFieldFormat const PROTO_FORMAT_FIELDS[];
+extern ProtoEnumLabel const PROTO_FORMAT_ENUM_LABELS[];
+extern ProtoMessageFormat const PROTO_FORMAT_MESSAGES[];
+
+static const char*
+get_string(int index)
+{
+    if (index >= 0 && index < PROTO_FORMAT_STRING_POOL_SIZE) {
+        return PROTO_FORMAT_STRING_POOL + index;
+    } else {
+        // These indices all come from within the generated table, so just crash now.
+        *(int*)NULL = 42;
+        return NULL;
+    }
+}
+
+static ProtoMessageFormat const*
+get_message(int index)
+{
+    if (index >= 0 && index < PROTO_FORMAT_MESSAGES_LENGTH) {
+        return PROTO_FORMAT_MESSAGES + index;
+    } else {
+        // These indices all come from within the generated table, so just crash now.
+        *(int*)NULL = 42;
+        return NULL;
+    }
+}
+
+static int
+compare_name(const char* full, const char* package, const char* clazz)
+{
+    int const packageLen = strlen(package);
+    int cmp = strncmp(full, package, packageLen);
+    if (cmp == 0) {
+        cmp = full[packageLen] - '.';
+        if (cmp == 0) {
+            return strcmp(full + packageLen, clazz);
+        }
+    }
+    return cmp;
+}
+
+int
+find_message_index(const char* name)
+{
+    size_t low = 0;
+    size_t high = PROTO_FORMAT_FIELDS_LENGTH - 1;
+
+    while (low <= high) {
+        size_t mid = (low + high) >> 1;
+        ProtoMessageFormat const* msg = get_message(mid);
+
+        int cmp = compare_name(name, get_string(msg->package_name), get_string(msg->package_name));
+        if (cmp < 0) {
+            low = mid + 1;
+        } else if (cmp > 0) {
+            high = mid - 1;
+        } else {
+            return mid;
+        }
+    }
+    return -1;
+}
diff --git a/tools/incident_report/generic_message.cpp b/tools/incident_report/generic_message.cpp
new file mode 100644
index 0000000..84d9d7c
--- /dev/null
+++ b/tools/incident_report/generic_message.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2016 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 "generic_message.h"
+
+GenericMessage::GenericMessage()
+{
+}
+
+GenericMessage::~GenericMessage()
+{
+}
+
+void
+GenericMessage::addInt32(int32_t fieldId, uint32_t value)
+{
+    Node node;
+    node.type = TYPE_VALUE32;
+    node.value32 = value;
+    mNodes.insert(pair<int32_t,Node>(fieldId, node));
+}
+
+void
+GenericMessage::addInt64(int32_t fieldId, uint64_t value)
+{
+    Node node;
+    node.type = TYPE_VALUE64;
+    node.value64 = value;
+    mNodes.insert(pair<int32_t,Node>(fieldId, node));
+}
+
+GenericMessage*
+GenericMessage::addMessage(int32_t fieldId)
+{
+    GenericMessage* result = new GenericMessage();
+    Node node;
+    node.type = TYPE_MESSAGE;
+    node.message = result;
+    mNodes.insert(pair<int32_t,Node>(fieldId, node));
+    return result;
+}
+
+void
+GenericMessage::addString(int32_t fieldId, const string& value)
+{
+    Node node;
+    node.type = TYPE_STRING;
+    node.str = new string(value);
+    mNodes.insert(pair<int32_t,Node>(fieldId, node));
+}
+
+GenericMessage::const_iterator_pair
+GenericMessage::find(int fieldId) const
+{
+    return mNodes.equal_range(fieldId);
+}
+
diff --git a/tools/incident_report/generic_message.h b/tools/incident_report/generic_message.h
new file mode 100644
index 0000000..df3f7b2
--- /dev/null
+++ b/tools/incident_report/generic_message.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef GENERIC_MESSAGE_H
+#define GENERIC_MESSAGE_H
+
+#include <map>
+#include <string>
+
+using namespace std;
+
+/**
+ * Class to represent a protobuf Message, where we don't actually
+ * know what any of the fields are, just their type codes. In other
+ * words, this loslessly stores a parsed protobuf object without
+ * having the .proto file that generated it.
+ */
+class GenericMessage
+{
+public:
+    GenericMessage();
+    ~GenericMessage();
+
+    enum {
+        TYPE_VALUE32,
+        TYPE_VALUE64,
+        TYPE_MESSAGE,
+        TYPE_STRING,
+        TYPE_DATA
+    };
+
+    struct Node {
+        uint32_t type;
+        union {
+            uint32_t value32;
+            uint64_t value64;
+            GenericMessage* message;
+            string* str;
+            string* data;
+        };
+    };
+
+    void addInt32(int32_t fieldId, uint32_t value);
+    void addInt64(int32_t fieldId, uint64_t value);
+    GenericMessage* addMessage(int32_t fieldId);
+    void addString(int32_t fieldId, const string& value);
+
+    typedef multimap<int32_t,Node>::const_iterator const_iterator;
+    typedef pair<const_iterator,const_iterator> const_iterator_pair;
+
+    const_iterator_pair find(int fieldId) const;
+
+private:
+    multimap<int,Node> mNodes;
+};
+
+#endif // GENERIC_MESSAGE_H
+
diff --git a/tools/incident_report/main.cpp b/tools/incident_report/main.cpp
new file mode 100644
index 0000000..a814847
--- /dev/null
+++ b/tools/incident_report/main.cpp
@@ -0,0 +1,576 @@
+/*
+ * Copyright (C) 2016 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 "generic_message.h"
+#include "printer.h"
+
+#include <frameworks/base/core/proto/android/os/incident_proto.pb.h>
+#include <google/protobuf/wire_format.h>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+using namespace android::os;
+using namespace google::protobuf;
+using namespace google::protobuf::io;
+using namespace google::protobuf::internal;
+
+static bool read_message(CodedInputStream* in, Descriptor const* descriptor,
+        GenericMessage* message);
+static void print_message(Out* out, Descriptor const* descriptor, GenericMessage const* message);
+
+// ================================================================================
+static bool
+read_length_delimited(CodedInputStream* in, uint32 fieldId, Descriptor const* descriptor,
+        GenericMessage* message)
+{
+    uint32 size;
+    if (!in->ReadVarint32(&size)) {
+        return false;
+    }
+
+    FieldDescriptor const* field = descriptor->FindFieldByNumber(fieldId);
+    if (field != NULL) {
+        int type = field->type();
+        if (type == FieldDescriptor::TYPE_MESSAGE) {
+            GenericMessage* child = message->addMessage(fieldId);
+
+            CodedInputStream::Limit limit = in->PushLimit(size);
+            bool rv = read_message(in, field->message_type(), child);
+            in->PopLimit(limit);
+            return rv;
+        } else if (type == FieldDescriptor::TYPE_STRING) {
+            // TODO: do a version of readstring that just pumps the data
+            // rather than allocating a string which we don't care about.
+            string str;
+            if (in->ReadString(&str, size)) {
+                message->addString(fieldId, str);
+                return true;
+            } else {
+                return false;
+            }
+        } else if (type == FieldDescriptor::TYPE_BYTES) {
+            // TODO: Save bytes field.
+            return in->Skip(size);
+        }
+    }
+    return in->Skip(size);
+}
+
+// ================================================================================
+static bool
+read_message(CodedInputStream* in, Descriptor const* descriptor, GenericMessage* message)
+{
+    uint32 value32;
+    uint64 value64;
+
+    while (true) {
+        uint32 tag = in->ReadTag();
+        if (tag == 0) {
+            return true;
+        }
+        int fieldId = WireFormatLite::GetTagFieldNumber(tag);
+        switch (WireFormatLite::GetTagWireType(tag)) {
+            case WireFormatLite::WIRETYPE_VARINT:
+                if (in->ReadVarint64(&value64)) {
+                    message->addInt64(fieldId, value64);
+                    break;
+                } else {
+                    return false;
+                }
+            case WireFormatLite::WIRETYPE_FIXED64:
+                if (in->ReadLittleEndian64(&value64)) {
+                    message->addInt64(fieldId, value64);
+                    break;
+                } else {
+                    return false;
+                }
+            case WireFormatLite::WIRETYPE_LENGTH_DELIMITED:
+                if (!read_length_delimited(in, fieldId, descriptor, message)) {
+                    return false;
+                }
+                break;
+            case WireFormatLite::WIRETYPE_FIXED32:
+                if (in->ReadLittleEndian32(&value32)) {
+                    message->addInt32(fieldId, value32);
+                    break;
+                } else {
+                    return false;
+                }
+            default:
+                fprintf(stderr, "bad tag: 0x%x (%d) at index %d\n", tag, tag,
+                        in->CurrentPosition());
+                return false;
+        }
+    }
+}
+
+// ================================================================================
+static void
+print_value(Out* out, FieldDescriptor const* field, GenericMessage::Node const& node)
+{
+    uint32_t val32;
+    FieldDescriptor::Type type = field->type();
+
+    switch (node.type) {
+        case GenericMessage::TYPE_VALUE32:
+            switch (type) {
+                case FieldDescriptor::TYPE_FIXED32:
+                    out->printf("%u", node.value32);
+                    break;
+                case FieldDescriptor::TYPE_SFIXED32:
+                    out->printf("%d", node.value32);
+                    break;
+                case FieldDescriptor::TYPE_FLOAT:
+                    out->printf("%f", *(float*)&node.value32);
+                    break;
+                default:
+                    out->printf("(unexpected value %d (0x%x)", node.value32, node.value32);
+                    break;
+            }
+            break;
+        case GenericMessage::TYPE_VALUE64:
+            switch (type) {
+                case FieldDescriptor::TYPE_FIXED64:
+                case FieldDescriptor::TYPE_SFIXED64:
+                case FieldDescriptor::TYPE_DOUBLE:
+                    out->printf("%f", *(double*)&node.value64);
+                    break;
+                case FieldDescriptor::TYPE_SINT32:
+                case FieldDescriptor::TYPE_INT32:
+                    val32 = (uint32_t)node.value32;
+                    out->printf("%d", val32);
+                    break;
+                case FieldDescriptor::TYPE_INT64:
+                case FieldDescriptor::TYPE_UINT32:
+                    val32 = (uint32_t)node.value32;
+                    out->printf("%u", val32);
+                    break;
+                case FieldDescriptor::TYPE_UINT64:
+                case FieldDescriptor::TYPE_SINT64:
+                case FieldDescriptor::TYPE_BOOL:
+                    if (node.value64) {
+                        out->printf("true");
+                    } else {
+                        out->printf("false");
+                    }
+                    break;
+                case FieldDescriptor::TYPE_ENUM:
+                default:
+                    out->printf("(unexpected value %ld (0x%x))", node.value64, node.value64);
+                    break;
+            }
+            break;
+        case GenericMessage::TYPE_MESSAGE:
+            print_message(out, field->message_type(), node.message);
+            break;
+        case GenericMessage::TYPE_STRING:
+            // TODO: custom format for multi-line strings.
+            out->printf("%s", node.str->c_str());
+            break;
+        case GenericMessage::TYPE_DATA:
+            out->printf("<bytes>");
+            break;
+    }
+}
+
+static void
+print_message(Out* out, Descriptor const* descriptor, GenericMessage const* message)
+{
+    out->printf("%s {\n", descriptor->name().c_str());
+    out->indent();
+
+    int const N = descriptor->field_count();
+    for (int i=0; i<N; i++) {
+        FieldDescriptor const* field = descriptor->field(i);
+
+        int fieldId = field->number();
+        bool repeated = field->label() == FieldDescriptor::LABEL_REPEATED;
+        FieldDescriptor::Type type = field->type();
+        GenericMessage::const_iterator_pair it = message->find(fieldId);
+
+        out->printf("%s=", field->name().c_str());
+        if (repeated) {
+            if (it.first != it.second) {
+                out->printf("[");
+                if (type == FieldDescriptor::TYPE_MESSAGE
+                        || type == FieldDescriptor::TYPE_STRING
+                        || type == FieldDescriptor::TYPE_BYTES) {
+                    out->printf("\n");
+                }
+                out->indent();
+
+                for (GenericMessage::const_iterator_pair it = message->find(fieldId);
+                        it.first != it.second; it.first++) {
+                    print_value(out, field, it.first->second);
+                    if (type == FieldDescriptor::TYPE_MESSAGE
+                            || type == FieldDescriptor::TYPE_STRING
+                            || type == FieldDescriptor::TYPE_BYTES) {
+                        out->printf("\n");
+                    }
+                }
+
+                out->dedent();
+                out->printf("]");
+            } else {
+                out->printf("[]");
+            }
+        } else {
+            if (it.first != it.second) {
+                print_value(out, field, it.first->second);
+            } else {
+                switch (type) {
+                    case FieldDescriptor::TYPE_BOOL:
+                        out->printf("false");
+                        break;
+                    case FieldDescriptor::TYPE_STRING:
+                    case FieldDescriptor::TYPE_MESSAGE:
+                        out->printf("");
+                        break;
+                    case FieldDescriptor::TYPE_ENUM:
+                        out->printf("%s", field->default_value_enum()->name().c_str());
+                        break;
+                    default:
+                        out->printf("0");
+                        break;
+                }
+            }
+        }
+        out->printf("\n");
+    }
+    out->dedent();
+    out->printf("}");
+}
+
+// ================================================================================
+static uint8_t*
+write_raw_varint(uint8_t* buf, uint32_t val)
+{
+    uint8_t* p = buf;
+    while (true) {
+        if ((val & ~0x7F) == 0) {
+            *p++ = (uint8_t)val;
+            return p;
+        } else {
+            *p++ = (uint8_t)((val & 0x7F) | 0x80);
+            val >>= 7;
+        }
+    }
+}
+
+static int
+write_all(int fd, uint8_t const* buf, size_t size)
+{
+    while (size > 0) {
+        ssize_t amt = ::write(fd, buf, size);
+        if (amt < 0) {
+            return errno;
+        }
+        size -= amt;
+        buf += amt;
+    }
+    return 0;
+}
+
+static int
+adb_incident_workaround(const char* adbSerial, const vector<string>& sections)
+{
+    const int maxAllowedSize = 20 * 1024 * 1024; // 20MB
+    uint8_t* buffer = (uint8_t*)malloc(maxAllowedSize);
+
+    for (vector<string>::const_iterator it=sections.begin(); it!=sections.end(); it++) {
+        Descriptor const* descriptor = IncidentProto::descriptor();
+        FieldDescriptor const* field;
+
+        // Get the name and field id.
+        string name = *it;
+        char* end;
+        int id = strtol(name.c_str(), &end, 0);
+        if (*end == '\0') {
+            // If it's an id, find out the string.
+            field = descriptor->FindFieldByNumber(id);
+            if (field == NULL) {
+                fprintf(stderr, "Unable to find field number: %d\n", id);
+                return 1;
+            }
+            name = field->name();
+        } else {
+            // If it's a string, find out the id.
+            field = descriptor->FindFieldByName(name);
+            if (field == NULL) {
+                fprintf(stderr, "Unable to find field: %s\n", name.c_str());
+                return 1;
+            }
+            id = field->number();
+        }
+        
+        int pfd[2];
+        if (pipe(pfd) != 0) {
+            fprintf(stderr, "pipe failed: %s\n", strerror(errno));
+            return 1;
+        }
+
+        pid_t pid = fork();
+        if (pid == -1) {
+            fprintf(stderr, "fork failed: %s\n", strerror(errno));
+            return 1;
+        } else if (pid == 0) {
+            // child
+            dup2(pfd[1], STDOUT_FILENO);
+            close(pfd[0]);
+            close(pfd[1]);
+
+            char const** args = (char const**)malloc(sizeof(char*) * 8);
+            int argpos = 0;
+            args[argpos++] = "adb";
+            if (adbSerial != NULL) {
+                args[argpos++] = "-s";
+                args[argpos++] = adbSerial;
+            }
+            args[argpos++] = "shell";
+            args[argpos++] = "dumpsys";
+            args[argpos++] = name.c_str();
+            args[argpos++] = "--proto";
+            args[argpos++] = NULL;
+            execvp(args[0], (char*const*)args);
+            fprintf(stderr, "execvp failed: %s\n", strerror(errno));
+            return 1;
+        } else {
+            // parent
+            close(pfd[1]);
+
+            size_t size = 0;
+            while (size < maxAllowedSize) {
+                ssize_t amt = read(pfd[0], buffer + size, maxAllowedSize - size);
+                if (amt == 0) {
+                    break;
+                } else if (amt == -1) {
+                    fprintf(stderr, "read error: %s\n", strerror(errno));
+                    return 1;
+                }
+                size += amt;
+            }
+
+            int status;
+            do {
+                waitpid(pid, &status, 0);
+            } while (!WIFEXITED(status));
+            if (WEXITSTATUS(status) != 0) {
+                return WEXITSTATUS(status);
+            }
+
+            if (size > 0) {
+                uint8_t header[20];
+                uint8_t* p = write_raw_varint(header, (id << 3) | 2);
+                p = write_raw_varint(p, size);
+                int err = write_all(STDOUT_FILENO, header, p-header);
+                if (err != 0) {
+                    fprintf(stderr, "write error: %s\n", strerror(err));
+                    return 1;
+                }
+                err = write_all(STDOUT_FILENO, buffer, size);
+                if (err != 0) {
+                    fprintf(stderr, "write error: %s\n", strerror(err));
+                    return 1;
+                }
+            }
+
+            close(pfd[0]);
+        }
+    }
+
+    return 0;
+}
+
+// ================================================================================
+static void
+usage(FILE* out)
+{
+    fprintf(out, "usage: incident_report -i INPUT [-o OUTPUT]\n");
+    fprintf(out, "\n");
+    fprintf(out, "Pretty-prints an incident report protobuf file.\n");
+    fprintf(out, "  -i INPUT    the input file. INPUT may be '-' to use stdin\n");
+    fprintf(out, "  -o OUTPUT   the output file. OUTPUT may be '-' or omitted to use stdout\n");
+    fprintf(out, "\n");
+    fprintf(out, "\n");
+    fprintf(out, "usage: incident_report [-o OUTPUT] [-t|b] [-s SERIAL] [SECTION...]\n");
+    fprintf(out, "\n");
+    fprintf(out, "Take an incident report over adb (which must be in the PATH).\n");
+    fprintf(out, "  -b          output the incident report raw protobuf format\n");
+    fprintf(out, "  -o OUTPUT   the output file. OUTPUT may be '-' or omitted to use stdout\n");
+    fprintf(out, "  -s SERIAL   sent to adb to choose which device, instead of $ANDROID_SERIAL\n");
+    fprintf(out, "  -t          output the incident report in pretty-printed text format\n");
+    fprintf(out, "\n");
+    fprintf(out, "  SECTION     which bugreport sections to print, either the int code of the\n");
+    fprintf(out, "              section in the Incident proto or the field name.  If ommited,\n");
+    fprintf(out, "              the report will contain all fields\n");
+    fprintf(out, "\n");
+}
+
+int
+main(int argc, char** argv)
+{
+    enum { OUTPUT_TEXT, OUTPUT_PROTO } outputFormat = OUTPUT_TEXT;
+    const char* inFilename = NULL;
+    const char* outFilename = NULL;
+    const char* adbSerial = NULL;
+    bool adbIncidentWorkaround = true;
+    pid_t childPid = -1;
+    vector<string> sections;
+
+    int opt;
+    while ((opt = getopt(argc, argv, "bhi:o:s:tw")) != -1) {
+        switch (opt) {
+            case 'b':
+                outputFormat = OUTPUT_PROTO;
+                break;
+            case 'i':
+                inFilename = optarg;
+                break;
+            case 'o':
+                outFilename = optarg;
+                break;
+            case 's':
+                adbSerial = optarg;
+                break;
+            case 't':
+                outputFormat = OUTPUT_TEXT;
+                break;
+            case 'h':
+                usage(stdout);
+                return 0;
+            case 'w':
+                adbIncidentWorkaround = false;
+                break;
+            default:
+                usage(stderr);
+                return 1;
+        }
+    }
+
+    while (optind < argc) {
+        sections.push_back(argv[optind++]);
+    }
+
+    int inFd;
+    if (inFilename != NULL) {
+        // translate-only mode - oepn the file or use stdin.
+        if (strcmp("-", inFilename) == 0) {
+            inFd = STDIN_FILENO;
+        } else {
+            inFd = open(inFilename, O_RDONLY | O_CLOEXEC);
+            if (inFd < 0) {
+                fprintf(stderr, "unable to open file for read (%s): %s\n", strerror(errno),
+                        inFilename);
+                return 1;
+            }
+        }
+    } else {
+        // pipe mode - run adb shell incident ...
+        int pfd[2];
+        if (pipe(pfd) != 0) {
+            fprintf(stderr, "pipe failed: %s\n", strerror(errno));
+            return 1;
+        }
+
+        childPid = fork();
+        if (childPid == -1) {
+            fprintf(stderr, "fork failed: %s\n", strerror(errno));
+            return 1;
+        } else if (childPid == 0) {
+            dup2(pfd[1], STDOUT_FILENO);
+            close(pfd[0]);
+            close(pfd[1]);
+            // child
+            if (adbIncidentWorkaround) {
+                // TODO: Until the device side incident command is checked in,
+                // the incident_report builds the outer Incident proto by hand
+                // from individual adb shell dumpsys <service> --proto calls,
+                // with a maximum allowed output size.
+                return adb_incident_workaround(adbSerial, sections);
+            }
+
+            // TODO: This is what the real implementation will be...
+            char const** args = (char const**)malloc(sizeof(char*) * (6 + sections.size()));
+            int argpos = 0;
+            args[argpos++] = "adb";
+            if (adbSerial != NULL) {
+                args[argpos++] = "-s";
+                args[argpos++] = adbSerial;
+            }
+            args[argpos++] = "shell";
+            args[argpos++] = "incident";
+            for (vector<string>::const_iterator it=sections.begin(); it!=sections.end(); it++) {
+                args[argpos++] = it->c_str();
+            }
+            args[argpos++] = NULL;
+            execvp(args[0], (char*const*)args);
+            fprintf(stderr, "execvp failed: %s\n", strerror(errno));
+            return 0;
+        } else {
+            // parent
+            inFd = pfd[0];
+            close(pfd[1]);
+        }
+    }
+
+    int outFd;
+    if (outFilename == NULL || strcmp("-", outFilename) == 0) {
+        outFd = STDOUT_FILENO;
+    } else {
+        outFd = open(outFilename, O_CREAT | O_RDWR, 0666);
+        if (outFd < 0) {
+            fprintf(stderr, "unable to open file for write: %s\n", outFilename);
+            return 1;
+        }
+    }
+
+    GenericMessage message;
+
+    Descriptor const* descriptor = IncidentProto::descriptor();
+    FileInputStream infile(inFd);
+    CodedInputStream in(&infile);
+
+    if (!read_message(&in, descriptor, &message)) {
+        fprintf(stderr, "unable to read incident\n");
+        return 1;
+    }
+
+    Out out(outFd);
+
+    print_message(&out, descriptor, &message);
+    out.printf("\n");
+
+    if (childPid != -1) {
+        int status;
+        do {
+            waitpid(childPid, &status, 0);
+        } while (!WIFEXITED(status));
+        if (WEXITSTATUS(status) != 0) {
+            return WEXITSTATUS(status);
+        }
+    }
+
+    return 0;
+}
diff --git a/tools/incident_report/printer.cpp b/tools/incident_report/printer.cpp
new file mode 100644
index 0000000..8111b27
--- /dev/null
+++ b/tools/incident_report/printer.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2016 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 "printer.h"
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#define INITIAL_BUF_SIZE (16*1024)
+
+char const* SPACES = "                                                            ";
+const int SPACE_COUNT = strlen(SPACES);
+
+Out::Out(int fd)
+    :mOut(fd == STDOUT_FILENO ? stdout : fdopen(fd, "w")),
+     mBufSize(INITIAL_BUF_SIZE),
+     mBuf((char*)malloc(INITIAL_BUF_SIZE)),
+     mIndent(0),
+     mPendingIndent(false)
+{
+}
+
+Out::~Out()
+{
+    fclose(mOut);
+}
+
+int
+Out::reallocate(int size)
+{
+    if (size > mBufSize) {
+        char* p = (char*)malloc(size);
+        if (p != NULL) {
+            free(mBuf);
+            mBufSize = size;
+            mBuf = p;
+            return size;
+        }
+    }
+    return mBufSize;
+}
+
+void
+Out::printf(const char* format, ...)
+{
+    if (mPendingIndent) {
+        print_indent();
+        mPendingIndent = false;
+    }
+
+    int len;
+
+    va_list args;
+    va_start(args, format);
+
+    len = vsnprintf(mBuf, mBufSize, format, args);
+    bool truncated = (len >= mBufSize) && (reallocate(len) < len);
+    
+    len = vsnprintf(mBuf, mBufSize, format, args);
+    va_end(args);
+
+    if (len > 0) {
+        if (mIndent == 0) {
+            fwrite(mBuf, len, 1, mOut);
+        } else {
+            char* last = mBuf;
+            char* p;
+            do {
+                p = strchr(last, '\n');
+                int size = p != NULL ? p - last + 1 : strlen(last);
+                fwrite(last, size, 1, mOut);
+                if (p != NULL) {
+                    if (p[1] == '\0') {
+                        mPendingIndent = true;
+                    } else {
+                        print_indent();
+                    }
+                }
+                last = p+1;
+            } while (p != NULL);
+        }
+    }
+}
+
+void
+Out::indent()
+{
+    mPendingIndent = true;
+    mIndent += 2;
+}
+
+void
+Out::dedent()
+{
+    if (mIndent > 0) {
+        mIndent -= 2;
+    }
+}
+
+void
+Out::print_indent()
+{
+#if 0
+    fprintf(mOut, "[%d]", mIndent);
+#else
+    int indent = mIndent;
+    while (indent > SPACE_COUNT) {
+        fwrite(SPACES, SPACE_COUNT, 1, mOut);
+        indent -= SPACE_COUNT;
+    }
+    fwrite(SPACES + SPACE_COUNT - indent, indent, 1, mOut);
+#endif
+}
diff --git a/tools/incident_report/printer.h b/tools/incident_report/printer.h
new file mode 100644
index 0000000..ed93fa1
--- /dev/null
+++ b/tools/incident_report/printer.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef PRINTER_H
+#define PRINTER_H
+
+#include <stdio.h>
+
+class Out
+{
+public:
+    Out(int fd);
+    ~Out();
+
+    void printf(const char* format, ...);
+
+    void indent();
+    void dedent();
+
+private:
+    FILE* mOut;
+    int mBufSize;
+    char* mBuf;
+    int mIndent;
+    bool mPendingIndent;
+
+    int reallocate(int size);
+    void print_indent();
+};
+
+#endif // PRINTER_H
diff --git a/tools/incident_section_gen/Android.mk b/tools/incident_section_gen/Android.mk
new file mode 100644
index 0000000..acf3f83
--- /dev/null
+++ b/tools/incident_section_gen/Android.mk
@@ -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.
+#
+LOCAL_PATH:= $(call my-dir)
+
+# ==========================================================
+# Build the host executable: protoc-gen-javastream
+# ==========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := incident-section-gen
+LOCAL_CFLAGS += -g -O0
+LOCAL_C_INCLUDES := \
+    external/protobuf/src
+LOCAL_SRC_FILES := \
+    main.cpp
+LOCAL_LDFLAGS := -ldl
+LOCAL_SHARED_LIBRARIES := \
+    libplatformprotos \
+    libprotobuf-cpp-full
+
+include $(BUILD_HOST_EXECUTABLE)
+
diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp
new file mode 100644
index 0000000..d004810
--- /dev/null
+++ b/tools/incident_section_gen/main.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2016 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 <frameworks/base/core/proto/android/os/incident_proto.pb.h>
+
+
+#include <map>
+
+using namespace android::os;
+using namespace google::protobuf;
+using namespace google::protobuf::io;
+using namespace google::protobuf::internal;
+using namespace std;
+
+int
+main(int, const char**)
+{
+    map<string,FieldDescriptor const*> sections;
+    int N;
+
+    printf("// Auto generated file. Do not modify\n");
+    printf("\n");
+    printf("#include \"incident_sections.h\"\n");
+    printf("\n");
+
+    Descriptor const* descriptor = IncidentProto::descriptor();
+    N = descriptor->field_count();
+    for (int i=0; i<N; i++) {
+        const FieldDescriptor* field = descriptor->field(i);
+        if (field->type() == FieldDescriptor::TYPE_MESSAGE) {
+            sections[field->name()] = field;
+        }
+    }
+
+    printf("IncidentSection const INCIDENT_SECTIONS[] = {\n");
+    N = sections.size();
+    int i = 0;
+    for (map<string,FieldDescriptor const*>::const_iterator it = sections.begin();
+            it != sections.end(); it++, i++) {
+        const FieldDescriptor* field = it->second;
+        printf("    { %d, \"%s\" }", field->number(), field->name().c_str());
+        if (i != N-1) {
+            printf(",\n");
+        } else {
+            printf("\n");
+        }
+    }
+    printf("};\n");
+
+    printf("const int INCIDENT_SECTION_COUNT = %d;\n", N);
+
+    return 0;
+}