Merge "Fix ViewCaptureTest, Bitmap.sameAs"
diff --git a/Android.mk b/Android.mk
index 9d1b0cc..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 += \
@@ -234,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 \
@@ -528,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
@@ -1385,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/api/current.txt b/api/current.txt
index dddb58b..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();
@@ -11751,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);
@@ -11815,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;
   }
 
@@ -12174,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 {
@@ -12709,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
@@ -37459,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";
@@ -37996,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();
@@ -38080,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
@@ -61397,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 cfb20ee..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);
@@ -5715,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();
@@ -12244,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);
@@ -12308,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;
   }
 
@@ -12667,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 {
@@ -13202,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
@@ -31716,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();
@@ -40519,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";
@@ -41094,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();
@@ -41209,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
@@ -64899,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 5e1c8ab..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();
@@ -11782,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);
@@ -11846,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;
   }
 
@@ -12205,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 {
@@ -12740,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
@@ -29289,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();
@@ -37556,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";
@@ -38093,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();
@@ -38177,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
@@ -61686,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 f04455b..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;
     }
 
     /**
@@ -1012,6 +1012,14 @@
     }
 
     /**
+     * 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/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/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 5943433..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;
             }
@@ -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) {
@@ -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/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java
index c456288..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.
@@ -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/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/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/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index d7c3722..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);
     }
 
     /**
@@ -3780,6 +3789,8 @@
                     ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE;
         }
 
+        final boolean hasVisibleToEphemeral =
+                sa.hasValue(R.styleable.AndroidManifestActivity_visibleToEphemeral);
         final boolean isEphemeral = ((flags & PARSE_IS_EPHEMERAL) != 0);
         final boolean visibleToEphemeral = isEphemeral
                 || sa.getBoolean(R.styleable.AndroidManifestActivity_visibleToEphemeral, false);
@@ -3818,7 +3829,7 @@
                     return null;
                 }
                 intent.setEphemeral(isEphemeral);
-                intent.setVisibleToEphemeral(visibleToEphemeral);
+                intent.setVisibleToEphemeral(visibleToEphemeral || isWebBrowsableIntent(intent));
                 if (intent.countActions() == 0) {
                     Slog.w(TAG, "No actions in intent filter at "
                             + mArchiveSourcePath + " "
@@ -3826,6 +3837,10 @@
                 } else {
                     a.intents.add(intent);
                 }
+                // adjust activity flags when we implicitly expose it via a browsable filter
+                if (!hasVisibleToEphemeral && intent.isVisibleToEphemeral()) {
+                    a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL;
+                }
             } else if (!receiver && parser.getName().equals("preferred")) {
                 ActivityIntentInfo intent = new ActivityIntentInfo(a);
                 if (!parseIntent(res, parser, false /*allowGlobs*/, false /*allowAutoVerify*/,
@@ -3833,7 +3848,7 @@
                     return null;
                 }
                 intent.setEphemeral(isEphemeral);
-                intent.setVisibleToEphemeral(visibleToEphemeral);
+                intent.setVisibleToEphemeral(visibleToEphemeral || isWebBrowsableIntent(intent));
                 if (intent.countActions() == 0) {
                     Slog.w(TAG, "No actions in preferred at "
                             + mArchiveSourcePath + " "
@@ -3844,6 +3859,10 @@
                     }
                     owner.preferredActivityFilters.add(intent);
                 }
+                // adjust activity flags when we implicitly expose it via a browsable filter
+                if (!hasVisibleToEphemeral && intent.isVisibleToEphemeral()) {
+                    a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL;
+                }
             } else if (parser.getName().equals("meta-data")) {
                 if ((a.metaData = parseMetaData(res, parser, a.metaData,
                         outError)) == null) {
@@ -4084,6 +4103,7 @@
             }
         }
 
+        // TODO add visibleToInstantApp attribute to activity alias
         final boolean isEphemeral = ((flags & PARSE_IS_EPHEMERAL) != 0);
         final boolean visibleToEphemeral = isEphemeral
                 || ((a.info.flags & ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL) != 0);
@@ -4115,9 +4135,14 @@
                             + parser.getPositionDescription());
                 } else {
                     intent.setEphemeral(isEphemeral);
-                    intent.setVisibleToEphemeral(visibleToEphemeral);
+                    intent.setVisibleToEphemeral(visibleToEphemeral
+                            || isWebBrowsableIntent(intent));
                     a.intents.add(intent);
                 }
+                // adjust activity flags when we implicitly expose it via a browsable filter
+                if (intent.isVisibleToEphemeral()) {
+                    a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL;
+                }
             } else if (parser.getName().equals("meta-data")) {
                 if ((a.metaData=parseMetaData(res, parser, a.metaData,
                         outError)) == null) {
@@ -4253,6 +4278,8 @@
                     ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE;
         }
 
+        final boolean hasVisibleToEphemeral =
+                sa.hasValue(R.styleable.AndroidManifestProvider_visibleToEphemeral);
         final boolean isEphemeral = ((flags & PARSE_IS_EPHEMERAL) != 0);
         final boolean visibleToEphemeral = isEphemeral
                 || sa.getBoolean(R.styleable.AndroidManifestProvider_visibleToEphemeral, false);
@@ -4282,7 +4309,8 @@
         }
         p.info.authority = cpname.intern();
 
-        if (!parseProviderTags(res, parser, isEphemeral, visibleToEphemeral, p, outError)) {
+        if (!parseProviderTags(
+                res, parser, isEphemeral, hasVisibleToEphemeral, visibleToEphemeral, p, outError)) {
             return null;
         }
 
@@ -4290,8 +4318,9 @@
     }
 
     private boolean parseProviderTags(Resources res, XmlResourceParser parser,
-            boolean isEphemeral, boolean visibleToEphemeral, Provider outInfo, String[] outError)
-            throws XmlPullParserException, IOException {
+            boolean isEphemeral, boolean hasVisibleToEphemeral, boolean visibleToEphemeral,
+            Provider outInfo, String[] outError)
+                    throws XmlPullParserException, IOException {
         int outerDepth = parser.getDepth();
         int type;
         while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -4308,8 +4337,12 @@
                     return false;
                 }
                 intent.setEphemeral(isEphemeral);
-                intent.setVisibleToEphemeral(visibleToEphemeral);
+                intent.setVisibleToEphemeral(visibleToEphemeral || isWebBrowsableIntent(intent));
                 outInfo.intents.add(intent);
+                // adjust provider flags when we implicitly expose it via a browsable filter
+                if (!hasVisibleToEphemeral && intent.isVisibleToEphemeral()) {
+                    outInfo.info.flags |= ProviderInfo.FLAG_VISIBLE_TO_EPHEMERAL;
+                }
 
             } else if (parser.getName().equals("meta-data")) {
                 if ((outInfo.metaData=parseMetaData(res, parser,
@@ -4556,6 +4589,8 @@
                     ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE;
         }
 
+        final boolean hasVisibleToEphemeral =
+                sa.hasValue(R.styleable.AndroidManifestService_visibleToEphemeral);
         final boolean isEphemeral = ((flags & PARSE_IS_EPHEMERAL) != 0);
         final boolean visibleToEphemeral = isEphemeral
                 || sa.getBoolean(R.styleable.AndroidManifestService_visibleToEphemeral, false);
@@ -4591,8 +4626,11 @@
                     return null;
                 }
                 intent.setEphemeral(isEphemeral);
-                intent.setVisibleToEphemeral(visibleToEphemeral);
-
+                intent.setVisibleToEphemeral(visibleToEphemeral || isWebBrowsableIntent(intent));
+                // adjust activity flags when we implicitly expose it via a browsable filter
+                if (!hasVisibleToEphemeral && intent.isVisibleToEphemeral()) {
+                    s.info.flags |= ServiceInfo.FLAG_VISIBLE_TO_EPHEMERAL;
+                }
                 s.intents.add(intent);
             } else if (parser.getName().equals("meta-data")) {
                 if ((s.metaData=parseMetaData(res, parser, s.metaData,
@@ -4620,6 +4658,12 @@
         return s;
     }
 
+    private boolean isWebBrowsableIntent(IntentInfo intent) {
+        return intent.hasAction(Intent.ACTION_VIEW)
+                && intent.hasCategory(Intent.CATEGORY_BROWSABLE)
+                && (intent.hasDataScheme("http") || intent.hasDataScheme("https"));
+    }
+
     private boolean parseAllMetaData(Resources res, XmlResourceParser parser, String tag,
             Component<?> outInfo, String[] outError) throws XmlPullParserException, IOException {
         int outerDepth = parser.getDepth();
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/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/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/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/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/ViewGroup.java b/core/java/android/view/ViewGroup.java
index c83298b..7340cf7 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -869,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);
@@ -1104,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();
@@ -3026,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/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/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/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 d33d20b..80711aa 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));
@@ -790,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 &&
@@ -800,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;
     }
 
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 18ed0ed..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");
     }
@@ -399,7 +399,8 @@
 
     // We always decode to sRGB, but only mark the bitmap with a color space if linear
     // blending is enabled.
-    SkImageInfo bitmapInfo = decodeInfo.makeColorSpace(GraphicsJNI::defaultColorSpace());
+    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 d8984d3..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
     };
 
@@ -458,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 03dc1fb..508c9ff 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -95,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_view_GraphicBuffer.cpp b/core/jni/android_view_GraphicBuffer.cpp
index b6b5245..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:
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 81b0fcb..d75d5c1 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -98,12 +98,12 @@
 // DisplayList view properties
 // ----------------------------------------------------------------------------
 
-static void android_view_RenderNode_output(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(jlong renderNodePtr) {
+static jint android_view_RenderNode_getDebugSize(JNIEnv* env, jobject clazz, jlong renderNodePtr) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     return renderNode->getDebugSize();
 }
@@ -472,13 +472,15 @@
 // RenderProperties - Animations
 // ----------------------------------------------------------------------------
 
-static void android_view_RenderNode_addAnimator(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);
 }
 
-static void android_view_RenderNode_endAllAnimators(jlong renderNodePtr) {
+static void android_view_RenderNode_endAllAnimators(JNIEnv* env, jobject clazz,
+        jlong renderNodePtr) {
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     renderNode->animators().endAllStagingAnimators();
 }
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 92693b7..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;
     }
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 3c8db7f..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;
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/res/values/config.xml b/core/res/res/values/config.xml
index 69c7b60..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
@@ -2613,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>
 
@@ -2690,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 10badab..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>
@@ -2377,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 dcb44d3..7c50f62 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -494,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" />
@@ -733,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" />
@@ -1125,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" />
@@ -1132,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" />
@@ -2754,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/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/OpenGLReadback.cpp b/libs/hwui/OpenGLReadback.cpp
index 4c8abc5..938b6ef 100644
--- a/libs/hwui/OpenGLReadback.cpp
+++ b/libs/hwui/OpenGLReadback.cpp
@@ -131,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");
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/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 2177af1..a9058b1 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -108,6 +108,8 @@
         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;
@@ -306,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/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 376346f..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(), SkClipOp::kReplace);
+            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, SkClipOp::kReplace);
+    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/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/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 65eb169..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 {
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 722faf6..f3a663e 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -324,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;
     };
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/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/SystemUI/res/drawable/header_dot.xml b/packages/SystemUI/res/drawable/header_dot.xml
index 568a9c2..dbc6b37 100644
--- a/packages/SystemUI/res/drawable/header_dot.xml
+++ b/packages/SystemUI/res/drawable/header_dot.xml
@@ -2,26 +2,27 @@
 <!--
 ** Copyright 2015, The Android Open Source Project
 **
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
+** 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 
+**     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 
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
 ** limitations under the License.
 -->
 <shape
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="oval">
+    android:shape="oval"
+    android:tint="?android:attr/colorControlNormal">
 
-    <solid 
+    <solid
         android:color="#FFFFFF"/>
 
-    <size 
+    <size
         android:width="3dp"
         android:height="3dp"/>
 </shape>
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 967a84c..31b83cc 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1687,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/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/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/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/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c73c486..eae4905 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;
 
@@ -367,6 +369,7 @@
 import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS;
 import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
 import static com.android.server.am.ActivityStackSupervisor.RESTORE_FROM_RECENTS;
 import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
@@ -537,8 +540,6 @@
     private static final String INTENT_REMOTE_BUGREPORT_FINISHED =
             "android.intent.action.REMOTE_BUGREPORT_FINISHED";
 
-    // Used to indicate that a task is removed it should also be removed from recents.
-    private static final boolean REMOVE_FROM_RECENTS = true;
     // Used to indicate that an app transition should be animated.
     static final boolean ANIMATE = true;
 
@@ -1220,6 +1221,15 @@
     /**
      * Set while we are wanting to sleep, to prevent any
      * activities from being started/resumed.
+     *
+     * TODO(b/33594039): Clarify the actual state transitions represented by mSleeping.
+     *
+     * Currently mSleeping is set to true when transitioning into the sleep state, and remains true
+     * while in the sleep state until there is a pending transition out of sleep, in which case
+     * mSleeping is set to false, and remains false while awake.
+     *
+     * Whether mSleeping can quickly toggled between true/false without the device actually
+     * display changing states is undefined.
      */
     private boolean mSleeping = false;
 
@@ -1552,10 +1562,10 @@
     static final int SYSTEM_USER_UNLOCK_MSG = 59;
     static final int LOG_STACK_STATE = 60;
     static final int VR_MODE_CHANGE_MSG = 61;
-    static final int VR_MODE_APPLY_IF_NEEDED_MSG = 62;
-    static final int SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG_MSG = 63;
-    static final int HANDLE_TRUST_STORAGE_UPDATE_MSG = 64;
-    static final int REPORT_LOCKED_BOOT_COMPLETE_MSG = 65;
+    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;
@@ -2290,14 +2300,8 @@
                     }
                 }
                 vrService.setVrMode(vrMode, requestedPackage, userId, callingPackage);
-            } break;
-            case VR_MODE_APPLY_IF_NEEDED_MSG: {
-                final ActivityRecord r = (ActivityRecord) msg.obj;
-                final boolean needsVrMode = r != null && r.requestedVrComponent != null;
-                if (needsVrMode) {
-                    applyVrMode(msg.arg1 == 1, r.requestedVrComponent, r.userId,
-                            r.info.getComponentName(), false);
-                }
+            } case NOTIFY_VR_SLEEPING_MSG: {
+                notifyVrManagerOfSleepState(msg.arg1 != 0);
             } break;
             case HANDLE_TRUST_STORAGE_UPDATE_MSG: {
                 synchronized (ActivityManagerService.this) {
@@ -3071,20 +3075,17 @@
                 mHandler.obtainMessage(VR_MODE_CHANGE_MSG, 0, 0, r));
     }
 
-    void applyVrModeIfNeededLocked(ActivityRecord r, boolean enable) {
+    private void sendNotifyVrManagerOfSleepState(boolean isSleeping) {
         mHandler.sendMessage(
-                mHandler.obtainMessage(VR_MODE_APPLY_IF_NEEDED_MSG, enable ? 1 : 0, 0, r));
+                mHandler.obtainMessage(NOTIFY_VR_SLEEPING_MSG, isSleeping ? 1 : 0, 0));
     }
 
-    private void applyVrMode(boolean enabled, ComponentName packageName, int userId,
-            ComponentName callingPackage, boolean immediate) {
-        VrManagerInternal vrService =
-                LocalServices.getService(VrManagerInternal.class);
-        if (immediate) {
-            vrService.setVrModeImmediate(enabled, packageName, userId, callingPackage);
-        } else {
-            vrService.setVrMode(enabled, packageName, userId, callingPackage);
+    private void notifyVrManagerOfSleepState(boolean isSleeping) {
+        final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
+        if (vrService == null) {
+            return;
         }
+        vrService.onSleepStateChanged(isSleeping);
     }
 
     final void showAskCompatModeDialogLocked(ActivityRecord r) {
@@ -3682,6 +3683,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];
@@ -3708,12 +3718,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);
@@ -4815,7 +4825,7 @@
                     // because we don't support returning them across task boundaries. Also, to
                     // keep backwards compatibility we remove the task from recents when finishing
                     // task with root activity.
-                    res = removeTaskByIdLocked(tr.taskId, false, finishWithRootActivity);
+                    res = mStackSupervisor.removeTaskByIdLocked(tr.taskId, false, finishWithRootActivity);
                     if (!res) {
                         Slog.i(TAG, "Removing task failed to finish activity");
                     }
@@ -5460,7 +5470,7 @@
                             tr.getBaseIntent().getComponent().getPackageName();
                     if (tr.userId != userId) continue;
                     if (!taskPackageName.equals(packageName)) continue;
-                    removeTaskByIdLocked(tr.taskId, false, REMOVE_FROM_RECENTS);
+                    mStackSupervisor.removeTaskByIdLocked(tr.taskId, false, REMOVE_FROM_RECENTS);
                 }
             }
 
@@ -7522,6 +7532,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();
@@ -7531,7 +7558,8 @@
                         "enterPictureInPictureMode", token, aspectRatio, checkAspectRatio,
                         true /* checkActivityVisibility */);
 
-                enterPictureInPictureModeLocked(r, displayId, aspectRatio,
+                r.pictureInPictureArgs.aspectRatio = aspectRatio;
+                enterPictureInPictureModeLocked(r, displayId, r.pictureInPictureArgs,
                         true /* moveHomeStackToFront */, "enterPictureInPictureMode");
             }
         } finally {
@@ -7539,29 +7567,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
@@ -7573,11 +7585,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);
@@ -7613,7 +7653,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);
         }
 
@@ -9544,79 +9584,6 @@
         mWindowManager.executeAppTransition();
     }
 
-    private void cleanUpRemovedTaskLocked(TaskRecord tr, boolean killProcess,
-            boolean removeFromRecents) {
-        if (removeFromRecents) {
-            mRecentTasks.remove(tr);
-            tr.removedFromRecents();
-        }
-        ComponentName component = tr.getBaseIntent().getComponent();
-        if (component == null) {
-            Slog.w(TAG, "No component for base intent of task: " + tr);
-            return;
-        }
-
-        // Find any running services associated with this app and stop if needed.
-        mServices.cleanUpRemovedTaskLocked(tr, component, new Intent(tr.getBaseIntent()));
-
-        if (!killProcess) {
-            return;
-        }
-
-        // Determine if the process(es) for this task should be killed.
-        final String pkg = component.getPackageName();
-        ArrayList<ProcessRecord> procsToKill = new ArrayList<>();
-        ArrayMap<String, SparseArray<ProcessRecord>> pmap = mProcessNames.getMap();
-        for (int i = 0; i < pmap.size(); i++) {
-
-            SparseArray<ProcessRecord> uids = pmap.valueAt(i);
-            for (int j = 0; j < uids.size(); j++) {
-                ProcessRecord proc = uids.valueAt(j);
-                if (proc.userId != tr.userId) {
-                    // Don't kill process for a different user.
-                    continue;
-                }
-                if (proc == mHomeProcess) {
-                    // Don't kill the home process along with tasks from the same package.
-                    continue;
-                }
-                if (!proc.pkgList.containsKey(pkg)) {
-                    // Don't kill process that is not associated with this task.
-                    continue;
-                }
-
-                for (int k = 0; k < proc.activities.size(); k++) {
-                    TaskRecord otherTask = proc.activities.get(k).task;
-                    if (tr.taskId != otherTask.taskId && otherTask.inRecents) {
-                        // Don't kill process(es) that has an activity in a different task that is
-                        // also in recents.
-                        return;
-                    }
-                }
-
-                if (proc.foregroundServices) {
-                    // Don't kill process(es) with foreground service.
-                    return;
-                }
-
-                // Add process to kill list.
-                procsToKill.add(proc);
-            }
-        }
-
-        // Kill the running processes.
-        for (int i = 0; i < procsToKill.size(); i++) {
-            ProcessRecord pr = procsToKill.get(i);
-            if (pr.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND
-                    && pr.curReceivers.isEmpty()) {
-                pr.kill("remove task", true);
-            } else {
-                // We delay killing processes that are not in the background or running a receiver.
-                pr.waitingToKill = "remove task";
-            }
-        }
-    }
-
     private void removeTasksByPackageNameLocked(String packageName, int userId) {
         // Remove all tasks with activities in the specified package from the list of recent tasks
         for (int i = mRecentTasks.size() - 1; i >= 0; i--) {
@@ -9626,7 +9593,7 @@
             ComponentName cn = tr.intent.getComponent();
             if (cn != null && cn.getPackageName().equals(packageName)) {
                 // If the package name matches, remove the task.
-                removeTaskByIdLocked(tr.taskId, true, REMOVE_FROM_RECENTS);
+                mStackSupervisor.removeTaskByIdLocked(tr.taskId, true, REMOVE_FROM_RECENTS);
             }
         }
     }
@@ -9644,35 +9611,11 @@
             final boolean sameComponent = cn != null && cn.getPackageName().equals(packageName)
                     && (filterByClasses == null || filterByClasses.contains(cn.getClassName()));
             if (sameComponent) {
-                removeTaskByIdLocked(tr.taskId, false, REMOVE_FROM_RECENTS);
+                mStackSupervisor.removeTaskByIdLocked(tr.taskId, false, REMOVE_FROM_RECENTS);
             }
         }
     }
 
-    /**
-     * Removes the task with the specified task id.
-     *
-     * @param taskId Identifier of the task to be removed.
-     * @param killProcess Kill any process associated with the task if possible.
-     * @param removeFromRecents Whether to also remove the task from recents.
-     * @return Returns true if the given task was found and removed.
-     */
-    private boolean removeTaskByIdLocked(int taskId, boolean killProcess,
-            boolean removeFromRecents) {
-        final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(
-                taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
-        if (tr != null) {
-            tr.removeTaskActivitiesLocked();
-            cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents);
-            if (tr.isPersistable) {
-                notifyTaskPersisterLocked(null, true);
-            }
-            return true;
-        }
-        Slog.w(TAG, "Request to remove task ignored for non-existent task " + taskId);
-        return false;
-    }
-
     @Override
     public void removeStack(int stackId) {
         enforceCallingPermission(Manifest.permission.MANAGE_ACTIVITY_STACKS, "removeStack()");
@@ -9683,15 +9626,7 @@
         synchronized (this) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                final ActivityStack stack = mStackSupervisor.getStack(stackId);
-                if (stack == null) {
-                    return;
-                }
-                final ArrayList<TaskRecord> tasks = stack.getAllTasks();
-                for (int i = tasks.size() - 1; i >= 0; i--) {
-                    removeTaskByIdLocked(
-                            tasks.get(i).taskId, true /* killProcess */, REMOVE_FROM_RECENTS);
-                }
+                mStackSupervisor.removeStackLocked(stackId);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -9720,7 +9655,7 @@
         synchronized (this) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                return removeTaskByIdLocked(taskId, true, REMOVE_FROM_RECENTS);
+                return mStackSupervisor.removeTaskByIdLocked(taskId, true, REMOVE_FROM_RECENTS);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -11693,6 +11628,7 @@
             startTimeTrackingFocusedActivityLocked();
             mTopProcessState = ActivityManager.PROCESS_STATE_TOP;
             mStackSupervisor.comeOutOfSleepIfNeededLocked();
+            sendNotifyVrManagerOfSleepState(false);
             updateOomAdjLocked();
         } else if (!mSleeping && shouldSleepLocked()) {
             mSleeping = true;
@@ -11701,6 +11637,7 @@
             }
             mTopProcessState = ActivityManager.PROCESS_STATE_TOP_SLEEPING;
             mStackSupervisor.goingToSleepLocked();
+            sendNotifyVrManagerOfSleepState(true);
             updateOomAdjLocked();
 
             // Initialize the wake times of all processes.
@@ -22238,22 +22175,6 @@
         public SleepToken acquireSleepToken(String tag) {
             Preconditions.checkNotNull(tag);
 
-            ComponentName requestedVrService = null;
-            ComponentName callingVrActivity = null;
-            int userId = -1;
-            synchronized (ActivityManagerService.this) {
-                final ActivityRecord resumedActivity = mStackSupervisor.getResumedActivityLocked();
-                if (resumedActivity != null) {
-                    requestedVrService = resumedActivity.requestedVrComponent;
-                    callingVrActivity = resumedActivity.info.getComponentName();
-                    userId = resumedActivity.userId;
-                }
-            }
-
-            if (requestedVrService != null) {
-                applyVrMode(false, requestedVrService, userId, callingVrActivity, true);
-            }
-
             synchronized (ActivityManagerService.this) {
                 SleepTokenImpl token = new SleepTokenImpl(tag);
                 mSleepTokens.add(token);
@@ -22493,7 +22414,8 @@
                 long origId = Binder.clearCallingIdentity();
                 try {
                     // We remove the task from recents to preserve backwards
-                    if (!removeTaskByIdLocked(mTaskId, false, REMOVE_FROM_RECENTS)) {
+                    if (!mStackSupervisor.removeTaskByIdLocked(mTaskId, false,
+                            REMOVE_FROM_RECENTS)) {
                         throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
                     }
                 } finally {
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/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index fe261f1..b4b3465 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -249,6 +249,9 @@
     // Don't execute any calls to resume.
     static final boolean DEFER_RESUME = true;
 
+    // Used to indicate that a task is removed it should also be removed from recents.
+    static final boolean REMOVE_FROM_RECENTS = true;
+
     // Activity actions an app cannot start if it uses a permission which is not granted.
     private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION =
             new ArrayMap<>();
@@ -2183,7 +2186,8 @@
                         // Update the return-to to reflect where the pinned stack task was moved
                         // from so that we retain the stack that was previously visible if the
                         // pinned stack is recreated. See moveActivityToPinnedStackLocked().
-                        task.setTaskToReturnTo(getFocusedStack().getStackId() == HOME_STACK_ID
+                        final int focusedStackId = getFocusedStack().getStackId();
+                        task.setTaskToReturnTo(focusedStackId == HOME_STACK_ID || !onTop
                                 ? HOME_ACTIVITY_TYPE : APPLICATION_ACTIVITY_TYPE);
                     }
                     moveTaskToStackLocked(tasks.get(i).taskId,
@@ -2374,6 +2378,141 @@
         return activityContainer.mStack;
     }
 
+    /**
+     * Removes the stack associed with the given {@param stackId}.  If the {@param stackId} is the
+     * pinned stack, then its tasks are not explicitly removed when the stack is destroyed, but
+     * instead moved back onto the fullscreen stack.
+     */
+    void removeStackLocked(int stackId) {
+        final ActivityStack stack = getStack(stackId);
+        if (stack == null) {
+            return;
+        }
+
+        final ArrayList<TaskRecord> tasks = stack.getAllTasks();
+        if (stack.getStackId() == PINNED_STACK_ID) {
+            final ActivityStack fullscreenStack = getStack(FULLSCREEN_WORKSPACE_STACK_ID);
+            if (fullscreenStack != null) {
+                final boolean isFullscreenStackVisible =
+                        fullscreenStack.getStackVisibilityLocked(null) == STACK_VISIBLE;
+                for (int i = 0; i < tasks.size(); i++) {
+                    // Insert the task either at the top of the fullscreen stack if it is hidden,
+                    // or just under the top task if it is currently visible
+                    final int insertPosition = isFullscreenStackVisible
+                            ? Math.max(0, fullscreenStack.getChildCount() - 1)
+                            : fullscreenStack.getChildCount();
+                    positionTaskInStackLocked(tasks.get(i).taskId, FULLSCREEN_WORKSPACE_STACK_ID,
+                            insertPosition);
+                }
+                ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                resumeFocusedStackTopActivityLocked();
+            } else {
+                // If there is no fullscreen stack, then create the stack and move all the tasks
+                // onto the stack
+                moveTasksToFullscreenStackLocked(PINNED_STACK_ID, false /* onTop */);
+            }
+        } else {
+            for (int i = tasks.size() - 1; i >= 0; i--) {
+                removeTaskByIdLocked(tasks.get(i).taskId, true /* killProcess */,
+                        REMOVE_FROM_RECENTS);
+            }
+        }
+    }
+
+    /**
+     * Removes the task with the specified task id.
+     *
+     * @param taskId Identifier of the task to be removed.
+     * @param killProcess Kill any process associated with the task if possible.
+     * @param removeFromRecents Whether to also remove the task from recents.
+     * @return Returns true if the given task was found and removed.
+     */
+    boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents) {
+        final TaskRecord tr = anyTaskForIdLocked(taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
+        if (tr != null) {
+            tr.removeTaskActivitiesLocked();
+            cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents);
+            if (tr.isPersistable) {
+                mService.notifyTaskPersisterLocked(null, true);
+            }
+            return true;
+        }
+        Slog.w(TAG, "Request to remove task ignored for non-existent task " + taskId);
+        return false;
+    }
+
+    void cleanUpRemovedTaskLocked(TaskRecord tr, boolean killProcess, boolean removeFromRecents) {
+        if (removeFromRecents) {
+            mRecentTasks.remove(tr);
+            tr.removedFromRecents();
+        }
+        ComponentName component = tr.getBaseIntent().getComponent();
+        if (component == null) {
+            Slog.w(TAG, "No component for base intent of task: " + tr);
+            return;
+        }
+
+        // Find any running services associated with this app and stop if needed.
+        mService.mServices.cleanUpRemovedTaskLocked(tr, component, new Intent(tr.getBaseIntent()));
+
+        if (!killProcess) {
+            return;
+        }
+
+        // Determine if the process(es) for this task should be killed.
+        final String pkg = component.getPackageName();
+        ArrayList<ProcessRecord> procsToKill = new ArrayList<>();
+        ArrayMap<String, SparseArray<ProcessRecord>> pmap = mService.mProcessNames.getMap();
+        for (int i = 0; i < pmap.size(); i++) {
+
+            SparseArray<ProcessRecord> uids = pmap.valueAt(i);
+            for (int j = 0; j < uids.size(); j++) {
+                ProcessRecord proc = uids.valueAt(j);
+                if (proc.userId != tr.userId) {
+                    // Don't kill process for a different user.
+                    continue;
+                }
+                if (proc == mService.mHomeProcess) {
+                    // Don't kill the home process along with tasks from the same package.
+                    continue;
+                }
+                if (!proc.pkgList.containsKey(pkg)) {
+                    // Don't kill process that is not associated with this task.
+                    continue;
+                }
+
+                for (int k = 0; k < proc.activities.size(); k++) {
+                    TaskRecord otherTask = proc.activities.get(k).task;
+                    if (tr.taskId != otherTask.taskId && otherTask.inRecents) {
+                        // Don't kill process(es) that has an activity in a different task that is
+                        // also in recents.
+                        return;
+                    }
+                }
+
+                if (proc.foregroundServices) {
+                    // Don't kill process(es) with foreground service.
+                    return;
+                }
+
+                // Add process to kill list.
+                procsToKill.add(proc);
+            }
+        }
+
+        // Kill the running processes.
+        for (int i = 0; i < procsToKill.size(); i++) {
+            ProcessRecord pr = procsToKill.get(i);
+            if (pr.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND
+                    && pr.curReceivers.isEmpty()) {
+                pr.kill("remove task", true);
+            } else {
+                // We delay killing processes that are not in the background or running a receiver.
+                pr.waitingToKill = "remove task";
+            }
+        }
+    }
+
     int getNextStackId() {
         while (true) {
             if (mNextFreeStackId >= FIRST_DYNAMIC_STACK_ID
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index 19bf536..cfe2eb0 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -121,8 +121,6 @@
                 // Some stack visibility might change (e.g. docked stack)
                 mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
                 mWindowManager.executeAppTransition();
-                mService.applyVrModeIfNeededLocked(mStackSupervisor.getResumedActivityLocked(),
-                        true /* enable */);
             } finally {
                 mWindowManager.continueSurfaceLayout();
             }
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/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c65514b..34c1470 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -5524,9 +5524,21 @@
             final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
             final ActivityInfo ai = getActivityInfo(comp, flags, userId);
             if (ai != null) {
-                final ResolveInfo ri = new ResolveInfo();
-                ri.activityInfo = ai;
-                list.add(ri);
+                // When specifying an explicit component, we prevent the activity from being
+                // used when either 1) the calling package is normal and the activity is within
+                // an ephemeral application or 2) the calling package is ephemeral and the
+                // activity is not visible to ephemeral applications.
+                boolean blockResolution =
+                        (ephemeralPkgName == null
+                                && (ai.applicationInfo.privateFlags
+                                        & ApplicationInfo.PRIVATE_FLAG_EPHEMERAL) != 0)
+                        || (ephemeralPkgName != null
+                                && (ai.flags & ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL) == 0);
+                if (!blockResolution) {
+                    final ResolveInfo ri = new ResolveInfo();
+                    ri.activityInfo = ai;
+                    list.add(ri);
+                }
             }
             return list;
         }
@@ -5604,10 +5616,10 @@
             } else {
                 final PackageParser.Package pkg = mPackages.get(pkgName);
                 if (pkg != null) {
-                    result = filterIfNotSystemUser(
+                    result = filterForEphemeral(filterIfNotSystemUser(
                             mActivities.queryIntentForPackage(
                                     intent, resolvedType, flags, pkg.activities, userId),
-                            userId);
+                            userId), ephemeralPkgName);
                 } else {
                     // the caller wants to resolve for a particular package; however, there
                     // were no installed results, so, try to find an ephemeral result
diff --git a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
index 7928257..cdb69ce 100644
--- a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
+++ b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
@@ -48,7 +48,15 @@
      */
     private static class PinShortcutRequestInner extends IPinItemRequest.Stub {
         private final ShortcutRequestPinProcessor mProcessor;
-        public final ShortcutInfo shortcut;
+        /** 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;
@@ -59,10 +67,12 @@
         private boolean mAccepted;
 
         private PinShortcutRequestInner(ShortcutRequestPinProcessor processor,
-                ShortcutInfo shortcut, IntentSender resultIntent,
+                ShortcutInfo shortcutOriginal, ShortcutInfo shortcutForLauncher,
+                IntentSender resultIntent,
                 String launcherPackage, int launcherUserId, boolean preExisting) {
             mProcessor = processor;
-            this.shortcut = shortcut;
+            this.shortcutOriginal = shortcutOriginal;
+            this.shortcutForLauncher = shortcutForLauncher;
             mResultIntent = resultIntent;
             this.launcherPackage = launcherPackage;
             this.launcherUserId = launcherUserId;
@@ -99,8 +109,8 @@
                 mAccepted = true;
             }
             if (DEBUG) {
-                Slog.d(TAG, "Launcher accepted shortcut. ID=" + shortcut.getId()
-                        + " package=" + shortcut.getPackage()
+                Slog.d(TAG, "Launcher accepted shortcut. ID=" + shortcutOriginal.getId()
+                        + " package=" + shortcutOriginal.getPackage()
                         + " options=" + options);
             }
 
@@ -163,7 +173,7 @@
         }
 
         // This is the shortcut that'll be sent to the launcher.
-        final ShortcutInfo shortcutToSend;
+        final ShortcutInfo shortcutForLauncher;
 
         if (existsAlready) {
             validateExistingShortcut(existing);
@@ -179,8 +189,10 @@
 
             // Pass a clone, not the original.
             // Note this will remove the intent and icons.
-            shortcutToSend = existing.clone(ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER);
-            shortcutToSend.clearFlags(ShortcutInfo.FLAG_PINNED);
+            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);
@@ -191,17 +203,18 @@
             if (DEBUG) {
                 Slog.d(TAG, "resolved shortcut=" + inShortcut.toInsecureString());
             }
-            // TODO Remove the intent here -- don't pass shortcut intents to the launcher.
-            shortcutToSend = inShortcut;
+            // 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, shortcutToSend, resultIntent,
+                new PinShortcutRequestInner(this, inShortcut, shortcutForLauncher, resultIntent,
                         launcherPackage, launcherUserId, existsAlready);
 
         final PinItemRequest outer = new PinItemRequest(PinItemRequest.REQUEST_TYPE_SHORTCUT,
-                shortcutToSend, inner);
+                shortcutForLauncher, inner);
 
         return startRequestConfirmActivity(launcherComponent, launcherUserId, outer);
     }
@@ -210,7 +223,7 @@
         // Make sure it's enabled.
         // (Because we can't always force enable it automatically as it may be a stale
         // manifest shortcut.)
-        Preconditions.checkState(shortcutInfo.isEnabled(),
+        Preconditions.checkArgument(shortcutInfo.isEnabled(),
                 "Shortcut ID=" + shortcutInfo + " already exists but disabled.");
 
     }
@@ -270,7 +283,7 @@
      */
     public boolean directPinShortcut(PinShortcutRequestInner request) {
 
-        final ShortcutInfo original = request.shortcut;
+        final ShortcutInfo original = request.shortcutOriginal;
         final int appUserId = original.getUserId();
         final String appPackageName = original.getPackage();
         final int launcherUserId = request.launcherUserId;
@@ -292,7 +305,7 @@
             try {
                 if (current == null) {
                     // It doesn't exist, so it must have all necessary fields.
-                    mService.validateShortcutForPinRequest(request.shortcut);
+                    mService.validateShortcutForPinRequest(original);
                 } else {
                     validateExistingShortcut(current);
                 }
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 424830b..436a53c 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1865,9 +1865,10 @@
         synchronized (mLock) {
             throwIfUserLockedL(userId);
 
-            // TODO Make sure the caller is in the foreground.
+            Preconditions.checkState(isUidForegroundLocked(injectBinderCallingUid()),
+                    "Calling application must have a foreground activity or a foreground service");
 
-            // TODO Cancel all pending request from the same app.
+            // TODO Cancel all pending requests from the caller.
 
             // Send request to the launcher, if supported.
             ret = mShortcutRequestPinProcessor.requestPinShortcutLocked(shortcut, resultIntent);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 65fbc55..9d4b51f 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -229,6 +229,7 @@
 import com.android.server.policy.keyguard.KeyguardStateMonitor.StateCallback;
 import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.wm.AppTransition;
+import com.android.server.vr.VrManagerInternal;
 
 import java.io.File;
 import java.io.FileReader;
@@ -6455,6 +6456,7 @@
                 mKeyguardDelegate.onScreenTurnedOff();
             }
         }
+        reportScreenStateToVrManager(false);
     }
 
     // Called on the DisplayManager's DisplayPowerController thread.
@@ -6490,6 +6492,15 @@
                 mKeyguardDelegate.onScreenTurnedOn();
             }
         }
+        reportScreenStateToVrManager(true);
+    }
+
+    private void reportScreenStateToVrManager(boolean isScreenOn) {
+        VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
+        if (vrService == null) {
+            return;
+        }
+        vrService.onScreenStateChanged(isScreenOn);
     }
 
     private void finishWindowsDrawn() {
diff --git a/services/core/java/com/android/server/vr/VrManagerInternal.java b/services/core/java/com/android/server/vr/VrManagerInternal.java
index ad87a88..0fc1900 100644
--- a/services/core/java/com/android/server/vr/VrManagerInternal.java
+++ b/services/core/java/com/android/server/vr/VrManagerInternal.java
@@ -56,25 +56,27 @@
             int userId, @NonNull ComponentName calling);
 
     /**
-     * Set the current VR mode state immediately.
+     * Set whether the system has acquired a sleep token.
      *
-     * @param enabled {@code true} to enable VR mode.
-     * @param packageName The package name of the requested VrListenerService to bind.
-     * @param userId the user requesting the VrListenerService component.
-     * @param calling the component currently using VR mode, or null to leave unchanged.
+     * @param isAsleep is {@code true} if the device is asleep, or {@code false} otherwise.
      */
-    public abstract void setVrModeImmediate(boolean enabled, @NonNull ComponentName packageName,
-            int userId, @NonNull ComponentName calling);
+    public abstract void onSleepStateChanged(boolean isAsleep);
 
+    /**
+     * Set whether the display used for VR output is on.
+     *
+     * @param isScreenOn is {@code true} if the display is on and can receive commands,
+     *      or {@code false} otherwise.
+     */
+    public abstract void onScreenStateChanged(boolean isScreenOn);
 
-   /**
-    * Return NO_ERROR if the given package is installed on the device and enabled as a
-    * VrListenerService for the given current user, or a negative error code indicating a failure.
-    *
-    * @param packageName the name of the package to check, or null to select the default package.
-    * @return NO_ERROR if the given package is installed and is enabled, or a negative error code
-    *       given in {@link android.service.vr.VrModeException} on failure.
-    */
+    /**
+     * Return NO_ERROR if the given package is installed on the device and enabled as a
+     * VrListenerService for the given current user, or a negative error code indicating a failure.
+     *
+     * @param packageName the name of the package to check, or null to select the default package.
+     * @return NO_ERROR if the given package is installed and is enabled, or a negative error code
+     *       given in {@link android.service.vr.VrModeException} on failure.
+     */
     public abstract int hasVrPackage(@NonNull ComponentName packageName, int userId);
-
 }
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index 10a0f6f..1083e0a 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -101,6 +101,14 @@
     private static final int PENDING_STATE_DELAY_MS = 300;
     private static final int EVENT_LOG_SIZE = 32;
     private static final int INVALID_APPOPS_MODE = -1;
+    /** Null set of sleep sleep flags. */
+    private static final int FLAG_NONE = 0;
+    /** Flag set when the device is not sleeping. */
+    private static final int FLAG_AWAKE = 1;
+    /** Flag set when the screen has been turned on. */
+    private static final int FLAG_SCREEN_ON = 2;
+    /** Flag indicating that all system sleep flags have been set.*/
+    private static final int FLAG_ALL = FLAG_AWAKE | FLAG_SCREEN_ON;
 
     private static native void initializeNative();
     private static native void setVrModeNative(boolean enabled);
@@ -110,6 +118,7 @@
     private final IBinder mOverlayToken = new Binder();
 
     // State protected by mLock
+    private boolean mVrModeAllowed;
     private boolean mVrModeEnabled;
     private EnabledComponentsObserver mComponentObserver;
     private ManagedApplicationService mCurrentVrService;
@@ -125,10 +134,64 @@
     private VrState mPendingState;
     private final ArrayDeque<VrState> mLoggingDeque = new ArrayDeque<>(EVENT_LOG_SIZE);
     private final NotificationAccessManager mNotifAccessManager = new NotificationAccessManager();
+    /** Tracks the state of the screen and keyguard UI.*/
+    private int mSystemSleepFlags = FLAG_NONE;
 
     private static final int MSG_VR_STATE_CHANGE = 0;
     private static final int MSG_PENDING_VR_STATE_CHANGE = 1;
 
+    /**
+     * Set whether VR mode may be enabled.
+     * <p/>
+     * If VR mode is not allowed to be enabled, calls to set VR mode will be cached.  When VR mode
+     * is again allowed to be enabled, the most recent cached state will be applied.
+     *
+     * @param allowed {@code true} if calling any of the setVrMode methods may cause the device to
+     *   enter VR mode.
+     */
+    private void setVrModeAllowedLocked(boolean allowed) {
+        if (mVrModeAllowed != allowed) {
+            mVrModeAllowed = allowed;
+            Slog.i(TAG, "VR mode is " + ((allowed) ? "allowed" : "disallowed"));
+            if (mVrModeAllowed) {
+                consumeAndApplyPendingStateLocked();
+            } else {
+                // Set pending state to current state.
+                mPendingState = (mVrModeEnabled && mCurrentVrService != null)
+                    ? new VrState(mVrModeEnabled, mCurrentVrService.getComponent(),
+                        mCurrentVrService.getUserId(), mCurrentVrModeComponent)
+                    : null;
+
+                // Unbind current VR service and do necessary callbacks.
+                updateCurrentVrServiceLocked(false, null, 0, null);
+            }
+        }
+    }
+
+    private void setSleepState(boolean isAsleep) {
+        synchronized(mLock) {
+
+            if (!isAsleep) {
+                mSystemSleepFlags |= FLAG_AWAKE;
+            } else {
+                mSystemSleepFlags &= ~FLAG_AWAKE;
+            }
+
+            setVrModeAllowedLocked(mSystemSleepFlags == FLAG_ALL);
+        }
+    }
+
+    private void setScreenOn(boolean isScreenOn) {
+        synchronized(mLock) {
+            if (isScreenOn) {
+                mSystemSleepFlags |= FLAG_SCREEN_ON;
+            } else {
+                mSystemSleepFlags &= ~FLAG_SCREEN_ON;
+            }
+            setVrModeAllowedLocked(mSystemSleepFlags == FLAG_ALL);
+        }
+    }
+
     private final Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
@@ -148,7 +211,9 @@
                 } break;
                 case MSG_PENDING_VR_STATE_CHANGE : {
                     synchronized(mLock) {
-                        VrManagerService.this.consumeAndApplyPendingStateLocked();
+                        if (mVrModeAllowed) {
+                           VrManagerService.this.consumeAndApplyPendingStateLocked();
+                        }
                     }
                 } break;
                 default :
@@ -268,8 +333,8 @@
             }
             mNotifAccessManager.update(enabledPackages);
 
-            if (mCurrentVrService == null) {
-                return; // No active services
+            if (!mVrModeAllowed) {
+                return; // Don't do anything, we shouldn't be in VR mode.
             }
 
             // If there is a pending state change, we'd better deal with that first
@@ -321,6 +386,7 @@
                 return;
             }
             pw.println("********* Dump of VrManagerService *********");
+            pw.println("VR mode is currently: " + ((mVrModeAllowed) ? "allowed" : "disallowed"));
             pw.println("Previous state transitions:\n");
             String tab = "  ";
             dumpStateTransitions(pw);
@@ -374,13 +440,17 @@
         @Override
         public void setVrMode(boolean enabled, ComponentName packageName, int userId,
                 ComponentName callingPackage) {
-            VrManagerService.this.setVrMode(enabled, packageName, userId, callingPackage, false);
+            VrManagerService.this.setVrMode(enabled, packageName, userId, callingPackage);
         }
 
         @Override
-        public void setVrModeImmediate(boolean enabled, ComponentName packageName, int userId,
-                ComponentName callingPackage) {
-            VrManagerService.this.setVrMode(enabled, packageName, userId, callingPackage, true);
+        public void onSleepStateChanged(boolean isAsleep) {
+            VrManagerService.this.setSleepState(isAsleep);
+        }
+
+        @Override
+        public void onScreenStateChanged(boolean isScreenOn) {
+            VrManagerService.this.setScreenOn(isScreenOn);
         }
 
         @Override
@@ -424,6 +494,10 @@
 
                 mComponentObserver.rebuildAll();
             }
+        } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
+            synchronized (mLock) {
+                mVrModeAllowed = true;
+            }
         }
     }
 
@@ -466,12 +540,16 @@
                     false, mOverlayToken, null, oldUserId);
         }
 
+        if (!mVrModeEnabled) {
+            return;
+        }
+
         // Apply the restrictions for the current user based on vr state
         String[] exemptions = (exemptedPackage == null) ? new String[0] :
                 new String[] { exemptedPackage };
 
         appOpsManager.setUserRestrictionForUser(AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
-                mVrModeEnabled, mOverlayToken, exemptions, newUserId);
+                true, mOverlayToken, exemptions, newUserId);
     }
 
     private void updateDependentAppOpsLocked(String newVrServicePackage, int newUserId,
@@ -512,7 +590,8 @@
 
             boolean validUserComponent = (mComponentObserver.isValid(component, userId) ==
                     EnabledComponentsObserver.NO_ERROR);
-            if (!mVrModeEnabled && !enabled) {
+            boolean goingIntoVrMode = validUserComponent && enabled;
+            if (!mVrModeEnabled && !goingIntoVrMode) {
                 return validUserComponent; // Disabled -> Disabled transition does nothing.
             }
 
@@ -520,29 +599,37 @@
                     ? mCurrentVrService.getComponent().getPackageName() : null;
             final int oldUserId = mCurrentVrModeUser;
 
-            // Always send mode change events.
-            changeVrModeLocked(enabled);
+            // Notify system services and VR HAL of mode change.
+            changeVrModeLocked(goingIntoVrMode);
 
-            if (!enabled || !validUserComponent) {
-                // Unbind whatever is running
+            boolean nothingChanged = false;
+            if (!goingIntoVrMode) {
+                // Not going into VR mode, unbind whatever is running
                 if (mCurrentVrService != null) {
                     Slog.i(TAG, "Disconnecting " + mCurrentVrService.getComponent() + " for user " +
                             mCurrentVrService.getUserId());
                     mCurrentVrService.disconnect();
                     mCurrentVrService = null;
+                } else {
+                    nothingChanged = true;
                 }
             } else {
+                // Going into VR mode
                 if (mCurrentVrService != null) {
-                    // Unbind any running service that doesn't match the component/user selection
+                    // Unbind any running service that doesn't match the latest component/user
+                    // selection.
                     if (mCurrentVrService.disconnectIfNotMatching(component, userId)) {
                         Slog.i(TAG, "Disconnecting " + mCurrentVrService.getComponent() +
                                 " for user " + mCurrentVrService.getUserId());
                         createAndConnectService(component, userId);
                         sendUpdatedCaller = true;
+                    } else {
+                        nothingChanged = true;
                     }
-                    // The service with the correct component/user is bound
+                    // The service with the correct component/user is already bound, do nothing.
                 } else {
-                    // Nothing was previously running, bind a new service
+                    // Nothing was previously running, bind a new service for the latest
+                    // component/user selection.
                     createAndConnectService(component, userId);
                     sendUpdatedCaller = true;
                 }
@@ -577,7 +664,10 @@
                     }
                 });
             }
-            logStateLocked();
+
+            if (!nothingChanged) {
+                logStateLocked();
+            }
 
             return validUserComponent;
         } finally {
@@ -784,7 +874,10 @@
                     mPendingState.targetPackageName, mPendingState.userId,
                     mPendingState.callingPackage);
             mPendingState = null;
+        } else {
+            updateCurrentVrServiceLocked(false, null, 0, null);
         }
+
     }
 
     private void logStateLocked() {
@@ -834,13 +927,20 @@
     /*
      * Implementation of VrManagerInternal calls.  These are callable from system services.
      */
-
     private void setVrMode(boolean enabled, @NonNull ComponentName targetPackageName,
-            int userId, @NonNull ComponentName callingPackage, boolean immediate) {
+            int userId, @NonNull ComponentName callingPackage) {
 
         synchronized (mLock) {
+            VrState pending = new VrState(enabled, targetPackageName, userId, callingPackage);
+            if (!mVrModeAllowed) {
+                // We're not allowed to be in VR mode.  Make this state pending.  This will be
+                // applied the next time we are allowed to enter VR mode unless it is superseded by
+                // another call.
+                mPendingState = pending;
+                return;
+            }
 
-            if (!enabled && mCurrentVrService != null && !immediate) {
+            if (!enabled && mCurrentVrService != null) {
                 // If we're transitioning out of VR mode, delay briefly to avoid expensive HAL calls
                 // and service bind/unbind in case we are immediately switching to another VR app.
                 if (mPendingState == null) {
@@ -848,7 +948,7 @@
                             PENDING_STATE_DELAY_MS);
                 }
 
-                mPendingState = new VrState(enabled, targetPackageName, userId, callingPackage);
+                mPendingState = pending;
                 return;
             } else {
                 mHandler.removeMessages(MSG_PENDING_VR_STATE_CHANGE);
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 10c321b..3281bd6 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4342,9 +4342,8 @@
         Preconditions.checkArgument(timeoutMs >= 0, "Timeout must not be a negative number.");
         // timeoutMs with value 0 means that the admin doesn't participate
         // timeoutMs is clamped to the interval in case the internal constants change in the future
-        final long minimumStrongAuthTimeout = getMinimumStrongAuthTimeoutMs();
-        if (timeoutMs != 0 && timeoutMs < minimumStrongAuthTimeout) {
-            timeoutMs = minimumStrongAuthTimeout;
+        if (timeoutMs != 0 && timeoutMs < MINIMUM_STRONG_AUTH_TIMEOUT_MS) {
+            timeoutMs = MINIMUM_STRONG_AUTH_TIMEOUT_MS;
         }
         if (timeoutMs > DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS) {
             timeoutMs = DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS;
@@ -4388,19 +4387,10 @@
                     strongAuthUnlockTimeout = Math.min(timeout, strongAuthUnlockTimeout);
                 }
             }
-            return Math.max(strongAuthUnlockTimeout, getMinimumStrongAuthTimeoutMs());
+            return Math.max(strongAuthUnlockTimeout, MINIMUM_STRONG_AUTH_TIMEOUT_MS);
         }
     }
 
-    private long getMinimumStrongAuthTimeoutMs() {
-        if (!mInjector.isBuildDebuggable()) {
-            return MINIMUM_STRONG_AUTH_TIMEOUT_MS;
-        }
-        return Math.min(mInjector.systemPropertiesGetLong("persist.sys.min_strong_auth_timeout",
-                MINIMUM_STRONG_AUTH_TIMEOUT_MS),
-                MINIMUM_STRONG_AUTH_TIMEOUT_MS);
-    }
-
     @Override
     public void lockNow(boolean parent) {
         if (!mHasFeature) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
index 5f464bd..49ae2bc3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
@@ -46,11 +46,11 @@
 
     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 AtomicBoolean mIsLoggingEnabled;
 
     private final INetdEventCallback mNetdEventCallback = new INetdEventCallback.Stub() {
         @Override
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/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/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index cb27af1..6bc4c19 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -71,6 +71,7 @@
 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;
@@ -1289,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) {
@@ -1297,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.
      */
@@ -1695,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 =
@@ -1968,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;
     }
@@ -2030,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/ShortcutManagerTest8.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
index de344c2..fbf0ed2 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
@@ -15,7 +15,10 @@
  */
 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;
@@ -31,10 +34,14 @@
 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;
 
 /**
@@ -45,6 +52,11 @@
  -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 {
@@ -56,6 +68,14 @@
         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));
@@ -188,6 +208,23 @@
                 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);
 
@@ -203,14 +240,15 @@
         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 = makeShortcut("s1");
+            ShortcutInfo s1 = makeShortcutWithIcon("s1", res32x32);
 
             assertTrue(mManager.requestPinShortcut(s1,
                     resultIntent == null ? null : resultIntent.getIntentSender()));
 
-            verify(mServiceContext, times(0))
-                    .sendIntentSender(any(IntentSender.class));
+            verify(mServiceContext, times(0)).sendIntentSender(any(IntentSender.class));
 
             // Shortcut shouldn't be registered yet.
             assertWith(getCallerShortcuts())
@@ -232,16 +270,18 @@
 
             assertWith(request.getShortcutInfo())
                     .haveIds("s1")
-                    .areAllOrphan();
+                    .areAllOrphan()
+                    .areAllWithNoIntent();
 
-            // Can't test icons; need to test on CTS.
+            assertAllHaveIcon(list(request.getShortcutInfo()));
 
             // Accept the request.
-            request.accept();
+            assertForLauncherCallbackNoThrow(mLauncherApps,
+                    () -> assertTrue(request.accept()))
+                    .assertCallbackCalledForPackageAndUser(CALLING_PACKAGE_1, HANDLE_USER_P0)
+                    .haveIds("s1");
         });
 
-        // Check from the launcher side, including callback
-
         // This method is always called, even with PI == null.
         if (resultIntent == null) {
             verify(mServiceContext, times(1)).sendIntentSender(eq(null));
@@ -254,7 +294,8 @@
                     .haveIds("s1")
                     .areAllNotDynamic()
                     .areAllEnabled()
-                    .areAllPinned();
+                    .areAllPinned()
+                    .areAllWithIntent();
         });
     }
 
@@ -269,23 +310,919 @@
         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:
-    // Shortcut exists as a dynamic shortcut.
-    // Shortcut exists as a manifest shortcut.
-    // Shortcut exists as a dynamic, already pinned by this launcher
-    // Shortcut exists as a manifest, already pinned by this launcher
-    // Shortcut exists as floating, already pinned by this launcher
-
-    // Shortcut exists as a dynamic, already pinned by another launcher
-    // Shortcut exists as a manifest, already pinned by another launcher
-    // Shortcut exists as floating, already pinned by another launcher
-
-    // Shortcut exists but disabled (both mutable and immutable)
-
-    // Shortcut exists but removed before accept().
-    // Shortcut exists but disabled before accept().
-    // Shortcut exists but pinned before accept().
-    // Shortcut exists but unpinned before accept().
 
     // Cancel previous pending request and release memory?
+
+    // Check the launcher callback too.
+
+    // Missing fields -- pre and post, both.
 }
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 8ecea71..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
@@ -934,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++) {
@@ -1092,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/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/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;
+}