Merge "Add tests for getResource*Name()" into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index 95eeeee..0177e4f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -22280,7 +22280,7 @@
     method public void subscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback);
     method public void subscribe(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SubscriptionCallback);
     method public void unsubscribe(java.lang.String);
-    method public void unsubscribe(java.lang.String, android.os.Bundle);
+    method public void unsubscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback);
     field public static final java.lang.String EXTRA_PAGE = "android.media.browse.extra.PAGE";
     field public static final java.lang.String EXTRA_PAGE_SIZE = "android.media.browse.extra.PAGE_SIZE";
   }
@@ -23053,6 +23053,7 @@
     method public abstract void onStartRecording(android.net.Uri);
     method public abstract void onStopRecording();
     method public abstract void onTune(android.net.Uri);
+    method public void onTune(android.net.Uri, android.os.Bundle);
   }
 
   public static abstract class TvInputService.Session implements android.view.KeyEvent.Callback {
@@ -23102,6 +23103,7 @@
     method public void startRecording(android.net.Uri);
     method public void stopRecording();
     method public void tune(java.lang.String, android.net.Uri);
+    method public void tune(java.lang.String, android.net.Uri, android.os.Bundle);
   }
 
   public static abstract class TvRecordingClient.RecordingCallback {
@@ -30268,7 +30270,7 @@
     method public android.print.PrinterInfo build();
     method public android.print.PrinterInfo.Builder setCapabilities(android.print.PrinterCapabilitiesInfo);
     method public android.print.PrinterInfo.Builder setDescription(java.lang.String);
-    method public android.print.PrinterInfo.Builder setHasCustomPrinterIcon();
+    method public android.print.PrinterInfo.Builder setHasCustomPrinterIcon(boolean);
     method public android.print.PrinterInfo.Builder setIconResourceId(int);
     method public android.print.PrinterInfo.Builder setInfoIntent(android.app.PendingIntent);
     method public android.print.PrinterInfo.Builder setName(java.lang.String);
@@ -30320,6 +30322,7 @@
     method public boolean isStarted();
     method public void setProgress(float);
     method public void setStatus(java.lang.CharSequence);
+    method public void setStatus(int);
     method public boolean setTag(java.lang.String);
     method public boolean start();
   }
@@ -30396,6 +30399,7 @@
   public class BlockedNumberContract {
     method public static boolean canCurrentUserBlockNumbers(android.content.Context);
     method public static boolean isBlocked(android.content.Context, java.lang.String);
+    method public static int unblock(android.content.Context, java.lang.String);
     field public static final java.lang.String AUTHORITY = "com.android.blockednumber";
     field public static final android.net.Uri AUTHORITY_URI;
   }
@@ -32388,6 +32392,7 @@
     field public static final java.lang.String ACTION_VOICE_CONTROL_BATTERY_SAVER_MODE = "android.settings.VOICE_CONTROL_BATTERY_SAVER_MODE";
     field public static final java.lang.String ACTION_VOICE_CONTROL_DO_NOT_DISTURB_MODE = "android.settings.VOICE_CONTROL_DO_NOT_DISTURB_MODE";
     field public static final java.lang.String ACTION_VOICE_INPUT_SETTINGS = "android.settings.VOICE_INPUT_SETTINGS";
+    field public static final java.lang.String ACTION_VPN_SETTINGS = "android.settings.VPN_SETTINGS";
     field public static final java.lang.String ACTION_VR_LISTENER_SETTINGS = "android.settings.VR_LISTENER_SETTINGS";
     field public static final java.lang.String ACTION_WIFI_IP_SETTINGS = "android.settings.WIFI_IP_SETTINGS";
     field public static final java.lang.String ACTION_WIFI_SETTINGS = "android.settings.WIFI_SETTINGS";
@@ -36141,6 +36146,7 @@
     method public final android.telecom.CallAudioState getCallAudioState();
     method public final java.util.List<android.telecom.Connection> getConferenceableConnections();
     method public final int getConnectionCapabilities();
+    method public final int getConnectionProperties();
     method public final long getConnectionTime();
     method public final java.util.List<android.telecom.Connection> getConnections();
     method public final android.telecom.DisconnectCause getDisconnectCause();
@@ -36168,6 +36174,7 @@
     method public final void setActive();
     method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
     method public final void setConnectionCapabilities(int);
+    method public final void setConnectionProperties(int);
     method public final void setConnectionTime(long);
     method public final void setDialing();
     method public final void setDisconnected(android.telecom.DisconnectCause);
@@ -36197,6 +36204,7 @@
     method public final android.telecom.Conference getConference();
     method public final java.util.List<android.telecom.Conferenceable> getConferenceables();
     method public final int getConnectionCapabilities();
+    method public final int getConnectionProperties();
     method public final android.telecom.DisconnectCause getDisconnectCause();
     method public final android.os.Bundle getExtras();
     method public final int getState();
@@ -36222,6 +36230,7 @@
     method public void onUnhold();
     method public final void putExtras(android.os.Bundle);
     method public final void removeExtras(java.util.List<java.lang.String>);
+    method public static java.lang.String propertiesToString(int);
     method public void sendConnectionEvent(java.lang.String, android.os.Bundle);
     method public final void setActive();
     method public final void setAddress(android.net.Uri, int);
@@ -36230,6 +36239,7 @@
     method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
     method public final void setConferenceables(java.util.List<android.telecom.Conferenceable>);
     method public final void setConnectionCapabilities(int);
+    method public final void setConnectionProperties(int);
     method public final void setDialing();
     method public final void setDisconnected(android.telecom.DisconnectCause);
     method public final deprecated void setExtras(android.os.Bundle);
@@ -36246,12 +36256,11 @@
     method public static java.lang.String stateToString(int);
     field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 8388608; // 0x800000
     field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
-    field public static final int CAPABILITY_CAN_PULL_CALL = 33554432; // 0x2000000
     field public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 4194304; // 0x400000
+    field public static final int CAPABILITY_CAN_PULL_CALL = 16777216; // 0x1000000
     field public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 524288; // 0x80000
     field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
     field public static final int CAPABILITY_HOLD = 1; // 0x1
-    field public static final int CAPABILITY_IS_EXTERNAL_CALL = 16777216; // 0x1000000
     field public static final int CAPABILITY_MANAGE_CONFERENCE = 128; // 0x80
     field public static final int CAPABILITY_MERGE_CONFERENCE = 4; // 0x4
     field public static final int CAPABILITY_MUTE = 64; // 0x40
@@ -36269,6 +36278,7 @@
     field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
     field public static final java.lang.String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS";
     field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
+    field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
     field public static final int STATE_ACTIVE = 4; // 0x4
     field public static final int STATE_DIALING = 3; // 0x3
     field public static final int STATE_DISCONNECTED = 6; // 0x6
@@ -36491,6 +36501,7 @@
     method public void disconnect();
     method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
     method public final int getConnectionCapabilities();
+    method public final int getConnectionProperties();
     method public final java.util.List<android.telecom.RemoteConnection> getConnections();
     method public android.telecom.DisconnectCause getDisconnectCause();
     method public final android.os.Bundle getExtras();
@@ -36513,6 +36524,7 @@
     method public void onConferenceableConnectionsChanged(android.telecom.RemoteConference, java.util.List<android.telecom.RemoteConnection>);
     method public void onConnectionAdded(android.telecom.RemoteConference, android.telecom.RemoteConnection);
     method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConference, int);
+    method public void onConnectionPropertiesChanged(android.telecom.RemoteConference, int);
     method public void onConnectionRemoved(android.telecom.RemoteConference, android.telecom.RemoteConnection);
     method public void onDestroyed(android.telecom.RemoteConference);
     method public void onDisconnected(android.telecom.RemoteConference, android.telecom.DisconnectCause);
@@ -36531,6 +36543,7 @@
     method public android.telecom.RemoteConference getConference();
     method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
     method public int getConnectionCapabilities();
+    method public int getConnectionProperties();
     method public android.telecom.DisconnectCause getDisconnectCause();
     method public final android.os.Bundle getExtras();
     method public int getState();
@@ -36560,6 +36573,7 @@
     method public void onConferenceableConnectionsChanged(android.telecom.RemoteConnection, java.util.List<android.telecom.RemoteConnection>);
     method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConnection, int);
     method public void onConnectionEvent(android.telecom.RemoteConnection, java.lang.String, android.os.Bundle);
+    method public void onConnectionPropertiesChanged(android.telecom.RemoteConnection, int);
     method public void onDestroyed(android.telecom.RemoteConnection);
     method public void onDisconnected(android.telecom.RemoteConnection, android.telecom.DisconnectCause);
     method public void onExtrasChanged(android.telecom.RemoteConnection, android.os.Bundle);
@@ -51124,41 +51138,11 @@
     method public java.io.File directory();
     method public java.lang.ProcessBuilder directory(java.io.File);
     method public java.util.Map<java.lang.String, java.lang.String> environment();
-    method public java.lang.ProcessBuilder inheritIO();
-    method public java.lang.ProcessBuilder redirectError(java.lang.ProcessBuilder.Redirect);
-    method public java.lang.ProcessBuilder redirectError(java.io.File);
-    method public java.lang.ProcessBuilder.Redirect redirectError();
     method public boolean redirectErrorStream();
     method public java.lang.ProcessBuilder redirectErrorStream(boolean);
-    method public java.lang.ProcessBuilder redirectInput(java.lang.ProcessBuilder.Redirect);
-    method public java.lang.ProcessBuilder redirectInput(java.io.File);
-    method public java.lang.ProcessBuilder.Redirect redirectInput();
-    method public java.lang.ProcessBuilder redirectOutput(java.lang.ProcessBuilder.Redirect);
-    method public java.lang.ProcessBuilder redirectOutput(java.io.File);
-    method public java.lang.ProcessBuilder.Redirect redirectOutput();
     method public java.lang.Process start() throws java.io.IOException;
   }
 
-  public static abstract class ProcessBuilder.Redirect {
-    method public static java.lang.ProcessBuilder.Redirect appendTo(java.io.File);
-    method public java.io.File file();
-    method public static java.lang.ProcessBuilder.Redirect from(java.io.File);
-    method public static java.lang.ProcessBuilder.Redirect to(java.io.File);
-    method public abstract java.lang.ProcessBuilder.Redirect.Type type();
-    field public static final java.lang.ProcessBuilder.Redirect INHERIT;
-    field public static final java.lang.ProcessBuilder.Redirect PIPE;
-  }
-
-  public static final class ProcessBuilder.Redirect.Type extends java.lang.Enum {
-    method public static java.lang.ProcessBuilder.Redirect.Type valueOf(java.lang.String);
-    method public static final java.lang.ProcessBuilder.Redirect.Type[] values();
-    enum_constant public static final java.lang.ProcessBuilder.Redirect.Type APPEND;
-    enum_constant public static final java.lang.ProcessBuilder.Redirect.Type INHERIT;
-    enum_constant public static final java.lang.ProcessBuilder.Redirect.Type PIPE;
-    enum_constant public static final java.lang.ProcessBuilder.Redirect.Type READ;
-    enum_constant public static final java.lang.ProcessBuilder.Redirect.Type WRITE;
-  }
-
   public abstract interface Readable {
     method public abstract int read(java.nio.CharBuffer) throws java.io.IOException;
   }
diff --git a/api/system-current.txt b/api/system-current.txt
index 507a1a7..7956826 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -23845,7 +23845,7 @@
     method public void subscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback);
     method public void subscribe(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SubscriptionCallback);
     method public void unsubscribe(java.lang.String);
-    method public void unsubscribe(java.lang.String, android.os.Bundle);
+    method public void unsubscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback);
     field public static final java.lang.String EXTRA_PAGE = "android.media.browse.extra.PAGE";
     field public static final java.lang.String EXTRA_PAGE_SIZE = "android.media.browse.extra.PAGE_SIZE";
   }
@@ -32583,7 +32583,7 @@
     method public android.print.PrinterInfo build();
     method public android.print.PrinterInfo.Builder setCapabilities(android.print.PrinterCapabilitiesInfo);
     method public android.print.PrinterInfo.Builder setDescription(java.lang.String);
-    method public android.print.PrinterInfo.Builder setHasCustomPrinterIcon();
+    method public android.print.PrinterInfo.Builder setHasCustomPrinterIcon(boolean);
     method public android.print.PrinterInfo.Builder setIconResourceId(int);
     method public android.print.PrinterInfo.Builder setInfoIntent(android.app.PendingIntent);
     method public android.print.PrinterInfo.Builder setName(java.lang.String);
@@ -32635,6 +32635,7 @@
     method public boolean isStarted();
     method public void setProgress(float);
     method public void setStatus(java.lang.CharSequence);
+    method public void setStatus(int);
     method public boolean setTag(java.lang.String);
     method public boolean start();
   }
@@ -32711,6 +32712,7 @@
   public class BlockedNumberContract {
     method public static boolean canCurrentUserBlockNumbers(android.content.Context);
     method public static boolean isBlocked(android.content.Context, java.lang.String);
+    method public static int unblock(android.content.Context, java.lang.String);
     field public static final java.lang.String AUTHORITY = "com.android.blockednumber";
     field public static final android.net.Uri AUTHORITY_URI;
   }
@@ -34835,6 +34837,7 @@
     field public static final java.lang.String ACTION_VOICE_CONTROL_BATTERY_SAVER_MODE = "android.settings.VOICE_CONTROL_BATTERY_SAVER_MODE";
     field public static final java.lang.String ACTION_VOICE_CONTROL_DO_NOT_DISTURB_MODE = "android.settings.VOICE_CONTROL_DO_NOT_DISTURB_MODE";
     field public static final java.lang.String ACTION_VOICE_INPUT_SETTINGS = "android.settings.VOICE_INPUT_SETTINGS";
+    field public static final java.lang.String ACTION_VPN_SETTINGS = "android.settings.VPN_SETTINGS";
     field public static final java.lang.String ACTION_VR_LISTENER_SETTINGS = "android.settings.VR_LISTENER_SETTINGS";
     field public static final java.lang.String ACTION_WIFI_IP_SETTINGS = "android.settings.WIFI_IP_SETTINGS";
     field public static final java.lang.String ACTION_WIFI_SETTINGS = "android.settings.WIFI_SETTINGS";
@@ -38712,6 +38715,7 @@
     method public final java.util.List<android.telecom.Connection> getConferenceableConnections();
     method public final deprecated long getConnectTimeMillis();
     method public final int getConnectionCapabilities();
+    method public final int getConnectionProperties();
     method public final long getConnectionTime();
     method public final java.util.List<android.telecom.Connection> getConnections();
     method public final android.telecom.DisconnectCause getDisconnectCause();
@@ -38742,6 +38746,7 @@
     method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
     method public final deprecated void setConnectTimeMillis(long);
     method public final void setConnectionCapabilities(int);
+    method public final void setConnectionProperties(int);
     method public final void setConnectionTime(long);
     method public final void setDialing();
     method public final void setDisconnected(android.telecom.DisconnectCause);
@@ -38772,6 +38777,7 @@
     method public final android.telecom.Conference getConference();
     method public final java.util.List<android.telecom.Conferenceable> getConferenceables();
     method public final int getConnectionCapabilities();
+    method public final int getConnectionProperties();
     method public final android.telecom.DisconnectCause getDisconnectCause();
     method public final android.os.Bundle getExtras();
     method public final int getState();
@@ -38798,6 +38804,7 @@
     method public void onUnhold();
     method public final void putExtras(android.os.Bundle);
     method public final void removeExtras(java.util.List<java.lang.String>);
+    method public static java.lang.String propertiesToString(int);
     method public void sendConnectionEvent(java.lang.String, android.os.Bundle);
     method public final void setActive();
     method public final void setAddress(android.net.Uri, int);
@@ -38806,6 +38813,7 @@
     method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
     method public final void setConferenceables(java.util.List<android.telecom.Conferenceable>);
     method public final void setConnectionCapabilities(int);
+    method public final void setConnectionProperties(int);
     method public final void setDialing();
     method public final void setDisconnected(android.telecom.DisconnectCause);
     method public final deprecated void setExtras(android.os.Bundle);
@@ -38822,12 +38830,11 @@
     method public static java.lang.String stateToString(int);
     field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 8388608; // 0x800000
     field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
-    field public static final int CAPABILITY_CAN_PULL_CALL = 33554432; // 0x2000000
     field public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 4194304; // 0x400000
+    field public static final int CAPABILITY_CAN_PULL_CALL = 16777216; // 0x1000000
     field public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 524288; // 0x80000
     field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
     field public static final int CAPABILITY_HOLD = 1; // 0x1
-    field public static final int CAPABILITY_IS_EXTERNAL_CALL = 16777216; // 0x1000000
     field public static final int CAPABILITY_MANAGE_CONFERENCE = 128; // 0x80
     field public static final int CAPABILITY_MERGE_CONFERENCE = 4; // 0x4
     field public static final int CAPABILITY_MUTE = 64; // 0x40
@@ -38845,6 +38852,7 @@
     field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
     field public static final java.lang.String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS";
     field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
+    field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
     field public static final int STATE_ACTIVE = 4; // 0x4
     field public static final int STATE_DIALING = 3; // 0x3
     field public static final int STATE_DISCONNECTED = 6; // 0x6
@@ -39122,6 +39130,7 @@
     method public void disconnect();
     method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
     method public final int getConnectionCapabilities();
+    method public final int getConnectionProperties();
     method public final java.util.List<android.telecom.RemoteConnection> getConnections();
     method public android.telecom.DisconnectCause getDisconnectCause();
     method public final android.os.Bundle getExtras();
@@ -39145,6 +39154,7 @@
     method public void onConferenceableConnectionsChanged(android.telecom.RemoteConference, java.util.List<android.telecom.RemoteConnection>);
     method public void onConnectionAdded(android.telecom.RemoteConference, android.telecom.RemoteConnection);
     method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConference, int);
+    method public void onConnectionPropertiesChanged(android.telecom.RemoteConference, int);
     method public void onConnectionRemoved(android.telecom.RemoteConference, android.telecom.RemoteConnection);
     method public void onDestroyed(android.telecom.RemoteConference);
     method public void onDisconnected(android.telecom.RemoteConference, android.telecom.DisconnectCause);
@@ -39163,6 +39173,7 @@
     method public android.telecom.RemoteConference getConference();
     method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
     method public int getConnectionCapabilities();
+    method public int getConnectionProperties();
     method public android.telecom.DisconnectCause getDisconnectCause();
     method public final android.os.Bundle getExtras();
     method public int getState();
@@ -39193,6 +39204,7 @@
     method public void onConferenceableConnectionsChanged(android.telecom.RemoteConnection, java.util.List<android.telecom.RemoteConnection>);
     method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConnection, int);
     method public void onConnectionEvent(android.telecom.RemoteConnection, java.lang.String, android.os.Bundle);
+    method public void onConnectionPropertiesChanged(android.telecom.RemoteConnection, int);
     method public void onDestroyed(android.telecom.RemoteConnection);
     method public void onDisconnected(android.telecom.RemoteConnection, android.telecom.DisconnectCause);
     method public void onExtrasChanged(android.telecom.RemoteConnection, android.os.Bundle);
@@ -54189,41 +54201,11 @@
     method public java.io.File directory();
     method public java.lang.ProcessBuilder directory(java.io.File);
     method public java.util.Map<java.lang.String, java.lang.String> environment();
-    method public java.lang.ProcessBuilder inheritIO();
-    method public java.lang.ProcessBuilder redirectError(java.lang.ProcessBuilder.Redirect);
-    method public java.lang.ProcessBuilder redirectError(java.io.File);
-    method public java.lang.ProcessBuilder.Redirect redirectError();
     method public boolean redirectErrorStream();
     method public java.lang.ProcessBuilder redirectErrorStream(boolean);
-    method public java.lang.ProcessBuilder redirectInput(java.lang.ProcessBuilder.Redirect);
-    method public java.lang.ProcessBuilder redirectInput(java.io.File);
-    method public java.lang.ProcessBuilder.Redirect redirectInput();
-    method public java.lang.ProcessBuilder redirectOutput(java.lang.ProcessBuilder.Redirect);
-    method public java.lang.ProcessBuilder redirectOutput(java.io.File);
-    method public java.lang.ProcessBuilder.Redirect redirectOutput();
     method public java.lang.Process start() throws java.io.IOException;
   }
 
-  public static abstract class ProcessBuilder.Redirect {
-    method public static java.lang.ProcessBuilder.Redirect appendTo(java.io.File);
-    method public java.io.File file();
-    method public static java.lang.ProcessBuilder.Redirect from(java.io.File);
-    method public static java.lang.ProcessBuilder.Redirect to(java.io.File);
-    method public abstract java.lang.ProcessBuilder.Redirect.Type type();
-    field public static final java.lang.ProcessBuilder.Redirect INHERIT;
-    field public static final java.lang.ProcessBuilder.Redirect PIPE;
-  }
-
-  public static final class ProcessBuilder.Redirect.Type extends java.lang.Enum {
-    method public static java.lang.ProcessBuilder.Redirect.Type valueOf(java.lang.String);
-    method public static final java.lang.ProcessBuilder.Redirect.Type[] values();
-    enum_constant public static final java.lang.ProcessBuilder.Redirect.Type APPEND;
-    enum_constant public static final java.lang.ProcessBuilder.Redirect.Type INHERIT;
-    enum_constant public static final java.lang.ProcessBuilder.Redirect.Type PIPE;
-    enum_constant public static final java.lang.ProcessBuilder.Redirect.Type READ;
-    enum_constant public static final java.lang.ProcessBuilder.Redirect.Type WRITE;
-  }
-
   public abstract interface Readable {
     method public abstract int read(java.nio.CharBuffer) throws java.io.IOException;
   }
diff --git a/api/test-current.txt b/api/test-current.txt
index c382c3d..264b5eb 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -22345,7 +22345,7 @@
     method public void subscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback);
     method public void subscribe(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SubscriptionCallback);
     method public void unsubscribe(java.lang.String);
-    method public void unsubscribe(java.lang.String, android.os.Bundle);
+    method public void unsubscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback);
     field public static final java.lang.String EXTRA_PAGE = "android.media.browse.extra.PAGE";
     field public static final java.lang.String EXTRA_PAGE_SIZE = "android.media.browse.extra.PAGE_SIZE";
   }
@@ -23118,6 +23118,7 @@
     method public abstract void onStartRecording(android.net.Uri);
     method public abstract void onStopRecording();
     method public abstract void onTune(android.net.Uri);
+    method public void onTune(android.net.Uri, android.os.Bundle);
   }
 
   public static abstract class TvInputService.Session implements android.view.KeyEvent.Callback {
@@ -23167,6 +23168,7 @@
     method public void startRecording(android.net.Uri);
     method public void stopRecording();
     method public void tune(java.lang.String, android.net.Uri);
+    method public void tune(java.lang.String, android.net.Uri, android.os.Bundle);
   }
 
   public static abstract class TvRecordingClient.RecordingCallback {
@@ -30260,7 +30262,7 @@
     method public android.print.PrinterId getPrinterId();
     method public float getProgress();
     method public int getState();
-    method public java.lang.CharSequence getStatus();
+    method public java.lang.CharSequence getStatus(android.content.pm.PackageManager);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.print.PrintJobInfo> CREATOR;
     field public static final int STATE_BLOCKED = 4; // 0x4
@@ -30337,7 +30339,7 @@
     method public android.print.PrinterInfo build();
     method public android.print.PrinterInfo.Builder setCapabilities(android.print.PrinterCapabilitiesInfo);
     method public android.print.PrinterInfo.Builder setDescription(java.lang.String);
-    method public android.print.PrinterInfo.Builder setHasCustomPrinterIcon();
+    method public android.print.PrinterInfo.Builder setHasCustomPrinterIcon(boolean);
     method public android.print.PrinterInfo.Builder setIconResourceId(int);
     method public android.print.PrinterInfo.Builder setInfoIntent(android.app.PendingIntent);
     method public android.print.PrinterInfo.Builder setName(java.lang.String);
@@ -30389,6 +30391,7 @@
     method public boolean isStarted();
     method public void setProgress(float);
     method public void setStatus(java.lang.CharSequence);
+    method public void setStatus(int);
     method public boolean setTag(java.lang.String);
     method public boolean start();
   }
@@ -30465,6 +30468,7 @@
   public class BlockedNumberContract {
     method public static boolean canCurrentUserBlockNumbers(android.content.Context);
     method public static boolean isBlocked(android.content.Context, java.lang.String);
+    method public static int unblock(android.content.Context, java.lang.String);
     field public static final java.lang.String AUTHORITY = "com.android.blockednumber";
     field public static final android.net.Uri AUTHORITY_URI;
   }
@@ -32457,6 +32461,7 @@
     field public static final java.lang.String ACTION_VOICE_CONTROL_BATTERY_SAVER_MODE = "android.settings.VOICE_CONTROL_BATTERY_SAVER_MODE";
     field public static final java.lang.String ACTION_VOICE_CONTROL_DO_NOT_DISTURB_MODE = "android.settings.VOICE_CONTROL_DO_NOT_DISTURB_MODE";
     field public static final java.lang.String ACTION_VOICE_INPUT_SETTINGS = "android.settings.VOICE_INPUT_SETTINGS";
+    field public static final java.lang.String ACTION_VPN_SETTINGS = "android.settings.VPN_SETTINGS";
     field public static final java.lang.String ACTION_VR_LISTENER_SETTINGS = "android.settings.VR_LISTENER_SETTINGS";
     field public static final java.lang.String ACTION_WIFI_IP_SETTINGS = "android.settings.WIFI_IP_SETTINGS";
     field public static final java.lang.String ACTION_WIFI_SETTINGS = "android.settings.WIFI_SETTINGS";
@@ -36212,6 +36217,7 @@
     method public final android.telecom.CallAudioState getCallAudioState();
     method public final java.util.List<android.telecom.Connection> getConferenceableConnections();
     method public final int getConnectionCapabilities();
+    method public final int getConnectionProperties();
     method public final long getConnectionTime();
     method public final java.util.List<android.telecom.Connection> getConnections();
     method public final android.telecom.DisconnectCause getDisconnectCause();
@@ -36239,6 +36245,7 @@
     method public final void setActive();
     method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
     method public final void setConnectionCapabilities(int);
+    method public final void setConnectionProperties(int);
     method public final void setConnectionTime(long);
     method public final void setDialing();
     method public final void setDisconnected(android.telecom.DisconnectCause);
@@ -36268,6 +36275,7 @@
     method public final android.telecom.Conference getConference();
     method public final java.util.List<android.telecom.Conferenceable> getConferenceables();
     method public final int getConnectionCapabilities();
+    method public final int getConnectionProperties();
     method public final android.telecom.DisconnectCause getDisconnectCause();
     method public final android.os.Bundle getExtras();
     method public final int getState();
@@ -36293,6 +36301,7 @@
     method public void onUnhold();
     method public final void putExtras(android.os.Bundle);
     method public final void removeExtras(java.util.List<java.lang.String>);
+    method public static java.lang.String propertiesToString(int);
     method public void sendConnectionEvent(java.lang.String, android.os.Bundle);
     method public final void setActive();
     method public final void setAddress(android.net.Uri, int);
@@ -36301,6 +36310,7 @@
     method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
     method public final void setConferenceables(java.util.List<android.telecom.Conferenceable>);
     method public final void setConnectionCapabilities(int);
+    method public final void setConnectionProperties(int);
     method public final void setDialing();
     method public final void setDisconnected(android.telecom.DisconnectCause);
     method public final deprecated void setExtras(android.os.Bundle);
@@ -36317,12 +36327,11 @@
     method public static java.lang.String stateToString(int);
     field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 8388608; // 0x800000
     field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
-    field public static final int CAPABILITY_CAN_PULL_CALL = 33554432; // 0x2000000
     field public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 4194304; // 0x400000
+    field public static final int CAPABILITY_CAN_PULL_CALL = 16777216; // 0x1000000
     field public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 524288; // 0x80000
     field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
     field public static final int CAPABILITY_HOLD = 1; // 0x1
-    field public static final int CAPABILITY_IS_EXTERNAL_CALL = 16777216; // 0x1000000
     field public static final int CAPABILITY_MANAGE_CONFERENCE = 128; // 0x80
     field public static final int CAPABILITY_MERGE_CONFERENCE = 4; // 0x4
     field public static final int CAPABILITY_MUTE = 64; // 0x40
@@ -36340,6 +36349,7 @@
     field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
     field public static final java.lang.String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS";
     field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
+    field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
     field public static final int STATE_ACTIVE = 4; // 0x4
     field public static final int STATE_DIALING = 3; // 0x3
     field public static final int STATE_DISCONNECTED = 6; // 0x6
@@ -36562,6 +36572,7 @@
     method public void disconnect();
     method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
     method public final int getConnectionCapabilities();
+    method public final int getConnectionProperties();
     method public final java.util.List<android.telecom.RemoteConnection> getConnections();
     method public android.telecom.DisconnectCause getDisconnectCause();
     method public final android.os.Bundle getExtras();
@@ -36584,6 +36595,7 @@
     method public void onConferenceableConnectionsChanged(android.telecom.RemoteConference, java.util.List<android.telecom.RemoteConnection>);
     method public void onConnectionAdded(android.telecom.RemoteConference, android.telecom.RemoteConnection);
     method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConference, int);
+    method public void onConnectionPropertiesChanged(android.telecom.RemoteConference, int);
     method public void onConnectionRemoved(android.telecom.RemoteConference, android.telecom.RemoteConnection);
     method public void onDestroyed(android.telecom.RemoteConference);
     method public void onDisconnected(android.telecom.RemoteConference, android.telecom.DisconnectCause);
@@ -36602,6 +36614,7 @@
     method public android.telecom.RemoteConference getConference();
     method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
     method public int getConnectionCapabilities();
+    method public int getConnectionProperties();
     method public android.telecom.DisconnectCause getDisconnectCause();
     method public final android.os.Bundle getExtras();
     method public int getState();
@@ -36631,6 +36644,7 @@
     method public void onConferenceableConnectionsChanged(android.telecom.RemoteConnection, java.util.List<android.telecom.RemoteConnection>);
     method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConnection, int);
     method public void onConnectionEvent(android.telecom.RemoteConnection, java.lang.String, android.os.Bundle);
+    method public void onConnectionPropertiesChanged(android.telecom.RemoteConnection, int);
     method public void onDestroyed(android.telecom.RemoteConnection);
     method public void onDisconnected(android.telecom.RemoteConnection, android.telecom.DisconnectCause);
     method public void onExtrasChanged(android.telecom.RemoteConnection, android.os.Bundle);
@@ -51197,41 +51211,11 @@
     method public java.io.File directory();
     method public java.lang.ProcessBuilder directory(java.io.File);
     method public java.util.Map<java.lang.String, java.lang.String> environment();
-    method public java.lang.ProcessBuilder inheritIO();
-    method public java.lang.ProcessBuilder redirectError(java.lang.ProcessBuilder.Redirect);
-    method public java.lang.ProcessBuilder redirectError(java.io.File);
-    method public java.lang.ProcessBuilder.Redirect redirectError();
     method public boolean redirectErrorStream();
     method public java.lang.ProcessBuilder redirectErrorStream(boolean);
-    method public java.lang.ProcessBuilder redirectInput(java.lang.ProcessBuilder.Redirect);
-    method public java.lang.ProcessBuilder redirectInput(java.io.File);
-    method public java.lang.ProcessBuilder.Redirect redirectInput();
-    method public java.lang.ProcessBuilder redirectOutput(java.lang.ProcessBuilder.Redirect);
-    method public java.lang.ProcessBuilder redirectOutput(java.io.File);
-    method public java.lang.ProcessBuilder.Redirect redirectOutput();
     method public java.lang.Process start() throws java.io.IOException;
   }
 
-  public static abstract class ProcessBuilder.Redirect {
-    method public static java.lang.ProcessBuilder.Redirect appendTo(java.io.File);
-    method public java.io.File file();
-    method public static java.lang.ProcessBuilder.Redirect from(java.io.File);
-    method public static java.lang.ProcessBuilder.Redirect to(java.io.File);
-    method public abstract java.lang.ProcessBuilder.Redirect.Type type();
-    field public static final java.lang.ProcessBuilder.Redirect INHERIT;
-    field public static final java.lang.ProcessBuilder.Redirect PIPE;
-  }
-
-  public static final class ProcessBuilder.Redirect.Type extends java.lang.Enum {
-    method public static java.lang.ProcessBuilder.Redirect.Type valueOf(java.lang.String);
-    method public static final java.lang.ProcessBuilder.Redirect.Type[] values();
-    enum_constant public static final java.lang.ProcessBuilder.Redirect.Type APPEND;
-    enum_constant public static final java.lang.ProcessBuilder.Redirect.Type INHERIT;
-    enum_constant public static final java.lang.ProcessBuilder.Redirect.Type PIPE;
-    enum_constant public static final java.lang.ProcessBuilder.Redirect.Type READ;
-    enum_constant public static final java.lang.ProcessBuilder.Redirect.Type WRITE;
-  }
-
   public abstract interface Readable {
     method public abstract int read(java.nio.CharBuffer) throws java.io.IOException;
   }
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index ea53e59..c597ed2 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -68,14 +68,11 @@
 
 // ---------------------------------------------------------------------------
 
-BootAnimation::BootAnimation() : Thread(false), mZip(NULL), mClockEnabled(true) {
+BootAnimation::BootAnimation() : Thread(false), mClockEnabled(true) {
     mSession = new SurfaceComposerClient();
 }
 
 BootAnimation::~BootAnimation() {
-    if (mZip != NULL) {
-        delete mZip;
-    }
 }
 
 void BootAnimation::onFirstRef() {
@@ -288,19 +285,15 @@
 
     bool encryptedAnimation = atoi(decrypt) != 0 || !strcmp("trigger_restart_min_framework", decrypt);
 
-    ZipFileRO* zipFile = NULL;
-    if ((encryptedAnimation &&
-            (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0) &&
-            ((zipFile = ZipFileRO::open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE)) != NULL)) ||
-
-            ((access(OEM_BOOTANIMATION_FILE, R_OK) == 0) &&
-            ((zipFile = ZipFileRO::open(OEM_BOOTANIMATION_FILE)) != NULL)) ||
-
-            ((access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) &&
-            ((zipFile = ZipFileRO::open(SYSTEM_BOOTANIMATION_FILE)) != NULL))) {
-        mZip = zipFile;
+    if (encryptedAnimation && (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0)) {
+        mZipFileName = SYSTEM_ENCRYPTED_BOOTANIMATION_FILE;
     }
-
+    else if (access(OEM_BOOTANIMATION_FILE, R_OK) == 0) {
+        mZipFileName = OEM_BOOTANIMATION_FILE;
+    }
+    else if (access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) {
+        mZipFileName = SYSTEM_BOOTANIMATION_FILE;
+    }
     return NO_ERROR;
 }
 
@@ -309,7 +302,7 @@
     bool r;
     // We have no bootanimation file, so we use the stock android logo
     // animation.
-    if (mZip == NULL) {
+    if (mZipFileName.isEmpty()) {
         r = android();
     } else {
         r = movie();
@@ -429,16 +422,17 @@
     return true;
 }
 
-bool BootAnimation::readFile(const char* name, String8& outString)
+
+static bool readFile(ZipFileRO* zip, const char* name, String8& outString)
 {
-    ZipEntryRO entry = mZip->findEntryByName(name);
+    ZipEntryRO entry = zip->findEntryByName(name);
     ALOGE_IF(!entry, "couldn't find %s", name);
     if (!entry) {
         return false;
     }
 
-    FileMap* entryMap = mZip->createEntryFileMap(entry);
-    mZip->releaseEntry(entry);
+    FileMap* entryMap = zip->createEntryFileMap(entry);
+    zip->releaseEntry(entry);
     ALOGE_IF(!entryMap, "entryMap is null");
     if (!entryMap) {
         return false;
@@ -512,18 +506,18 @@
     glBindTexture(GL_TEXTURE_2D, 0);
 }
 
-bool BootAnimation::movie()
+bool BootAnimation::parseAnimationDesc(Animation& animation)
 {
     String8 desString;
 
-    if (!readFile("desc.txt", desString)) {
+    if (!readFile(animation.zip, "desc.txt", desString)) {
         return false;
     }
     char const* s = desString.string();
 
     // Create and initialize an AudioPlayer if we have an audio_conf.txt file
     String8 audioConf;
-    if (readFile("audio_conf.txt", audioConf)) {
+    if (readFile(animation.zip, "audio_conf.txt", audioConf)) {
         mAudioPlayer = new AudioPlayer;
         if (!mAudioPlayer->init(audioConf.string())) {
             ALOGE("mAudioPlayer.init failed");
@@ -531,8 +525,6 @@
         }
     }
 
-    Animation animation;
-
     // Parse the description file
     for (;;) {
         const char* endl = strstr(s, "\n");
@@ -564,6 +556,7 @@
             part.path = path;
             part.clockPosY = clockPosY;
             part.audioFile = NULL;
+            part.animation = NULL;
             if (!parseColor(color, part.backgroundColor)) {
                 ALOGE("> invalid color '#%s'", color);
                 part.backgroundColor[0] = 0.0f;
@@ -572,13 +565,29 @@
             }
             animation.parts.add(part);
         }
-
+        else if (strcmp(l, "$SYSTEM") == 0) {
+            // ALOGD("> SYSTEM");
+            Animation::Part part;
+            part.playUntilComplete = false;
+            part.count = 1;
+            part.pause = 0;
+            part.audioFile = NULL;
+            part.animation = loadAnimation(String8(SYSTEM_BOOTANIMATION_FILE));
+            if (part.animation != NULL)
+                animation.parts.add(part);
+        }
         s = ++endl;
     }
 
+    return true;
+}
+
+bool BootAnimation::preloadZip(Animation& animation)
+{
     // read all the data structures
     const size_t pcount = animation.parts.size();
     void *cookie = NULL;
+    ZipFileRO* mZip = animation.zip;
     if (!mZip->startIteration(&cookie)) {
         return false;
     }
@@ -624,6 +633,16 @@
 
     mZip->endIteration(cookie);
 
+    return true;
+}
+
+bool BootAnimation::movie()
+{
+
+    Animation* animation = loadAnimation(mZipFileName);
+    if (animation == NULL)
+        return false;
+
     // Blend required to draw time on top of animation frames.
     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
     glShadeModel(GL_FLAT);
@@ -645,6 +664,19 @@
         mClockEnabled = clockTextureInitialized;
     }
 
+    playAnimation(*animation);
+    releaseAnimation(animation);
+
+    if (clockTextureInitialized) {
+        glDeleteTextures(1, &mClock.name);
+    }
+
+    return false;
+}
+
+bool BootAnimation::playAnimation(const Animation& animation)
+{
+    const size_t pcount = animation.parts.size();
     const int xc = (mWidth - animation.width) / 2;
     const int yc = ((mHeight - animation.height) / 2);
     nsecs_t frameDuration = s2ns(1) / animation.fps;
@@ -657,6 +689,14 @@
         const size_t fcount = part.frames.size();
         glBindTexture(GL_TEXTURE_2D, 0);
 
+        // Handle animation package
+        if (part.animation != NULL) {
+            playAnimation(*part.animation);
+            if (exitPending())
+                break;
+            continue; //to next part
+        }
+
         for (int r=0 ; !part.count || r<part.count ; r++) {
             // Exit any non playuntil complete parts immediately
             if(exitPending() && !part.playUntilComplete)
@@ -744,14 +784,46 @@
             }
         }
     }
-
-    if (clockTextureInitialized) {
-        glDeleteTextures(1, &mClock.name);
-    }
-
-    return false;
+    return true;
 }
 
+void BootAnimation::releaseAnimation(Animation* animation) const
+{
+    for (Vector<Animation::Part>::iterator it = animation->parts.begin(),
+         e = animation->parts.end(); it != e; ++it) {
+        if (it->animation)
+            releaseAnimation(it->animation);
+    }
+    if (animation->zip)
+        delete animation->zip;
+    delete animation;
+}
+
+BootAnimation::Animation* BootAnimation::loadAnimation(const String8& fn)
+{
+    if (mLoadedFiles.indexOf(fn) >= 0) {
+        ALOGE("File \"%s\" is already loaded. Cyclic ref is not allowed",
+            fn.string());
+        return NULL;
+    }
+    ZipFileRO *zip = ZipFileRO::open(fn);
+    if (zip == NULL) {
+        ALOGE("Failed to open animation zip \"%s\": %s",
+            fn.string(), strerror(errno));
+        return NULL;
+    }
+
+    Animation *animation =  new Animation;
+    animation->fileName = fn;
+    animation->zip = zip;
+    mLoadedFiles.add(animation->fileName);
+
+    parseAnimationDesc(*animation);
+    preloadZip(*animation);
+
+    mLoadedFiles.remove(fn);
+    return animation;
+}
 // ---------------------------------------------------------------------------
 
 }
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index 83e2b38..d49e1ec 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -76,19 +76,27 @@
             bool playUntilComplete;
             float backgroundColor[3];
             FileMap* audioFile;
+            Animation* animation;
         };
         int fps;
         int width;
         int height;
         Vector<Part> parts;
+        String8 audioConf;
+        String8 fileName;
+        ZipFileRO* zip;
     };
 
     status_t initTexture(Texture* texture, AssetManager& asset, const char* name);
     status_t initTexture(const Animation::Frame& frame);
     bool android();
-    bool readFile(const char* name, String8& outString);
     bool movie();
     void drawTime(const Texture& clockTex, const int yPos);
+    Animation* loadAnimation(const String8&);
+    bool playAnimation(const Animation&);
+    void releaseAnimation(Animation*) const;
+    bool parseAnimationDesc(Animation&);
+    bool preloadZip(Animation &animation);
 
     void checkExit();
 
@@ -104,8 +112,9 @@
     EGLDisplay  mSurface;
     sp<SurfaceControl> mFlingerSurfaceControl;
     sp<Surface> mFlingerSurface;
-    ZipFileRO   *mZip;
     bool        mClockEnabled;
+    String8     mZipFileName;
+    SortedVector<String8> mLoadedFiles;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 663f297..c6a5152 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -967,7 +967,7 @@
         AnimationHandler animationHandler = AnimationHandler.getInstance();
         animationHandler.addAnimationFrameCallback(this, (long) (mStartDelay * sDurationScale));
 
-        if (mStartDelay == 0) {
+        if (mStartDelay == 0 || mSeekFraction >= 0) {
             // If there's no start delay, init the animation and notify start listeners right away
             // to be consistent with the previous behavior. Otherwise, postpone this until the first
             // frame after the start delay.
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 2d33a2c..6380801 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -22,6 +22,7 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
+import android.content.res.Configuration;
 import android.graphics.Canvas;
 import android.graphics.Matrix;
 import android.graphics.Point;
@@ -1790,7 +1791,7 @@
 
         public int taskWidth;
         public int taskHeight;
-        public int screenOrientation;
+        public int screenOrientation = Configuration.ORIENTATION_UNDEFINED;
 
         public TaskThumbnailInfo() {
             // Do nothing
@@ -1807,7 +1808,16 @@
         public void reset() {
             taskWidth = 0;
             taskHeight = 0;
-            screenOrientation = 0;
+            screenOrientation = Configuration.ORIENTATION_UNDEFINED;
+        }
+
+        /**
+         * Copies from another ThumbnailInfo.
+         */
+        public void copyFrom(TaskThumbnailInfo o) {
+            taskWidth = o.taskWidth;
+            taskHeight = o.taskHeight;
+            screenOrientation = o.screenOrientation;
         }
 
         /** @hide */
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 5b7dae6..ed590e6 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -272,12 +272,17 @@
     }
 
     @Override
+    @SuppressWarnings("unchecked")
     public List<PermissionInfo> queryPermissionsByGroup(String group, int flags)
             throws NameNotFoundException {
         try {
-            List<PermissionInfo> pi = mPM.queryPermissionsByGroup(group, flags).getList();
-            if (pi != null) {
-                return pi;
+            ParceledListSlice<PermissionInfo> parceledList =
+                    mPM.queryPermissionsByGroup(group, flags);
+            if (parceledList != null) {
+                List<PermissionInfo> pi = parceledList.getList();
+                if (pi != null) {
+                    return pi;
+                }
             }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -288,7 +293,7 @@
 
     @Override
     public PermissionGroupInfo getPermissionGroupInfo(String name,
-                                                      int flags) throws NameNotFoundException {
+            int flags) throws NameNotFoundException {
         try {
             PermissionGroupInfo pgi = mPM.getPermissionGroupInfo(name, flags);
             if (pgi != null) {
@@ -302,9 +307,15 @@
     }
 
     @Override
+    @SuppressWarnings("unchecked")
     public List<PermissionGroupInfo> getAllPermissionGroups(int flags) {
         try {
-            return mPM.getAllPermissionGroups(flags).getList();
+            ParceledListSlice<PermissionGroupInfo> parceledList =
+                    mPM.getAllPermissionGroups(flags);
+            if (parceledList == null) {
+                return Collections.emptyList();
+            }
+            return parceledList.getList();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -439,9 +450,15 @@
     }
 
     @Override
+    @SuppressWarnings("unchecked")
     public FeatureInfo[] getSystemAvailableFeatures() {
         try {
-            final List<FeatureInfo> list = mPM.getSystemAvailableFeatures().getList();
+            ParceledListSlice<FeatureInfo> parceledList =
+                    mPM.getSystemAvailableFeatures();
+            if (parceledList == null) {
+                return new FeatureInfo[0];
+            }
+            final List<FeatureInfo> list = parceledList.getList();
             final FeatureInfo[] res = new FeatureInfo[list.size()];
             for (int i = 0; i < res.length; i++) {
                 res[i] = list.get(i);
@@ -636,10 +653,15 @@
 
     /** @hide */
     @Override
+    @SuppressWarnings("unchecked")
     public List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId) {
         try {
-            ParceledListSlice<PackageInfo> slice = mPM.getInstalledPackages(flags, userId);
-            return slice.getList();
+            ParceledListSlice<PackageInfo> parceledList =
+                    mPM.getInstalledPackages(flags, userId);
+            if (parceledList == null) {
+                return Collections.emptyList();
+            }
+            return parceledList.getList();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -651,9 +673,12 @@
             String[] permissions, int flags) {
         final int userId = mContext.getUserId();
         try {
-            ParceledListSlice<PackageInfo> slice = mPM.getPackagesHoldingPermissions(
-                    permissions, flags, userId);
-            return slice.getList();
+            ParceledListSlice<PackageInfo> parceledList =
+                    mPM.getPackagesHoldingPermissions(permissions, flags, userId);
+            if (parceledList == null) {
+                return Collections.emptyList();
+            }
+            return parceledList.getList();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -664,8 +689,12 @@
     public List<ApplicationInfo> getInstalledApplications(int flags) {
         final int userId = mContext.getUserId();
         try {
-            ParceledListSlice<ApplicationInfo> slice = mPM.getInstalledApplications(flags, userId);
-            return slice.getList();
+            ParceledListSlice<ApplicationInfo> parceledList =
+                    mPM.getInstalledApplications(flags, userId);
+            if (parceledList == null) {
+                return Collections.emptyList();
+            }
+            return parceledList.getList();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -770,20 +799,25 @@
 
     /** @hide Same as above but for a specific user */
     @Override
+    @SuppressWarnings("unchecked")
     public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent,
-                                                   int flags, int userId) {
+            int flags, int userId) {
         try {
-            return mPM.queryIntentActivities(
-                intent,
-                intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                flags,
-                userId).getList();
+            ParceledListSlice<ResolveInfo> parceledList =
+                    mPM.queryIntentActivities(intent,
+                            intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+                            flags, userId);
+            if (parceledList == null) {
+                return Collections.emptyList();
+            }
+            return parceledList.getList();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     @Override
+    @SuppressWarnings("unchecked")
     public List<ResolveInfo> queryIntentActivityOptions(
         ComponentName caller, Intent[] specifics, Intent intent,
         int flags) {
@@ -807,10 +841,13 @@
         }
 
         try {
-            return mPM
-                    .queryIntentActivityOptions(caller, specifics, specificTypes, intent,
-                            intent.resolveTypeIfNeeded(resolver), flags, mContext.getUserId())
-                    .getList();
+            ParceledListSlice<ResolveInfo> parceledList =
+                    mPM.queryIntentActivityOptions(caller, specifics, specificTypes, intent,
+                    intent.resolveTypeIfNeeded(resolver), flags, mContext.getUserId());
+            if (parceledList == null) {
+                return Collections.emptyList();
+            }
+            return parceledList.getList();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -820,13 +857,17 @@
      * @hide
      */
     @Override
+    @SuppressWarnings("unchecked")
     public List<ResolveInfo> queryBroadcastReceiversAsUser(Intent intent, int flags, int userId) {
         try {
-            return mPM.queryIntentReceivers(
-                intent,
-                intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                flags,
-                userId).getList();
+            ParceledListSlice<ResolveInfo> parceledList =
+                    mPM.queryIntentReceivers(intent,
+                            intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+                            flags,  userId);
+            if (parceledList == null) {
+                return Collections.emptyList();
+            }
+            return parceledList.getList();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -851,13 +892,17 @@
     }
 
     @Override
+    @SuppressWarnings("unchecked")
     public List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int flags, int userId) {
         try {
-            return mPM.queryIntentServices(
-                intent,
-                intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                flags,
-                userId).getList();
+            ParceledListSlice<ResolveInfo> parceledList =
+                    mPM.queryIntentServices(intent,
+                    intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+                    flags, userId);
+            if (parceledList == null) {
+                return Collections.emptyList();
+            }
+            return parceledList.getList();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -869,12 +914,18 @@
     }
 
     @Override
+    @SuppressWarnings("unchecked")
     public List<ResolveInfo> queryIntentContentProvidersAsUser(
             Intent intent, int flags, int userId) {
         try {
-            return mPM.queryIntentContentProviders(intent,
-                    intent.resolveTypeIfNeeded(mContext.getContentResolver()), flags, userId)
-                    .getList();
+            ParceledListSlice<ResolveInfo> parceledList =
+                    mPM.queryIntentContentProviders(intent,
+                            intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+                            flags, userId);
+            if (parceledList == null) {
+                return Collections.emptyList();
+            }
+            return parceledList.getList();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -901,12 +952,13 @@
     }
 
     @Override
+    @SuppressWarnings("unchecked")
     public List<ProviderInfo> queryContentProviders(String processName,
-                                                    int uid, int flags) {
+            int uid, int flags) {
         try {
-            ParceledListSlice<ProviderInfo> slice
-                    = mPM.queryContentProviders(processName, uid, flags);
-            return slice != null ? slice.getList() : null;
+            ParceledListSlice<ProviderInfo> slice =
+                    mPM.queryContentProviders(processName, uid, flags);
+            return slice != null ? slice.getList() : Collections.<ProviderInfo>emptyList();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -930,10 +982,16 @@
     }
 
     @Override
+    @SuppressWarnings("unchecked")
     public List<InstrumentationInfo> queryInstrumentation(
         String targetPackage, int flags) {
         try {
-            return mPM.queryInstrumentation(targetPackage, flags).getList();
+            ParceledListSlice<InstrumentationInfo> parceledList =
+                    mPM.queryInstrumentation(targetPackage, flags);
+            if (parceledList == null) {
+                return Collections.emptyList();
+            }
+            return parceledList.getList();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1575,18 +1633,30 @@
     }
 
     @Override
+    @SuppressWarnings("unchecked")
     public List<IntentFilterVerificationInfo> getIntentFilterVerifications(String packageName) {
         try {
-            return mPM.getIntentFilterVerifications(packageName).getList();
+            ParceledListSlice<IntentFilterVerificationInfo> parceledList =
+                    mPM.getIntentFilterVerifications(packageName);
+            if (parceledList == null) {
+                return Collections.emptyList();
+            }
+            return parceledList.getList();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     @Override
+    @SuppressWarnings("unchecked")
     public List<IntentFilter> getAllIntentFilters(String packageName) {
         try {
-            return mPM.getAllIntentFilters(packageName).getList();
+            ParceledListSlice<IntentFilter> parceledList =
+                    mPM.getAllIntentFilters(packageName);
+            if (parceledList == null) {
+                return Collections.emptyList();
+            }
+            return parceledList.getList();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index db7ca2a..84605bb 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1158,10 +1158,15 @@
         StrictJarFile jarFile = null;
         try {
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "strictJarFileCtor");
+            // Ignore signature stripping protections when verifying APKs from system partition.
+            // For those APKs we only care about extracting signer certificates, and don't care
+            // about verifying integrity.
+            boolean signatureSchemeRollbackProtectionsEnforced =
+                    (parseFlags & PARSE_IS_SYSTEM) == 0;
             jarFile = new StrictJarFile(
                     apkPath,
-                    !verified // whether to verify JAR signature
-                    );
+                    !verified, // whether to verify JAR signature
+                    signatureSchemeRollbackProtectionsEnforced);
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
 
             // Always verify manifest, regardless of source
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 9e360e1..20c2168 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -200,14 +200,6 @@
      */
     public static final int CMD_PREVENT_AUTOMATIC_RECONNECT = BASE + 15;
 
-    /**
-     * Sent by ConnectivityService to the NetworkAgent to install an APF program in the network
-     * chipset for use to filter packets.
-     *
-     * obj = byte[] containing the APF program bytecode.
-     */
-    public static final int CMD_PUSH_APF_PROGRAM = BASE + 16;
-
     public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
             NetworkCapabilities nc, LinkProperties lp, int score) {
         this(looper, context, logTag, ni, nc, lp, score, null);
@@ -327,10 +319,6 @@
                 preventAutomaticReconnect();
                 break;
             }
-            case CMD_PUSH_APF_PROGRAM: {
-                installPacketFilter((byte[]) msg.obj);
-                break;
-            }
         }
     }
 
@@ -506,15 +494,6 @@
     protected void preventAutomaticReconnect() {
     }
 
-    /**
-     * Install a packet filter.
-     * @param filter an APF program to filter incoming packets.
-     * @return {@code true} if filter successfully installed, {@code false} otherwise.
-     */
-    protected boolean installPacketFilter(byte[] filter) {
-        return false;
-    }
-
     protected void log(String s) {
         Log.d(LOG_TAG, "NetworkAgent: " + s);
     }
diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkMisc.java
index 748699e..5511a24 100644
--- a/core/java/android/net/NetworkMisc.java
+++ b/core/java/android/net/NetworkMisc.java
@@ -56,22 +56,6 @@
      */
     public String subscriberId;
 
-    /**
-     * Version of APF instruction set supported for packet filtering. 0 indicates no support for
-     * packet filtering using APF programs.
-     */
-    public int apfVersionSupported;
-
-    /**
-     * Maximum size of APF program allowed.
-     */
-    public int maximumApfProgramSize;
-
-    /**
-     * Format of packets passed to APF filter. Should be one of ARPHRD_*
-     */
-    public int apfPacketFormat;
-
     public NetworkMisc() {
     }
 
@@ -81,9 +65,6 @@
             explicitlySelected = nm.explicitlySelected;
             acceptUnvalidated = nm.acceptUnvalidated;
             subscriberId = nm.subscriberId;
-            apfVersionSupported = nm.apfVersionSupported;
-            maximumApfProgramSize = nm.maximumApfProgramSize;
-            apfPacketFormat = nm.apfPacketFormat;
         }
     }
 
@@ -98,9 +79,6 @@
         out.writeInt(explicitlySelected ? 1 : 0);
         out.writeInt(acceptUnvalidated ? 1 : 0);
         out.writeString(subscriberId);
-        out.writeInt(apfVersionSupported);
-        out.writeInt(maximumApfProgramSize);
-        out.writeInt(apfPacketFormat);
     }
 
     public static final Creator<NetworkMisc> CREATOR = new Creator<NetworkMisc>() {
@@ -111,9 +89,6 @@
             networkMisc.explicitlySelected = in.readInt() != 0;
             networkMisc.acceptUnvalidated = in.readInt() != 0;
             networkMisc.subscriberId = in.readString();
-            networkMisc.apfVersionSupported = in.readInt();
-            networkMisc.maximumApfProgramSize = in.readInt();
-            networkMisc.apfPacketFormat = in.readInt();
             return networkMisc;
         }
 
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index d847cd0..b32b2cc 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -18,6 +18,7 @@
 
 import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
 import static android.net.ConnectivityManager.TYPE_ETHERNET;
+import static android.net.ConnectivityManager.TYPE_PROXY;
 import static android.net.ConnectivityManager.TYPE_WIFI;
 import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
 import static android.net.ConnectivityManager.TYPE_WIMAX;
@@ -68,6 +69,7 @@
     public static final int MATCH_MOBILE_WILDCARD = 6;
     public static final int MATCH_WIFI_WILDCARD = 7;
     public static final int MATCH_BLUETOOTH = 8;
+    public static final int MATCH_PROXY = 9;
 
     /**
      * Set of {@link NetworkInfo#getType()} that reflect data usage.
@@ -157,6 +159,14 @@
         return new NetworkTemplate(MATCH_BLUETOOTH, null, null);
     }
 
+    /**
+     * Template to combine all {@link ConnectivityManager#TYPE_PROXY} style
+     * networks together.
+     */
+    public static NetworkTemplate buildTemplateProxy() {
+        return new NetworkTemplate(MATCH_PROXY, null, null);
+    }
+
     private final int mMatchRule;
     private final String mSubscriberId;
 
@@ -293,6 +303,8 @@
                 return matchesWifiWildcard(ident);
             case MATCH_BLUETOOTH:
                 return matchesBluetooth(ident);
+            case MATCH_PROXY:
+                return matchesProxy(ident);
             default:
                 throw new IllegalArgumentException("unknown network template");
         }
@@ -401,6 +413,13 @@
         return false;
     }
 
+    /**
+     * Check if matches Proxy network template.
+     */
+    private boolean matchesProxy(NetworkIdentity ident) {
+        return ident.mType == TYPE_PROXY;
+    }
+
     private static String getMatchRuleName(int matchRule) {
         switch (matchRule) {
             case MATCH_MOBILE_3G_LOWER:
@@ -419,6 +438,8 @@
                 return "WIFI_WILDCARD";
             case MATCH_BLUETOOTH:
                 return "BLUETOOTH";
+            case MATCH_PROXY:
+                return "PROXY";
             default:
                 return "UNKNOWN";
         }
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index e730ad8..be82d56a 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -30,6 +30,8 @@
 import android.telephony.SignalStrength;
 import android.text.format.DateFormat;
 import android.util.ArrayMap;
+import android.util.MutableBoolean;
+import android.util.Pair;
 import android.util.Printer;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
@@ -5317,26 +5319,28 @@
         }
 
         if (apps != null) {
-            SparseArray<ArrayList<String>> uids = new SparseArray<ArrayList<String>>();
+            SparseArray<Pair<ArrayList<String>, MutableBoolean>> uids = new SparseArray<>();
             for (int i=0; i<apps.size(); i++) {
                 ApplicationInfo ai = apps.get(i);
-                ArrayList<String> pkgs = uids.get(ai.uid);
+                Pair<ArrayList<String>, MutableBoolean> pkgs = uids.get(
+                        UserHandle.getAppId(ai.uid));
                 if (pkgs == null) {
-                    pkgs = new ArrayList<String>();
-                    uids.put(ai.uid, pkgs);
+                    pkgs = new Pair<>(new ArrayList<String>(), new MutableBoolean(false));
+                    uids.put(UserHandle.getAppId(ai.uid), pkgs);
                 }
-                pkgs.add(ai.packageName);
+                pkgs.first.add(ai.packageName);
             }
             SparseArray<? extends Uid> uidStats = getUidStats();
             final int NU = uidStats.size();
             String[] lineArgs = new String[2];
             for (int i=0; i<NU; i++) {
-                int uid = uidStats.keyAt(i);
-                ArrayList<String> pkgs = uids.get(uid);
-                if (pkgs != null) {
-                    for (int j=0; j<pkgs.size(); j++) {
+                int uid = UserHandle.getAppId(uidStats.keyAt(i));
+                Pair<ArrayList<String>, MutableBoolean> pkgs = uids.get(uid);
+                if (pkgs != null && !pkgs.second.value) {
+                    pkgs.second.value = true;
+                    for (int j=0; j<pkgs.first.size(); j++) {
                         lineArgs[0] = Integer.toString(uid);
-                        lineArgs[1] = pkgs.get(j);
+                        lineArgs[1] = pkgs.first.get(j);
                         dumpLine(pw, 0 /* uid */, "i" /* category */, UID_DATA,
                                 (Object[])lineArgs);
                     }
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index de8b690..d0029e1 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -91,6 +91,12 @@
     /** The name of the hardware (from the kernel command line or /proc). */
     public static final String HARDWARE = getString("ro.hardware");
 
+    /**
+     * Whether this build was for an emulator device.
+     * @hide
+     */
+    public static final boolean IS_EMULATOR = getString("ro.kernel.qemu").equals("1");
+
     /** A hardware serial number, if available.  Alphanumeric only, case-insensitive. */
     public static final String SERIAL = getString("ro.serialno");
 
diff --git a/core/java/android/os/HardwarePropertiesManager.java b/core/java/android/os/HardwarePropertiesManager.java
index 0f2e33d..f13e5b5 100644
--- a/core/java/android/os/HardwarePropertiesManager.java
+++ b/core/java/android/os/HardwarePropertiesManager.java
@@ -101,7 +101,8 @@
      *         {@link #UNDEFINED_TEMPERATURE} if undefined.
      *         Empty if platform doesn't provide the queried temperature.
      *
-     * @throws SecurityException if a non profile or device owner tries to call this method.
+     * @throws SecurityException if something other than the profile or device owner, or the
+     *        current VR service tries to retrieve information provided by this service.
     */
     public @NonNull float[] getDeviceTemperatures(@DeviceTemperatureType int type,
             @TemperatureSource int source) {
@@ -137,7 +138,8 @@
      *         each unplugged core.
      *         Empty if CPU usage is not supported on this system.
      *
-     * @throws SecurityException if a non profile or device owner tries to call this method.
+     * @throws SecurityException if something other than the profile or device owner, or the
+     *        current VR service tries to retrieve information provided by this service.
      */
     public @NonNull CpuUsageInfo[] getCpuUsages() {
         try {
@@ -153,7 +155,8 @@
      * @return an array of float fan speeds in RPM. Empty if there are no fans or fan speed is not
      * supported on this system.
      *
-     * @throws SecurityException if a non profile or device owner tries to call this method.
+     * @throws SecurityException if something other than the profile or device owner, or the
+     *        current VR service tries to retrieve information provided by this service.
      */
     public @NonNull float[] getFanSpeeds() {
         try {
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 100f6a1..24a6cdf 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -293,7 +293,9 @@
     /**
      * Control network activity of a UID over interfaces with a quota limit.
      */
-    void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces);
+    void setUidMeteredNetworkBlacklist(int uid, boolean enable);
+    void setUidMeteredNetworkWhitelist(int uid, boolean enable);
+    boolean setDataSaverModeEnabled(boolean enable);
 
     void setUidCleartextNetworkPolicy(int uid, int policy);
 
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index 17176ec..34c0b14 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -16,7 +16,6 @@
 
 package android.os.storage;
 
-import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.Intent;
@@ -336,7 +335,7 @@
      *
      * @see DocumentsContract
      */
-    public Intent createAccessIntent(@NonNull String directoryName) {
+    public Intent createAccessIntent(String directoryName) {
         final Intent intent = new Intent(ACTION_OPEN_EXTERNAL_DIRECTORY);
         intent.putExtra(EXTRA_STORAGE_VOLUME, this);
         intent.putExtra(EXTRA_DIRECTORY_NAME, directoryName);
diff --git a/core/java/android/print/IPrintSpooler.aidl b/core/java/android/print/IPrintSpooler.aidl
index 469a4ea..63bf885 100644
--- a/core/java/android/print/IPrintSpooler.aidl
+++ b/core/java/android/print/IPrintSpooler.aidl
@@ -61,6 +61,15 @@
     void setStatus(in PrintJobId printJobId, in CharSequence status);
 
     /**
+     * Set the status of this print job
+     *
+     * @param printJobId The print job to update
+     * @param status The new status as a string resource
+     * @param appPackageName App package name the resource belongs to
+     */
+    void setStatusRes(in PrintJobId printJobId, int status, in CharSequence appPackageName);
+
+    /**
      * Handle that a custom icon for a printer was loaded.
      *
      * @param printerId the id of the printer the icon belongs to
diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java
index 7e3a72f..f134943 100644
--- a/core/java/android/print/PrintJobInfo.java
+++ b/core/java/android/print/PrintJobInfo.java
@@ -21,7 +21,10 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.StringRes;
 import android.annotation.TestApi;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -181,7 +184,11 @@
     private float mProgress;
 
     /** A short string describing the status of this job. */
-    private CharSequence mStatus;
+    private @Nullable CharSequence mStatus;
+
+    /** A string resource describing the status of this job. */
+    private @StringRes int mStatusRes;
+    private @Nullable CharSequence mStatusResAppPackageName;
 
     /** Advanced printer specific options. */
     private Bundle mAdvancedOptions;
@@ -210,6 +217,8 @@
         mDocumentInfo = other.mDocumentInfo;
         mProgress = other.mProgress;
         mStatus = other.mStatus;
+        mStatusRes = other.mStatusRes;
+        mStatusResAppPackageName = other.mStatusResAppPackageName;
         mCanceling = other.mCanceling;
         mAdvancedOptions = other.mAdvancedOptions;
     }
@@ -235,8 +244,14 @@
         mDocumentInfo = (PrintDocumentInfo) parcel.readParcelable(null);
         mProgress = parcel.readFloat();
         mStatus = parcel.readCharSequence();
+        mStatusRes = parcel.readInt();
+        mStatusResAppPackageName = parcel.readCharSequence();
         mCanceling = (parcel.readInt() == 1);
         mAdvancedOptions = parcel.readBundle();
+
+        if (mAdvancedOptions != null) {
+            Preconditions.checkArgument(!mAdvancedOptions.containsKey(null));
+        }
     }
 
     /**
@@ -370,10 +385,28 @@
      * @hide
      */
     public void setStatus(@Nullable CharSequence status) {
+        mStatusRes = 0;
+        mStatusResAppPackageName = null;
+
         mStatus = status;
     }
 
     /**
+     * Sets the status of the print job.
+     *
+     * @param status The new status as a string resource
+     * @param appPackageName App package name the resource belongs to
+     *
+     * @hide
+     */
+    public void setStatus(@StringRes int status, @NonNull CharSequence appPackageName) {
+        mStatus = null;
+
+        mStatusRes = status;
+        mStatusResAppPackageName = appPackageName;
+    }
+
+    /**
      * Sets the owning application id.
      *
      * @return The owning app id.
@@ -633,6 +666,8 @@
         parcel.writeParcelable(mDocumentInfo, 0);
         parcel.writeFloat(mProgress);
         parcel.writeCharSequence(mStatus);
+        parcel.writeInt(mStatusRes);
+        parcel.writeCharSequence(mStatusResAppPackageName);
         parcel.writeInt(mCanceling ? 1 : 0);
         parcel.writeBundle(mAdvancedOptions);
     }
@@ -659,6 +694,9 @@
         builder.append(", progress: " + mProgress);
         builder.append(", status: " + (mStatus != null
                 ? mStatus.toString() : null));
+        builder.append(", statusRes: " + mStatusRes);
+        builder.append(", statusResAppPackageName: " + (mStatusResAppPackageName != null
+                ? mStatusResAppPackageName.toString() : null));
         builder.append("}");
         return builder.toString();
     }
@@ -707,12 +745,23 @@
     /**
      * Get the status of this job.
      *
+     * @param pm Package manager used to resolve the string
+     *
      * @return the status of this job or null if not set
      * @hide
      */
     @TestApi
-    public @Nullable CharSequence getStatus() {
-        return mStatus;
+    public @Nullable CharSequence getStatus(@NonNull PackageManager pm) {
+        if (mStatusRes == 0) {
+            return mStatus;
+        } else {
+            try {
+                return pm.getResourcesForApplication(mStatusResAppPackageName.toString())
+                        .getString(mStatusRes);
+            } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
+                return null;
+            }
+        }
     }
 
     /**
@@ -789,6 +838,8 @@
          * @param value The option value.
          */
         public void putAdvancedOption(@NonNull String key, @Nullable String value) {
+            Preconditions.checkNotNull(key, "key cannot be null");
+
             if (mPrototype.mAdvancedOptions == null) {
                 mPrototype.mAdvancedOptions = new Bundle();
             }
diff --git a/core/java/android/print/PrinterInfo.java b/core/java/android/print/PrinterInfo.java
index 0d2d9f4..1ee6389 100644
--- a/core/java/android/print/PrinterInfo.java
+++ b/core/java/android/print/PrinterInfo.java
@@ -467,10 +467,12 @@
          * {@link android.printservice.PrinterDiscoverySession#onRequestCustomPrinterIcon}.
          * </p>
          *
+         * @param hasCustomPrinterIcon If the printer has a custom icon or not.
+         *
          * @return This builder.
          */
-        public @NonNull Builder setHasCustomPrinterIcon() {
-            mHasCustomPrinterIcon = true;
+        public @NonNull Builder setHasCustomPrinterIcon(boolean hasCustomPrinterIcon) {
+            mHasCustomPrinterIcon = hasCustomPrinterIcon;
             return this;
         }
 
diff --git a/core/java/android/printservice/IPrintServiceClient.aidl b/core/java/android/printservice/IPrintServiceClient.aidl
index 0ae1e18..f0ea6ae 100644
--- a/core/java/android/printservice/IPrintServiceClient.aidl
+++ b/core/java/android/printservice/IPrintServiceClient.aidl
@@ -52,6 +52,15 @@
      */
     void setStatus(in PrintJobId printJobId, in CharSequence status);
 
+    /**
+     * Set the status of this print job
+     *
+     * @param printJobId The print job to update
+     * @param status The new status as a string resource
+     * @param appPackageName The app package name the string belongs to
+     */
+    void setStatusRes(in PrintJobId printJobId, int status, in CharSequence appPackageName);
+
     void onPrintersAdded(in ParceledListSlice printers);
     void onPrintersRemoved(in ParceledListSlice printerIds);
 
diff --git a/core/java/android/printservice/PrintJob.java b/core/java/android/printservice/PrintJob.java
index 6414b6a..7a7ca23 100644
--- a/core/java/android/printservice/PrintJob.java
+++ b/core/java/android/printservice/PrintJob.java
@@ -20,11 +20,14 @@
 import android.annotation.MainThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.StringRes;
+import android.content.Context;
 import android.os.RemoteException;
 import android.print.PrintJobId;
 import android.print.PrintJobInfo;
 import android.text.TextUtils;
 import android.util.Log;
+import com.android.internal.util.Preconditions;
 
 /**
  * This class represents a print job from the perspective of a print
@@ -45,7 +48,12 @@
 
     private PrintJobInfo mCachedInfo;
 
-    PrintJob(@NonNull PrintJobInfo jobInfo, @NonNull IPrintServiceClient client) {
+    /** Context that created the object */
+    private final Context mContext;
+
+    PrintJob(@NonNull Context context, @NonNull PrintJobInfo jobInfo,
+            @NonNull IPrintServiceClient client) {
+        mContext = context;
         mCachedInfo = jobInfo;
         mPrintServiceClient = client;
         mDocument = new PrintDocument(mCachedInfo.getId(), client,
@@ -216,11 +224,10 @@
     }
 
     /**
-     * Blocks the print job. You should call this method if {@link
-     * #isStarted()} or {@link #isBlocked()} returns true and you need
-     * to block the print job. For example, the user has to add some
-     * paper to continue printing. To resume the print job call {@link
-     * #start()}.
+     * Blocks the print job. You should call this method if {@link #isStarted()} returns true and
+     * you need to block the print job. For example, the user has to add some paper to continue
+     * printing. To resume the print job call {@link #start()}. To change the reason call
+     * {@link #setStatus(CharSequence)}.
      *
      * @param reason The human readable, short, and translated reason why the print job is blocked.
      * @return Whether the job was blocked.
@@ -233,9 +240,7 @@
         PrintService.throwIfNotCalledOnMainThread();
         PrintJobInfo info = getInfo();
         final int state = info.getState();
-        if (state == PrintJobInfo.STATE_STARTED
-                || (state == PrintJobInfo.STATE_BLOCKED
-                        && !TextUtils.equals(info.getStatus(), reason))) {
+        if (state == PrintJobInfo.STATE_STARTED || state == PrintJobInfo.STATE_BLOCKED) {
             return setState(PrintJobInfo.STATE_BLOCKED, reason);
         }
         return false;
@@ -320,6 +325,9 @@
     /**
      * Sets the status of this print job. This should be a human readable, short, and translated
      * description of the current state of the print job.
+     * <p />
+     * This overrides any previously set status set via {@link #setStatus(CharSequence)},
+     * {@link #setStatus(int)}, {@link #block(String)}, or {@link #fail(String)},
      *
      * @param status The new status. If null the status will be empty.
      */
@@ -335,6 +343,29 @@
     }
 
     /**
+     * Sets the status of this print job as a string resource.
+     * <p />
+     * This overrides any previously set status set via {@link #setStatus(CharSequence)},
+     * {@link #setStatus(int)}, {@link #block(String)}, or {@link #fail(String)},
+     * <p />
+     * To clear the status use {@link #setStatus(CharSequence) <code>setStatus(null)</code>}
+     *
+     * @param status  The new status as a String resource.
+     */
+    @MainThread
+    public void setStatus(@StringRes int status) {
+        PrintService.throwIfNotCalledOnMainThread();
+        Preconditions.checkArgument(status != 0, "status has to be != 0");
+
+        try {
+            mPrintServiceClient.setStatusRes(mCachedInfo.getId(), status,
+                    mContext.getPackageName());
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error setting status for job: " + mCachedInfo.getId(), re);
+        }
+    }
+
+    /**
      * Sets a tag that is valid in the context of a {@link PrintService}
      * and is not interpreted by the system. For example, a print service
      * may set as a tag the key of the print job returned by a remote
diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java
index 62d214e..8f73518 100644
--- a/core/java/android/printservice/PrintService.java
+++ b/core/java/android/printservice/PrintService.java
@@ -329,7 +329,7 @@
                 final int printJobInfoCount = printJobInfos.size();
                 printJobs = new ArrayList<PrintJob>(printJobInfoCount);
                 for (int i = 0; i < printJobInfoCount; i++) {
-                    printJobs.add(new PrintJob(printJobInfos.get(i), mClient));
+                    printJobs.add(new PrintJob(this, printJobInfos.get(i), mClient));
                 }
             }
             if (printJobs != null) {
@@ -549,7 +549,7 @@
                                 + getPackageName());
                     }
                     PrintJobInfo printJobInfo = (PrintJobInfo) message.obj;
-                    onRequestCancelPrintJob(new PrintJob(printJobInfo, mClient));
+                    onRequestCancelPrintJob(new PrintJob(PrintService.this, printJobInfo, mClient));
                 } break;
 
                 case MSG_ON_PRINTJOB_QUEUED: {
@@ -561,7 +561,7 @@
                     if (DEBUG) {
                         Log.i(LOG_TAG, "Queued: " + printJobInfo);
                     }
-                    onPrintJobQueued(new PrintJob(printJobInfo, mClient));
+                    onPrintJobQueued(new PrintJob(PrintService.this, printJobInfo, mClient));
                 } break;
 
                 case MSG_SET_CLIENT: {
diff --git a/core/java/android/provider/BlockedNumberContract.java b/core/java/android/provider/BlockedNumberContract.java
index 6fe0189..e90dc9c 100644
--- a/core/java/android/provider/BlockedNumberContract.java
+++ b/core/java/android/provider/BlockedNumberContract.java
@@ -15,6 +15,7 @@
  */
 package android.provider;
 
+import android.annotation.WorkerThread;
 import android.content.Context;
 import android.net.Uri;
 import android.os.Bundle;
@@ -109,6 +110,8 @@
  * Uri uri = getContentResolver().insert(BlockedNumbers.CONTENT_URI, values);
  * getContentResolver().delete(uri, null, null);
  * </pre>
+ * To check if a particular number is blocked, use the method
+ * {@link #isBlocked(Context, String)}.
  * </p>
  * </dd>
  * <dt><b>Query</b></dt>
@@ -120,8 +123,12 @@
  *          new String[]{BlockedNumbers.COLUMN_ID, BlockedNumbers.COLUMN_ORIGINAL_NUMBER,
  *          BlockedNumbers.COLUMN_E164_NUMBER}, null, null, null);
  * </pre>
- * To check if a particular number is blocked, use the method
- * {@link #isBlocked(Context, String)}.
+ * </p>
+ * </dd>
+ * <dt><b>Unblock</b></dt>
+ * <dd>
+ * <p>
+ * Use the method {@link #unblock(Context, String)} to unblock numbers.
  * </p>
  * </dd>
  *
@@ -206,9 +213,15 @@
     public static final String METHOD_IS_BLOCKED = "is_blocked";
 
     /** @hide */
+    public static final String METHOD_UNBLOCK= "unblock";
+
+    /** @hide */
     public static final String RES_NUMBER_IS_BLOCKED = "blocked";
 
     /** @hide */
+    public static final String RES_NUM_ROWS_DELETED = "num_deleted";
+
+    /** @hide */
     public static final String METHOD_CAN_CURRENT_USER_BLOCK_NUMBERS =
             "can_current_user_block_numbers";
 
@@ -217,9 +230,15 @@
 
     /**
      * Returns whether a given number is in the blocked list.
+     *
+     * <p> This matches the {@code phoneNumber} against the
+     * {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} column, and the E164 representation of the
+     * {@code phoneNumber} with the {@link BlockedNumbers#COLUMN_E164_NUMBER} column.
+     *
      * <p> Note that if the {@link #canCurrentUserBlockNumbers} is {@code false} for the user
      * context {@code context}, this method will throw an {@link UnsupportedOperationException}.
      */
+    @WorkerThread
     public static boolean isBlocked(Context context, String phoneNumber) {
         final Bundle res = context.getContentResolver().call(
                 AUTHORITY_URI, METHOD_IS_BLOCKED, phoneNumber, null);
@@ -227,6 +246,30 @@
     }
 
     /**
+     * Unblocks the {@code phoneNumber} if it is blocked.
+     *
+     * <p> Returns the number of rows deleted in the blocked number provider as a result of unblock.
+     *
+     * <p> This deletes all rows where the {@code phoneNumber} matches the
+     * {@link BlockedNumbers#COLUMN_ORIGINAL_NUMBER} column or the E164 representation of the
+     * {@code phoneNumber} matches the {@link BlockedNumbers#COLUMN_E164_NUMBER} column.
+     *
+     * <p>To delete rows based on exact match with specific columns such as
+     * {@link BlockedNumbers#COLUMN_ID} use
+     * {@link android.content.ContentProvider#delete(Uri, String, String[])} with
+     * {@link BlockedNumbers#CONTENT_URI} URI.
+     *
+     * <p> Note that if the {@link #canCurrentUserBlockNumbers} is {@code false} for the user
+     * context {@code context}, this method will throw an {@link UnsupportedOperationException}.
+     */
+    @WorkerThread
+    public static int unblock(Context context, String phoneNumber) {
+        final Bundle res = context.getContentResolver().call(
+                AUTHORITY_URI, METHOD_UNBLOCK, phoneNumber, null);
+        return res.getInt(RES_NUM_ROWS_DELETED, 0);
+    }
+
+    /**
      * Returns {@code true} if blocking numbers is supported for the current user.
      * <p> Typically, blocking numbers is only supported for one user at a time.
      */
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d8937b4..f7e0e03 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -322,6 +322,20 @@
             "android.settings.PRIVACY_SETTINGS";
 
     /**
+     * Activity Action: Show settings to allow configuration of VPN.
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you
+     * safeguard against this.
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_VPN_SETTINGS =
+            "android.settings.VPN_SETTINGS";
+
+    /**
      * Activity Action: Show settings to allow configuration of Wi-Fi.
      * <p>
      * In some cases, a matching Activity may not exist, so ensure you
@@ -7699,6 +7713,16 @@
         public static final String DEVICE_IDLE_CONSTANTS = "device_idle_constants";
 
         /**
+         * Device Idle (Doze) specific settings for watches. See {@code #DEVICE_IDLE_CONSTANTS}
+         *
+         * <p>
+         * Type: string
+         * @hide
+         * @see com.android.server.DeviceIdleController.Constants
+         */
+        public static final String DEVICE_IDLE_CONSTANTS_WATCH = "device_idle_constants_watch";
+
+        /**
          * App standby (app idle) specific settings.
          * This is encoded as a key=value list, separated by commas. Ex:
          *
diff --git a/core/java/android/util/jar/StrictJarFile.java b/core/java/android/util/jar/StrictJarFile.java
index 302a08d..5d94b06 100644
--- a/core/java/android/util/jar/StrictJarFile.java
+++ b/core/java/android/util/jar/StrictJarFile.java
@@ -58,11 +58,22 @@
 
     public StrictJarFile(String fileName)
             throws IOException, SecurityException {
-        this(fileName, true);
+        this(fileName, true, true);
     }
 
-    public StrictJarFile(String fileName, boolean verify)
-            throws IOException, SecurityException {
+    /**
+     *
+     * @param verify whether to verify the file's JAR signatures and collect the corresponding
+     *        signer certificates.
+     * @param signatureSchemeRollbackProtectionsEnforced {@code true} to enforce protections against
+     *        stripping newer signature schemes (e.g., APK Signature Scheme v2) from the file, or
+     *        {@code false} to ignore any such protections. This parameter is ignored when
+     *        {@code verify} is {@code false}.
+     */
+    public StrictJarFile(String fileName,
+            boolean verify,
+            boolean signatureSchemeRollbackProtectionsEnforced)
+                    throws IOException, SecurityException {
         this.nativeHandle = nativeOpenJarFile(fileName);
         this.raf = new RandomAccessFile(fileName, "r");
 
@@ -73,7 +84,12 @@
             if (verify) {
                 HashMap<String, byte[]> metaEntries = getMetaEntries();
                 this.manifest = new StrictJarManifest(metaEntries.get(JarFile.MANIFEST_NAME), true);
-                this.verifier = new StrictJarVerifier(fileName, manifest, metaEntries);
+                this.verifier =
+                        new StrictJarVerifier(
+                                fileName,
+                                manifest,
+                                metaEntries,
+                                signatureSchemeRollbackProtectionsEnforced);
                 Set<String> files = manifest.getEntries().keySet();
                 for (String file : files) {
                     if (findEntry(file) == null) {
diff --git a/core/java/android/util/jar/StrictJarVerifier.java b/core/java/android/util/jar/StrictJarVerifier.java
index 0546a5f..6da50ba 100644
--- a/core/java/android/util/jar/StrictJarVerifier.java
+++ b/core/java/android/util/jar/StrictJarVerifier.java
@@ -72,6 +72,7 @@
     private final StrictJarManifest manifest;
     private final HashMap<String, byte[]> metaEntries;
     private final int mainAttributesEnd;
+    private final boolean signatureSchemeRollbackProtectionsEnforced;
 
     private final Hashtable<String, HashMap<String, Attributes>> signatures =
             new Hashtable<String, HashMap<String, Attributes>>(5);
@@ -164,13 +165,19 @@
      *
      * @param name
      *            the name of the JAR file being verified.
+     *
+     * @param signatureSchemeRollbackProtectionsEnforced {@code true} to enforce protections against
+     *        stripping newer signature schemes (e.g., APK Signature Scheme v2) from the file, or
+     *        {@code false} to ignore any such protections.
      */
     StrictJarVerifier(String name, StrictJarManifest manifest,
-        HashMap<String, byte[]> metaEntries) {
+        HashMap<String, byte[]> metaEntries, boolean signatureSchemeRollbackProtectionsEnforced) {
         jarName = name;
         this.manifest = manifest;
         this.metaEntries = metaEntries;
         this.mainAttributesEnd = manifest.getMainAttributesEnd();
+        this.signatureSchemeRollbackProtectionsEnforced =
+                signatureSchemeRollbackProtectionsEnforced;
     }
 
     /**
@@ -357,40 +364,42 @@
             return;
         }
 
-        // Check whether APK Signature Scheme v2 signature was stripped.
-        String apkSignatureSchemeIdList =
-                attributes.getValue(
-                        ApkSignatureSchemeV2Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_NAME);
-        if (apkSignatureSchemeIdList != null) {
-            // This field contains a comma-separated list of APK signature scheme IDs which were
-            // used to sign this APK. If an ID is known to us, it means signatures of that scheme
-            // were stripped from the APK because otherwise we wouldn't have fallen back to
-            // verifying the APK using the JAR signature scheme.
-            boolean v2SignatureGenerated = false;
-            StringTokenizer tokenizer = new StringTokenizer(apkSignatureSchemeIdList, ",");
-            while (tokenizer.hasMoreTokens()) {
-                String idText = tokenizer.nextToken().trim();
-                if (idText.isEmpty()) {
-                    continue;
+        // If requested, check whether APK Signature Scheme v2 signature was stripped.
+        if (signatureSchemeRollbackProtectionsEnforced) {
+            String apkSignatureSchemeIdList =
+                    attributes.getValue(
+                            ApkSignatureSchemeV2Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_NAME);
+            if (apkSignatureSchemeIdList != null) {
+                // This field contains a comma-separated list of APK signature scheme IDs which
+                // were used to sign this APK. If an ID is known to us, it means signatures of that
+                // scheme were stripped from the APK because otherwise we wouldn't have fallen back
+                // to verifying the APK using the JAR signature scheme.
+                boolean v2SignatureGenerated = false;
+                StringTokenizer tokenizer = new StringTokenizer(apkSignatureSchemeIdList, ",");
+                while (tokenizer.hasMoreTokens()) {
+                    String idText = tokenizer.nextToken().trim();
+                    if (idText.isEmpty()) {
+                        continue;
+                    }
+                    int id;
+                    try {
+                        id = Integer.parseInt(idText);
+                    } catch (Exception ignored) {
+                        continue;
+                    }
+                    if (id == ApkSignatureSchemeV2Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_ID) {
+                        // This APK was supposed to be signed with APK Signature Scheme v2 but no
+                        // such signature was found.
+                        v2SignatureGenerated = true;
+                        break;
+                    }
                 }
-                int id;
-                try {
-                    id = Integer.parseInt(idText);
-                } catch (Exception ignored) {
-                    continue;
-                }
-                if (id == ApkSignatureSchemeV2Verifier.SF_ATTRIBUTE_ANDROID_APK_SIGNED_ID) {
-                    // This APK was supposed to be signed with APK Signature Scheme v2 but no such
-                    // signature was found.
-                    v2SignatureGenerated = true;
-                    break;
-                }
-            }
 
-            if (v2SignatureGenerated) {
-                throw new SecurityException(signatureFile + " indicates " + jarName + " is signed"
-                        + " using APK Signature Scheme v2, but no such signature was found."
-                        + " Signature stripped?");
+                if (v2SignatureGenerated) {
+                    throw new SecurityException(signatureFile + " indicates " + jarName
+                            + " is signed using APK Signature Scheme v2, but no such signature was"
+                            + " found. Signature stripped?");
+                }
             }
         }
 
diff --git a/core/java/android/view/HardwareLayer.java b/core/java/android/view/HardwareLayer.java
index a12434c..41c44f1 100644
--- a/core/java/android/view/HardwareLayer.java
+++ b/core/java/android/view/HardwareLayer.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.annotation.Nullable;
 import android.graphics.Bitmap;
 import android.graphics.Matrix;
 import android.graphics.Paint;
@@ -51,8 +52,8 @@
      * @param paint The paint used when the layer is drawn into the destination canvas.
      * @see View#setLayerPaint(android.graphics.Paint)
      */
-    public void setLayerPaint(Paint paint) {
-        nSetLayerPaint(mFinalizer.get(), paint.getNativeInstance());
+    public void setLayerPaint(@Nullable Paint paint) {
+        nSetLayerPaint(mFinalizer.get(), paint != null ? paint.getNativeInstance() : 0);
         mRenderer.pushLayerUpdate(this);
     }
 
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index a45c18d..a19254f 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -259,7 +259,7 @@
         return nSetLayerType(mNativeRenderNode, layerType);
     }
 
-    public boolean setLayerPaint(Paint paint) {
+    public boolean setLayerPaint(@Nullable Paint paint) {
         return nSetLayerPaint(mNativeRenderNode, paint != null ? paint.getNativeInstance() : 0);
     }
 
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 1be4810..1a712c3 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
@@ -133,7 +134,6 @@
      */
     public TextureView(Context context) {
         super(context);
-        init();
     }
 
     /**
@@ -144,7 +144,6 @@
      */
     public TextureView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        init();
     }
 
     /**
@@ -158,7 +157,6 @@
      */
     public TextureView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        init();
     }
 
     /**
@@ -176,11 +174,6 @@
      */
     public TextureView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
-        init();
-    }
-
-    private void init() {
-        mLayerPaint = new Paint();
     }
 
     /**
@@ -260,7 +253,7 @@
      * method will however be taken into account when rendering the content of
      * this TextureView.
      *
-     * @param layerType The ype of layer to use with this view, must be one of
+     * @param layerType The type of layer to use with this view, must be one of
      *        {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or
      *        {@link #LAYER_TYPE_HARDWARE}
      * @param paint The paint used to compose the layer. This argument is optional
@@ -268,16 +261,16 @@
      *        {@link #LAYER_TYPE_NONE}
      */
     @Override
-    public void setLayerType(int layerType, Paint paint) {
-        if (paint != mLayerPaint) {
-            mLayerPaint = paint == null ? new Paint() : paint;
-            invalidate();
-        }
+    public void setLayerType(int layerType, @Nullable Paint paint) {
+        setLayerPaint(paint);
     }
 
     @Override
-    public void setLayerPaint(Paint paint) {
-        setLayerType(/* ignored */ 0, paint);
+    public void setLayerPaint(@Nullable Paint paint) {
+        if (paint != mLayerPaint) {
+            mLayerPaint = paint;
+            invalidate();
+        }
     }
 
     /**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 6fa2aad..83c6e9e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -11741,7 +11741,7 @@
         }
     }
 
-   /**
+    /**
      * Utility method to retrieve the inverse of the current mMatrix property.
      * We cache the matrix to avoid recalculating it when transform properties
      * have not changed.
@@ -15626,7 +15626,7 @@
      *
      * @attr ref android.R.styleable#View_layerType
      */
-    public void setLayerType(int layerType, Paint paint) {
+    public void setLayerType(int layerType, @Nullable Paint paint) {
         if (layerType < LAYER_TYPE_NONE || layerType > LAYER_TYPE_HARDWARE) {
             throw new IllegalArgumentException("Layer type can only be one of: LAYER_TYPE_NONE, "
                     + "LAYER_TYPE_SOFTWARE or LAYER_TYPE_HARDWARE");
@@ -15645,8 +15645,7 @@
         }
 
         mLayerType = layerType;
-        final boolean layerDisabled = (mLayerType == LAYER_TYPE_NONE);
-        mLayerPaint = layerDisabled ? null : (paint == null ? new Paint() : paint);
+        mLayerPaint = mLayerType == LAYER_TYPE_NONE ? null : paint;
         mRenderNode.setLayerPaint(mLayerPaint);
 
         // draw() behaves differently if we are on a layer, so we need to
@@ -15680,12 +15679,12 @@
      *
      * @see #setLayerType(int, android.graphics.Paint)
      */
-    public void setLayerPaint(Paint paint) {
+    public void setLayerPaint(@Nullable Paint paint) {
         int layerType = getLayerType();
         if (layerType != LAYER_TYPE_NONE) {
-            mLayerPaint = paint == null ? new Paint() : paint;
+            mLayerPaint = paint;
             if (layerType == LAYER_TYPE_HARDWARE) {
-                if (mRenderNode.setLayerPaint(mLayerPaint)) {
+                if (mRenderNode.setLayerPaint(paint)) {
                     invalidateViewProperty(false, false);
                 }
             } else {
@@ -16855,7 +16854,7 @@
                 canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint);
             } else {
                 // use layer paint to draw the bitmap, merging the two alphas, but also restore
-                int layerPaintAlpha = mLayerPaint.getAlpha();
+                int layerPaintAlpha = mLayerPaint != null ? mLayerPaint.getAlpha() : 255;
                 mLayerPaint.setAlpha((int) (alpha * layerPaintAlpha));
                 canvas.drawBitmap(cache, 0.0f, 0.0f, mLayerPaint);
                 mLayerPaint.setAlpha(layerPaintAlpha);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index ffebf71..7c06577 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3287,6 +3287,7 @@
     private final static int MSG_DISPATCH_APP_VISIBILITY = 8;
     private final static int MSG_DISPATCH_GET_NEW_SURFACE = 9;
     private final static int MSG_DISPATCH_KEY_FROM_IME = 11;
+    private final static int MSG_FINISH_INPUT_CONNECTION = 12;
     private final static int MSG_CHECK_FOCUS = 13;
     private final static int MSG_CLOSE_SYSTEM_DIALOGS = 14;
     private final static int MSG_DISPATCH_DRAG_EVENT = 15;
@@ -3326,6 +3327,8 @@
                     return "MSG_DISPATCH_GET_NEW_SURFACE";
                 case MSG_DISPATCH_KEY_FROM_IME:
                     return "MSG_DISPATCH_KEY_FROM_IME";
+                case MSG_FINISH_INPUT_CONNECTION:
+                    return "MSG_FINISH_INPUT_CONNECTION";
                 case MSG_CHECK_FOCUS:
                     return "MSG_CHECK_FOCUS";
                 case MSG_CLOSE_SYSTEM_DIALOGS:
@@ -3546,6 +3549,12 @@
                 }
                 enqueueInputEvent(event, null, QueuedInputEvent.FLAG_DELIVER_POST_IME, true);
             } break;
+            case MSG_FINISH_INPUT_CONNECTION: {
+                InputMethodManager imm = InputMethodManager.peekInstance();
+                if (imm != null) {
+                    imm.reportFinishInputConnection((InputConnection)msg.obj);
+                }
+            } break;
             case MSG_CHECK_FOCUS: {
                 InputMethodManager imm = InputMethodManager.peekInstance();
                 if (imm != null) {
@@ -5856,6 +5865,11 @@
         }
     }
 
+    public void dispatchFinishInputConnection(InputConnection connection) {
+        Message msg = mHandler.obtainMessage(MSG_FINISH_INPUT_CONNECTION, connection);
+        mHandler.sendMessage(msg);
+    }
+
     public void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
             Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
             Configuration newConfig, Rect backDropFrame, boolean forceLayout,
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 655c9b3..7f44bac 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -59,4 +59,8 @@
             boolean touchExplorationEnabled);
 
     IBinder getWindowToken(int windowId, int userId);
+
+    void enableAccessibilityService(in ComponentName service, int userId);
+
+    void disableAccessibilityService(in ComponentName service, int userId);
 }
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index a3c49c5..6a830f8 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -158,8 +158,8 @@
      *
      * @hide
      */
-    public void reportFinish() {
-        // Intentionally empty
+    protected void reportFinish() {
+        // Intentionaly empty
     }
 
     /**
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 4bb0c3c..cf5cc3e 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -317,6 +317,7 @@
     /**
      * The InputConnection that was last retrieved from the served view.
      */
+    InputConnection mServedInputConnection;
     ControlledInputConnectionWrapper mServedInputConnectionWrapper;
     /**
      * The completions that were last provided by the served view.
@@ -497,7 +498,7 @@
                             // from a thread that created mServedView. That could happen
                             // the current activity is running in the system process.
                             // In that case, we really should not call
-                            // mServedInputConnectionWrapper.finishComposingText().
+                            // mServedInputConnection.finishComposingText.
                             if (checkFocusNoStartInput(mHasBeenInactive, false)) {
                                 final int reason = active ?
                                         InputMethodClient.START_INPUT_REASON_ACTIVATED_BY_IMMS :
@@ -531,25 +532,22 @@
 
     private static class ControlledInputConnectionWrapper extends IInputConnectionWrapper {
         private final InputMethodManager mParentInputMethodManager;
+        private boolean mActive;
 
         public ControlledInputConnectionWrapper(final Looper mainLooper, final InputConnection conn,
                 final InputMethodManager inputMethodManager) {
             super(mainLooper, conn);
             mParentInputMethodManager = inputMethodManager;
+            mActive = true;
         }
 
         @Override
         public boolean isActive() {
-            return mParentInputMethodManager.mActive && !isFinished();
+            return mParentInputMethodManager.mActive && mActive;
         }
 
         void deactivate() {
-            if (isFinished()) {
-                // This is a small performance optimization.  Still only the 1st call of
-                // reportFinish() will take effect.
-                return;
-            }
-            reportFinish();
+            mActive = false;
         }
 
         @Override
@@ -564,9 +562,7 @@
 
         @Override
         public String toString() {
-            return "ControlledInputConnectionWrapper{"
-                    + "connection=" + getInputConnection()
-                    + " finished=" + isFinished()
+            return "ControlledInputConnectionWrapper{mActive=" + mActive
                     + " mParentInputMethodManager.mActive=" + mParentInputMethodManager.mActive
                     + "}";
         }
@@ -784,8 +780,7 @@
      */
     public boolean isAcceptingText() {
         checkFocus();
-        return mServedInputConnectionWrapper != null &&
-                mServedInputConnectionWrapper.getInputConnection() != null;
+        return mServedInputConnection != null;
     }
 
     /**
@@ -820,6 +815,7 @@
      */
     void clearConnectionLocked() {
         mCurrentTextBoxAttribute = null;
+        mServedInputConnection = null;
         if (mServedInputConnectionWrapper != null) {
             mServedInputConnectionWrapper.deactivate();
             mServedInputConnectionWrapper = null;
@@ -840,6 +836,7 @@
                     throw e.rethrowFromSystemServer();
                 }
             }
+            notifyInputConnectionFinished();
             mServedView = null;
             mCompletions = null;
             mServedConnecting = false;
@@ -847,6 +844,37 @@
         }
     }
 
+    /**
+     * Notifies the served view that the current InputConnection will no longer be used.
+     */
+    private void notifyInputConnectionFinished() {
+        if (mServedView != null && mServedInputConnection != null) {
+            // We need to tell the previously served view that it is no
+            // longer the input target, so it can reset its state.  Schedule
+            // this call on its window's Handler so it will be on the correct
+            // thread and outside of our lock.
+            ViewRootImpl viewRootImpl = mServedView.getViewRootImpl();
+            if (viewRootImpl != null) {
+                // This will result in a call to reportFinishInputConnection() below.
+                viewRootImpl.dispatchFinishInputConnection(mServedInputConnection);
+            }
+        }
+    }
+
+    /**
+     * Called from the FINISH_INPUT_CONNECTION message above.
+     * @hide
+     */
+    public void reportFinishInputConnection(InputConnection ic) {
+        if (mServedInputConnection != ic) {
+            ic.finishComposingText();
+            // To avoid modifying the public InputConnection interface
+            if (ic instanceof BaseInputConnection) {
+                ((BaseInputConnection) ic).reportFinish();
+            }
+        }
+    }
+
     public void displayCompletions(View view, CompletionInfo[] completions) {
         checkFocus();
         synchronized (mH) {
@@ -1212,10 +1240,9 @@
             // Hook 'em up and let 'er rip.
             mCurrentTextBoxAttribute = tba;
             mServedConnecting = false;
-            if (mServedInputConnectionWrapper != null) {
-                mServedInputConnectionWrapper.deactivate();
-                mServedInputConnectionWrapper = null;
-            }
+            // Notify the served view that its previous input connection is finished
+            notifyInputConnectionFinished();
+            mServedInputConnection = ic;
             ControlledInputConnectionWrapper servedContext;
             final int missingMethodFlags;
             if (ic != null) {
@@ -1240,6 +1267,9 @@
                 servedContext = null;
                 missingMethodFlags = 0;
             }
+            if (mServedInputConnectionWrapper != null) {
+                mServedInputConnectionWrapper.deactivate();
+            }
             mServedInputConnectionWrapper = servedContext;
 
             try {
@@ -1383,7 +1413,7 @@
             return false;
         }
 
-        final ControlledInputConnectionWrapper ic;
+        InputConnection ic = null;
         synchronized (mH) {
             if (mServedView == mNextServedView && !forceNewFocus) {
                 return false;
@@ -1403,7 +1433,7 @@
                 return false;
             }
 
-            ic = mServedInputConnectionWrapper;
+            ic = mServedInputConnection;
 
             mServedView = mNextServedView;
             mCurrentTextBoxAttribute = null;
@@ -2252,7 +2282,7 @@
         } else {
             p.println("  mCurrentTextBoxAttribute: null");
         }
-        p.println("  mServedInputConnectionWrapper=" + mServedInputConnectionWrapper);
+        p.println("  mServedInputConnection=" + mServedInputConnection);
         p.println("  mCompletions=" + Arrays.toString(mCompletions));
         p.println("  mCursorRect=" + mCursorRect);
         p.println("  mCursorSelStart=" + mCursorSelStart
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index d1b5fc8..18687c9 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -129,8 +129,8 @@
      */
     private static final int ANIMATION_STYLE_DEFAULT = -1;
 
-    private final int[] mDrawingLocation = new int[2];
-    private final int[] mScreenLocation = new int[2];
+    private final int[] mTmpDrawingLocation = new int[2];
+    private final int[] mTmpScreenLocation = new int[2];
     private final Rect mTempRect = new Rect();
 
     private Context mContext;
@@ -222,7 +222,7 @@
                         mDecorView.getLayoutParams();
 
                 updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
-                        mAnchoredGravity));
+                        p.width, p.height, mAnchoredGravity));
                 update(p.x, p.y, -1, -1, true);
             }
         }
@@ -1123,7 +1123,7 @@
 
         TransitionManager.endTransitions(mDecorView);
 
-        unregisterForViewTreeChanges();
+        detachFromAnchor();
 
         mIsShowing = true;
         mIsDropdown = false;
@@ -1206,7 +1206,7 @@
 
         TransitionManager.endTransitions(mDecorView);
 
-        registerForViewTreeChanges(anchor, xoff, yoff, gravity);
+        attachToAnchor(anchor, xoff, yoff, gravity);
 
         mIsShowing = true;
         mIsDropdown = true;
@@ -1214,7 +1214,8 @@
         final WindowManager.LayoutParams p = createPopupLayoutParams(anchor.getWindowToken());
         preparePopup(p);
 
-        final boolean aboveAnchor = findDropDownPosition(anchor, p, xoff, yoff, gravity);
+        final boolean aboveAnchor = findDropDownPosition(anchor, p, xoff, yoff,
+                p.width, p.height, gravity);
         updateAboveAnchor(aboveAnchor);
 
         invokePopup(p);
@@ -1494,120 +1495,138 @@
      * to reclaim space. If scrolling is not possible or not enough, the popup
      * window gets moved on top of the anchor.
      * <p>
-     * The height must have been set on the layout parameters prior to calling
-     * this method.
+     * The results of positioning are placed in {@code outParams}.
      *
      * @param anchor the view on which the popup window must be anchored
-     * @param p the layout parameters used to display the drop down
-     * @param xoff horizontal offset used to adjust for background padding
-     * @param yoff vertical offset used to adjust for background padding
+     * @param outParams the layout parameters used to display the drop down
+     * @param xOffset absolute horizontal offset from the top of the anchor
+     * @param yOffset absolute vertical offset from the top of the anchor
      * @param gravity horizontal gravity specifying popup alignment
      * @return true if the popup is translated upwards to fit on screen
      */
-    private boolean findDropDownPosition(View anchor, WindowManager.LayoutParams p, int xoff,
-            int yoff, int gravity) {
+    private boolean findDropDownPosition(View anchor, WindowManager.LayoutParams outParams,
+            int xOffset, int yOffset, int width, int height, int gravity) {
         final int anchorHeight = anchor.getHeight();
         final int anchorWidth = anchor.getWidth();
         if (mOverlapAnchor) {
-            yoff -= anchorHeight;
+            yOffset -= anchorHeight;
         }
 
-        anchor.getLocationInWindow(mDrawingLocation);
-        p.x = mDrawingLocation[0] + xoff;
-        p.y = mDrawingLocation[1] + anchorHeight + yoff;
+        // Initially, align to the bottom-left corner of the anchor plus offsets.
+        final int[] drawingLocation = mTmpDrawingLocation;
+        anchor.getLocationInWindow(drawingLocation);
+        outParams.x = drawingLocation[0] + xOffset;
+        outParams.y = drawingLocation[1] + anchorHeight + yOffset;
 
+        // If we need to adjust for gravity RIGHT, align to the bottom-right
+        // corner of the anchor (still accounting for offsets).
         final int hgrav = Gravity.getAbsoluteGravity(gravity, anchor.getLayoutDirection())
                 & Gravity.HORIZONTAL_GRAVITY_MASK;
         if (hgrav == Gravity.RIGHT) {
-            // Flip the location to align the right sides of the popup and
-            // anchor instead of left.
-            p.x -= mPopupWidth - anchorWidth;
+            outParams.x -= width - anchorWidth;
         }
 
-        boolean onTop = false;
+        // Let the window manager know to align the top to y.
+        outParams.gravity = Gravity.LEFT | Gravity.TOP;
 
-        p.gravity = Gravity.LEFT | Gravity.TOP;
+        final int[] screenLocation = mTmpScreenLocation;
+        anchor.getLocationOnScreen(screenLocation);
 
-        anchor.getLocationOnScreen(mScreenLocation);
         final Rect displayFrame = new Rect();
         anchor.getWindowVisibleDisplayFrame(displayFrame);
 
-        final int screenY = mScreenLocation[1] + anchorHeight + yoff;
+        boolean onTop = false;
+
         final View root = anchor.getRootView();
-        if (screenY + mPopupHeight > displayFrame.bottom
-                || p.x + mPopupWidth - root.getWidth() > 0) {
-            // If the drop down disappears at the bottom of the screen, we try
-            // to scroll a parent scrollview or move the drop down back up on
-            // top of the edit box.
+        final int screenY = screenLocation[1] + anchorHeight + yOffset;
+        final boolean tooFarDown = screenY + height > displayFrame.bottom;
+        final boolean tooFarRight = outParams.x + width > root.getWidth();
+        if (tooFarDown || tooFarRight) {
+            // If the popup extends beyond the visible area, try to scroll the
+            // parent so that it is fully visible.
             if (mAllowScrollingAnchorParent) {
                 final int scrollX = anchor.getScrollX();
                 final int scrollY = anchor.getScrollY();
-                final Rect r = new Rect(scrollX, scrollY, scrollX + mPopupWidth + xoff,
-                        scrollY + mPopupHeight + anchorHeight + yoff);
+                final Rect r = new Rect(scrollX, scrollY, scrollX + width + xOffset,
+                        scrollY + height + anchorHeight + yOffset);
                 anchor.requestRectangleOnScreen(r, true);
             }
 
-            // Now we re-evaluate the space available, and decide from that
-            // whether the pop-up will go above or below the anchor.
-            anchor.getLocationInWindow(mDrawingLocation);
-            p.x = mDrawingLocation[0] + xoff;
-            p.y = mDrawingLocation[1] + anchorHeight + yoff;
+            // Update for the new anchor position.
+            anchor.getLocationInWindow(drawingLocation);
+            outParams.x = drawingLocation[0] + xOffset;
+            outParams.y = drawingLocation[1] + anchorHeight + yOffset;
 
             // Preserve the gravity adjustment.
             if (hgrav == Gravity.RIGHT) {
-                p.x -= mPopupWidth - anchorWidth;
+                outParams.x -= width - anchorWidth;
             }
 
-            // Determine whether there is more space above or below the anchor.
-            anchor.getLocationOnScreen(mScreenLocation);
-            onTop = (displayFrame.bottom - mScreenLocation[1] - anchorHeight - yoff) <
-                    (mScreenLocation[1] - yoff - displayFrame.top);
-            if (!mOverlapAnchor) {
+            final int newScreenY = screenLocation[1] + anchorHeight + yOffset;
+            final boolean stillTooFarDown = newScreenY + height > displayFrame.bottom;
+            if (stillTooFarDown) {
+                // If the popup is still too far down, re-evaluate the space
+                // available and decide whether the pop-up will go above or
+                // below the anchor.
+                anchor.getLocationOnScreen(screenLocation);
+
+                final int below = displayFrame.bottom - screenLocation[1] - anchorHeight - yOffset;
+                final int above = screenLocation[1] - displayFrame.top + yOffset;
+                onTop = above > below;
+
                 if (onTop) {
-                    p.gravity = Gravity.LEFT | Gravity.BOTTOM;
-                    p.y = root.getHeight() - mDrawingLocation[1] + yoff;
-                } else {
-                    p.y = mDrawingLocation[1] + anchorHeight + yoff;
+                    // Move everything up.
+                    if (mOverlapAnchor) {
+                        yOffset += anchorHeight;
+                    }
+                    outParams.y = drawingLocation[1] - height + yOffset;
                 }
             }
         }
 
         if (mClipToScreen) {
-            final int winOffsetX = mScreenLocation[0] - mDrawingLocation[0];
-            final int winOffsetY = mScreenLocation[1] - mDrawingLocation[1];
-            p.x += winOffsetX;
-            p.y += winOffsetY;
-            final int displayFrameWidth = displayFrame.right - displayFrame.left;
-            final int right = p.x + p.width;
+            // Use screen coordinates for comparison against display frame.
+            final int winOffsetX = screenLocation[0] - drawingLocation[0];
+            final int winOffsetY = screenLocation[1] - drawingLocation[1];
+            outParams.x += winOffsetX;
+            outParams.y += winOffsetY;
+
+            final int right = outParams.x + width;
             if (right > displayFrame.right) {
-                p.x -= right - displayFrame.right;
+                // The popup is too far right, move it back in.
+                outParams.x -= right - displayFrame.right;
             }
 
-            if (p.x < displayFrame.left) {
-                p.x = displayFrame.left;
-                p.width = Math.min(p.width, displayFrameWidth);
+            if (outParams.x < displayFrame.left) {
+                // The popup is too far left, move it back in and clip if it's
+                // still too large.
+                outParams.x = displayFrame.left;
+
+                final int displayFrameWidth = displayFrame.width();
+                width = Math.min(width, displayFrameWidth);
             }
 
-            if (mOverlapAnchor) {
-                final int bottom = p.y + p.height;
-                if (bottom > displayFrame.bottom) {
-                    p.y -= bottom - displayFrame.bottom;
-                }
-            } else {
-                if (onTop) {
-                    final int popupTop = mScreenLocation[1] + yoff - mPopupHeight;
-                    if (popupTop < 0) {
-                        p.y += popupTop;
-                    }
-                } else {
-                    p.y = Math.max(p.y, displayFrame.top);
-                }
+            final int bottom = outParams.y + height;
+            if (bottom > displayFrame.bottom) {
+                // The popup is too far down, move it back in.
+                outParams.y -= bottom - displayFrame.bottom;
             }
-            p.x -= winOffsetX;
-            p.y -= winOffsetY;
+
+            if (outParams.y < displayFrame.top) {
+                // The popup is too far up, move it back in and clip if
+                // it's still too large.
+                outParams.y = displayFrame.top;
+
+                final int displayFrameHeight = displayFrame.height();
+                height = Math.min(height, displayFrameHeight);
+            }
+
+            outParams.x -= winOffsetX;
+            outParams.y -= winOffsetY;
         }
 
-        p.gravity |= Gravity.DISPLAY_CLIP_VERTICAL;
+        outParams.width = width;
+        outParams.height = height;
 
         return onTop;
     }
@@ -1665,7 +1684,7 @@
             anchor.getWindowVisibleDisplayFrame(displayFrame);
         }
 
-        final int[] anchorPos = mDrawingLocation;
+        final int[] anchorPos = mTmpDrawingLocation;
         anchor.getLocationOnScreen(anchorPos);
 
         final int bottomEdge = displayFrame.bottom;
@@ -1754,7 +1773,7 @@
         }
 
         // Clears the anchor view.
-        unregisterForViewTreeChanges();
+        detachFromAnchor();
 
         if (mOnDismissListener != null) {
             mOnDismissListener.onDismiss();
@@ -2018,43 +2037,40 @@
         }
 
         final WeakReference<View> oldAnchor = mAnchor;
+        final int gravity = mAnchoredGravity;
+
         final boolean needsUpdate = updateLocation && (mAnchorXoff != xoff || mAnchorYoff != yoff);
         if (oldAnchor == null || oldAnchor.get() != anchor || (needsUpdate && !mIsDropdown)) {
-            registerForViewTreeChanges(anchor, xoff, yoff, mAnchoredGravity);
+            attachToAnchor(anchor, xoff, yoff, gravity);
         } else if (needsUpdate) {
             // No need to register again if this is a DropDown, showAsDropDown already did.
             mAnchorXoff = xoff;
             mAnchorYoff = yoff;
         }
 
-        final WindowManager.LayoutParams p =
-                (WindowManager.LayoutParams) mDecorView.getLayoutParams();
+        final LayoutParams p = (LayoutParams) mDecorView.getLayoutParams();
+        final int oldGravity = p.gravity;
+        final int oldWidth = p.width;
+        final int oldHeight = p.height;
+        final int oldX = p.x;
+        final int oldY = p.y;
 
         if (updateDimension) {
             if (width == -1) {
                 width = mPopupWidth;
-            } else {
-                mPopupWidth = width;
-                p.width = width;
             }
             if (height == -1) {
                 height = mPopupHeight;
-            } else {
-                mPopupHeight = height;
-                p.height  = height;
             }
         }
 
-        final int x = p.x;
-        final int y = p.y;
-        if (updateLocation) {
-            updateAboveAnchor(findDropDownPosition(anchor, p, xoff, yoff, mAnchoredGravity));
-        } else {
-            updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
-                    mAnchoredGravity));
-        }
+        final boolean aboveAnchor = findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
+                width, height, gravity);
+        updateAboveAnchor(aboveAnchor);
 
-        update(p.x, p.y, width, height, x != p.x || y != p.y);
+        final boolean paramsChanged = oldGravity != p.gravity || oldX != p.x || oldY != p.y
+                || oldWidth != p.width || oldHeight != p.height;
+        update(p.x, p.y, p.width, p.height, paramsChanged);
     }
 
     /**
@@ -2067,7 +2083,7 @@
         public void onDismiss();
     }
 
-    private void unregisterForViewTreeChanges() {
+    private void detachFromAnchor() {
         final View anchor = mAnchor != null ? mAnchor.get() : null;
         if (anchor != null) {
             final ViewTreeObserver vto = anchor.getViewTreeObserver();
@@ -2084,8 +2100,8 @@
         mIsAnchorRootAttached = false;
     }
 
-    private void registerForViewTreeChanges(View anchor, int xoff, int yoff, int gravity) {
-        unregisterForViewTreeChanges();
+    private void attachToAnchor(View anchor, int xoff, int yoff, int gravity) {
+        detachFromAnchor();
 
         final ViewTreeObserver vto = anchor.getViewTreeObserver();
         if (vto != null) {
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
index c0dce22..7bd64e5 100644
--- a/core/java/com/android/internal/content/PackageMonitor.java
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -26,6 +26,7 @@
 import android.os.UserHandle;
 import android.util.Slog;
 import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.Preconditions;
 
 import java.util.HashSet;
 
@@ -72,15 +73,17 @@
 
     public void register(Context context, Looper thread, UserHandle user,
             boolean externalStorage) {
+        register(context, user, externalStorage,
+                (thread == null) ? BackgroundThread.getHandler() : new Handler(thread));
+    }
+
+    public void register(Context context, UserHandle user,
+        boolean externalStorage, Handler handler) {
         if (mRegisteredContext != null) {
             throw new IllegalStateException("Already registered");
         }
         mRegisteredContext = context;
-        if (thread == null) {
-            mRegisteredHandler = BackgroundThread.getHandler();
-        } else {
-            mRegisteredHandler = new Handler(thread);
-        }
+        mRegisteredHandler = Preconditions.checkNotNull(handler);
         if (user != null) {
             context.registerReceiverAsUser(this, user, sPackageFilt, null, mRegisteredHandler);
             context.registerReceiverAsUser(this, user, sNonDataFilt, null, mRegisteredHandler);
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
index 53cb56e..c46851e 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -35,6 +35,20 @@
     }
 
     /**
+     * Ensures that an expression checking an argument is true.
+     *
+     * @param expression the expression to check
+     * @param errorMessage the exception message to use if the check fails; will
+     *     be converted to a string using {@link String#valueOf(Object)}
+     * @throws IllegalArgumentException if {@code expression} is false
+     */
+    public static void checkArgument(boolean expression, final Object errorMessage) {
+        if (!expression) {
+            throw new IllegalArgumentException(String.valueOf(errorMessage));
+        }
+    }
+
+    /**
      * Ensures that an string reference passed as a parameter to the calling
      * method is not empty.
      *
diff --git a/core/java/com/android/internal/util/ScreenShapeHelper.java b/core/java/com/android/internal/util/ScreenShapeHelper.java
index 4a196f8..5f390be 100644
--- a/core/java/com/android/internal/util/ScreenShapeHelper.java
+++ b/core/java/com/android/internal/util/ScreenShapeHelper.java
@@ -1,27 +1,20 @@
 package com.android.internal.util;
 
 import android.content.res.Resources;
-import android.content.res.TypedArray;
 import android.os.Build;
 import android.os.SystemProperties;
-import android.util.DisplayMetrics;
-import android.util.TypedValue;
 import android.view.ViewRootImpl;
 
-import com.android.internal.R;
-
 /**
  * @hide
  */
 public class ScreenShapeHelper {
-    private static final boolean IS_EMULATOR = Build.HARDWARE.contains("goldfish");
-
     /**
      * Return the bottom pixel window outset of a window given its style attributes.
      * @return An outset dimension in pixels or 0 if no outset should be applied.
      */
     public static int getWindowOutsetBottomPx(Resources resources) {
-        if (IS_EMULATOR) {
+        if (Build.IS_EMULATOR) {
             return SystemProperties.getInt(ViewRootImpl.PROPERTY_EMULATOR_WIN_OUTSET_BOTTOM_PX, 0);
         } else {
             return resources.getInteger(com.android.internal.R.integer.config_windowOutsetBottom);
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index 02b9db3..3be15f3 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -16,10 +16,6 @@
 
 package com.android.internal.view;
 
-import com.android.internal.annotations.GuardedBy;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -27,7 +23,6 @@
 import android.os.RemoteException;
 import android.util.Log;
 import android.view.KeyEvent;
-import android.view.inputmethod.BaseInputConnection;
 import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.CorrectionInfo;
 import android.view.inputmethod.ExtractedTextRequest;
@@ -61,16 +56,11 @@
     private static final int DO_PERFORM_PRIVATE_COMMAND = 120;
     private static final int DO_CLEAR_META_KEY_STATES = 130;
     private static final int DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO = 140;
-    private static final int DO_REPORT_FINISH = 150;
 
-    @NonNull
-    private final WeakReference<InputConnection> mInputConnection;
+    private WeakReference<InputConnection> mInputConnection;
 
     private Looper mMainLooper;
     private Handler mH;
-    private Object mLock = new Object();
-    @GuardedBy("mLock")
-    private boolean mFinished = false;
 
     static class SomeArgs {
         Object arg1;
@@ -96,17 +86,6 @@
         mH = new MyHandler(mMainLooper);
     }
 
-    @Nullable
-    public InputConnection getInputConnection() {
-        return mInputConnection.get();
-    }
-
-    protected boolean isFinished() {
-        synchronized (mLock) {
-            return mFinished;
-        }
-    }
-
     abstract protected boolean isActive();
 
     /**
@@ -219,10 +198,6 @@
                 seq, callback));
     }
 
-    public void reportFinish() {
-        dispatchMessage(obtainMessage(DO_REPORT_FINISH));
-    }
-
     void dispatchMessage(Message msg) {
         // If we are calling this from the main thread, then we can call
         // right through.  Otherwise, we need to send the message to the
@@ -235,7 +210,7 @@
         
         mH.sendMessage(msg);
     }
-
+    
     void executeMessage(Message msg) {
         switch (msg.what) {
             case DO_GET_TEXT_AFTER_CURSOR: {
@@ -498,36 +473,6 @@
                 }
                 return;
             }
-            case DO_REPORT_FINISH: {
-                // Note that we do not need to worry about race condition here, because 1) mFinished
-                // is updated only inside this block, and 2) the code here is running on a Handler
-                // hence we assume multiple DO_REPORT_FINISH messages will not be handled at the
-                // same time.
-                if (isFinished()) {
-                    return;
-                }
-                try {
-                    InputConnection ic = mInputConnection.get();
-                    // Note we do NOT check isActive() here, because this is safe
-                    // for an IME to call at any time, and we need to allow it
-                    // through to clean up our state after the IME has switched to
-                    // another client.
-                    if (ic == null) {
-                        return;
-                    }
-                    ic.finishComposingText();
-                    // TODO: Make reportFinish() public method of InputConnection to remove this
-                    // check.
-                    if (ic instanceof BaseInputConnection) {
-                        ((BaseInputConnection) ic).reportFinish();
-                    }
-                } finally {
-                    synchronized (mLock) {
-                        mFinished = true;
-                    }
-                }
-                return;
-            }
         }
         Log.w(TAG, "Unhandled message code: " + msg.what);
     }
diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java
index a96b5a0..f211ff2 100644
--- a/core/java/com/android/internal/widget/EditableInputConnection.java
+++ b/core/java/com/android/internal/widget/EditableInputConnection.java
@@ -83,11 +83,8 @@
         return false;
     }
 
-    /**
-     * @hide
-     */
     @Override
-    public void reportFinish() {
+    protected void reportFinish() {
         super.reportFinish();
 
         synchronized(this) {
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 1ead618..528541d 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -723,6 +723,7 @@
         // Make sure that the recycled bitmap has the correct alpha type.
         mRecycledBitmap->setAlphaType(bitmap->alphaType());
 
+        bitmap->notifyPixelsChanged();
         bitmap->lockPixels();
         mNeedsCopy = false;
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 60f05448..8abb7e2 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -706,7 +706,9 @@
         android:description="@string/permgroupdesc_phone"
         android:priority="500" />
 
-    <!-- Allows read only access to phone state.
+    <!-- Allows read only access to phone state, including the phone number of the device,
+         current cellular network information, the status of any ongoing calls, and a list of any
+         {@link android.telecom.PhoneAccount}s registered on the device.
          <p class="note"><strong>Note:</strong> If <em>both</em> your <a
          href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code
          minSdkVersion}</a> and <a
diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml
index 58d3d91..c0716e9 100644
--- a/core/res/res/values-television/config.xml
+++ b/core/res/res/values-television/config.xml
@@ -26,12 +26,4 @@
 
     <!-- Default bounds [left top right bottom] on screen for picture-in-picture windows. -->
     <string translatable="false" name="config_defaultPictureInPictureBounds">"1328 54 1808 324"</string>
-
-    <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows, when the PIP
-         is located in center. -->
-    <string translatable="false" name="config_centeredPictureInPictureBounds">"596 280 1324 690"</string>
-
-    <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
-         when the PIP is shown with Recents. -->
-    <string translatable="false" name="config_pictureInPictureBoundsInRecents">"1484 96 1804 276"</string>
 </resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 01b2c47..6ecaa1f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2462,14 +2462,6 @@
     <!-- Default bounds [left top right bottom] on screen for picture-in-picture windows. -->
     <string translatable="false" name="config_defaultPictureInPictureBounds">"0 0 100 100"</string>
 
-    <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows, when the PIP
-         is located in center. -->
-    <string translatable="false" name="config_centeredPictureInPictureBounds">"0 0 300 300"</string>
-
-    <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
-         when the PIP is shown with Recents. -->
-    <string translatable="false" name="config_pictureInPictureBoundsInRecents">"0 0 100 100"</string>
-
     <!-- Controls the snap mode for the docked stack divider
              0 - 3 snap targets: left/top has 16:9 ratio, 1:1, and right/bottom has 16:9 ratio
              1 - 3 snap targets: fixed ratio, 1:1, (1 - fixed ratio)
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 081d613..9b7b1d4 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -22,6 +22,8 @@
     <dimen name="thumbnail_width">192dp</dimen>
     <!-- The height that is used when creating thumbnails of applications. -->
     <dimen name="thumbnail_height">192dp</dimen>
+    <!-- The amount to scale a fullscreen screenshot thumbnail. -->
+    <item name="thumbnail_fullscreen_scale" type="fraction">60%</item>
     <!-- The standard size (both width and height) of an application icon that
          will be displayed in the app launcher and elsewhere. -->
     <dimen name="app_icon_size">48dip</dimen>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index ce7d80b..7426ddf 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -308,8 +308,6 @@
   <java-symbol type="bool" name="config_guestUserEphemeral" />
   <java-symbol type="bool" name="config_localDisplaysMirrorContent" />
   <java-symbol type="string" name="config_defaultPictureInPictureBounds" />
-  <java-symbol type="string" name="config_centeredPictureInPictureBounds" />
-  <java-symbol type="string" name="config_pictureInPictureBoundsInRecents" />
   <java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_threshold" />
   <java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_factor" />
   <java-symbol type="integer" name="config_wifi_framework_5GHz_preference_penalty_threshold" />
@@ -1513,6 +1511,7 @@
   <java-symbol type="dimen" name="docked_stack_minimize_thickness" />
   <java-symbol type="integer" name="config_dockedStackDividerSnapMode" />
   <java-symbol type="fraction" name="docked_stack_divider_fixed_ratio" />
+  <java-symbol type="fraction" name="thumbnail_fullscreen_scale" />
   <java-symbol type="dimen" name="navigation_bar_height" />
   <java-symbol type="dimen" name="navigation_bar_height_landscape" />
   <java-symbol type="dimen" name="navigation_bar_width" />
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index 51221b4..2b950d3 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -243,13 +243,15 @@
     }
 
     /**
-     * Invokes {@link #loadDrawable(Context)} on a background thread
-     * and then runs <code>andThen</code> on the UI thread when finished.
+     * Invokes {@link #loadDrawable(Context)} on a background thread and notifies the <code>
+     * {@link OnDrawableLoadedListener#onDrawableLoaded listener} </code> on the {@code handler}
+     * when finished.
      *
      * @param context {@link Context Context} in which to load the drawable; see
      *                {@link #loadDrawable(Context)}
-     * @param listener a callback to run on the provided
-     * @param handler {@link Handler} on which to run <code>andThen</code>.
+     * @param listener to be {@link OnDrawableLoadedListener#onDrawableLoaded notified} when
+     *                 {@link #loadDrawable(Context)} finished
+     * @param handler {@link Handler} on which to notify the {@code listener}
      */
     public void loadDrawableAsync(Context context, final OnDrawableLoadedListener listener,
             Handler handler) {
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index c305f65..2246cf9c 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -21,6 +21,8 @@
 #include <cstdio>
 #include <errno.h>
 #include <inttypes.h>
+#include <limits>
+#include <cmath>
 #include <sys/mman.h>
 
 namespace android {
@@ -202,6 +204,40 @@
 
 }
 
+static bool shouldReplace(SlowFrame& existing, SlowFrame& candidate) {
+    if (candidate.whenHours - existing.whenHours >= 24) {
+        // If the old slowframe is over 24 hours older than the candidate,
+        // replace it. It's too stale
+        return true;
+    }
+    if (candidate.frametimeMs > existing.frametimeMs) {
+        return true;
+    }
+    return false;
+}
+
+void JankTracker::updateSlowest(const FrameInfo& frame) {
+    uint16_t durationMs = static_cast<uint16_t>(std::min(
+            ns2ms(frame[FrameInfoIndex::FrameCompleted] - frame[FrameInfoIndex::IntendedVsync]),
+            static_cast<nsecs_t>(std::numeric_limits<uint16_t>::max())));
+    uint16_t startHours = static_cast<uint16_t>(std::lround(
+            ns2s(frame[FrameInfoIndex::IntendedVsync]) / 3600.0f));
+    SlowFrame* toReplace = nullptr;
+    SlowFrame thisFrame{startHours, durationMs};
+    // First find the best candidate for replacement
+    for (SlowFrame& existing : mData->slowestFrames) {
+        // If we should replace the current data with the replacement candidate,
+        // it means the current data is worse than the replacement candidate
+        if (!toReplace || shouldReplace(existing, *toReplace)) {
+            toReplace = &existing;
+        }
+    }
+    // Now see if we should replace it
+    if (shouldReplace(*toReplace, thisFrame)) {
+        *toReplace = thisFrame;
+    }
+}
+
 void JankTracker::addFrame(const FrameInfo& frame) {
     mData->totalFrameCount++;
     // Fast-path for jank-free frames
@@ -215,6 +251,11 @@
         return;
     }
 
+    // For slowest frames we are still interested in frames that are otherwise
+    // exempt (such as first-draw). Although those frames don't directly impact
+    // smoothness, they do impact responsiveness.
+    updateSlowest(frame);
+
     if (frame[FrameInfoIndex::Flags] & EXEMPT_FRAMES_FLAGS) {
         return;
     }
@@ -247,6 +288,11 @@
     dprintf(fd, "\n90th percentile: %ums", findPercentile(data, 90));
     dprintf(fd, "\n95th percentile: %ums", findPercentile(data, 95));
     dprintf(fd, "\n99th percentile: %ums", findPercentile(data, 99));
+    dprintf(fd, "\nSlowest frames over last 24h: ");
+    for (auto& slowFrame : data->slowestFrames) {
+        if (!slowFrame.frametimeMs) continue;
+        dprintf(fd, "%ums ", slowFrame.frametimeMs);
+    }
     for (int i = 0; i < NUM_BUCKETS; i++) {
         dprintf(fd, "\nNumber %s: %u", JANK_TYPE_NAMES[i], data->jankTypeCounts[i]);
     }
diff --git a/libs/hwui/JankTracker.h b/libs/hwui/JankTracker.h
index 3887e5e..1a4a489 100644
--- a/libs/hwui/JankTracker.h
+++ b/libs/hwui/JankTracker.h
@@ -39,6 +39,11 @@
     NUM_BUCKETS,
 };
 
+struct SlowFrame {
+    uint16_t whenHours; // When this occurred in CLOCK_MONOTONIC in hours
+    uint16_t frametimeMs; // How long the frame took in ms
+};
+
 // Try to keep as small as possible, should match ASHMEM_SIZE in
 // GraphicsStatsService.java
 struct ProfileData {
@@ -49,6 +54,8 @@
     uint32_t totalFrameCount;
     uint32_t jankFrameCount;
     nsecs_t statStartTime;
+
+    std::array<SlowFrame, 10> slowestFrames;
 };
 
 // TODO: Replace DrawProfiler with this
@@ -71,6 +78,7 @@
 private:
     void freeData();
     void setFrameInterval(nsecs_t frameIntervalNanos);
+    void updateSlowest(const FrameInfo& frame);
 
     static uint32_t findPercentile(const ProfileData* data, int p);
     static void dumpData(const ProfileData* data, int fd);
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 1eb4fa0..acb88e2 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -191,13 +191,16 @@
             const SkPaint* paint) override;
 
     // Text
-    virtual void drawGlyphs(const uint16_t* glyphs, const float* positions, int glyphCount,
-            const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop,
-            float boundsRight, float boundsBottom, float totalAdvance) override;
-    virtual void drawGlyphsOnPath(const uint16_t* glyphs, int glyphCount, const SkPath& path,
-            float hOffset, float vOffset, const SkPaint& paint) override;
     virtual bool drawTextAbsolutePos() const override { return false; }
 
+protected:
+    virtual void drawGlyphs(const uint16_t* text, const float* positions, int count,
+            const SkPaint& paint, float x, float y,
+            float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
+            float totalAdvance) override;
+    virtual void drawGlyphsOnPath(const uint16_t* glyphs, int count, const SkPath& path,
+            float hOffset, float vOffset, const SkPaint& paint) override;
+
 private:
     const ClipBase* getRecordedClip() {
         return mState.writableSnapshot()->mutateClipArea().serializeClip(alloc());
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index b1ecb71..5d24fa0 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -147,13 +147,6 @@
             float dstLeft, float dstTop, float dstRight, float dstBottom,
             const SkPaint* paint) override;
 
-    virtual void drawGlyphs(const uint16_t* text, const float* positions, int count,
-            const SkPaint& paint, float x, float y,
-            float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
-            float totalAdvance) override;
-    virtual void drawGlyphsOnPath(const uint16_t* glyphs, int count, const SkPath& path,
-            float hOffset, float vOffset, const SkPaint& paint) override;
-
     virtual bool drawTextAbsolutePos() const  override { return true; }
     virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override;
 
@@ -169,6 +162,14 @@
     virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override;
     virtual void callDrawGLFunction(Functor* functor) override;
 
+protected:
+    virtual void drawGlyphs(const uint16_t* text, const float* positions, int count,
+            const SkPaint& paint, float x, float y,
+            float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
+            float totalAdvance) override;
+    virtual void drawGlyphsOnPath(const uint16_t* glyphs, int count, const SkPath& path,
+            float hOffset, float vOffset, const SkPaint& paint) override;
+
 private:
     struct SaveRec {
         int              saveCount;
@@ -761,14 +762,8 @@
         const SkPaint& paint, float x, float y,
         float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
         float totalAdvance) {
-    // Set align to left for drawing, as we don't want individual
-    // glyphs centered or right-aligned; the offset above takes
-    // care of all alignment.
-    SkPaint paintCopy(paint);
-    paintCopy.setTextAlign(SkPaint::kLeft_Align);
-
     static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats");
-    mCanvas->drawPosText(text, count << 1, reinterpret_cast<const SkPoint*>(positions), paintCopy);
+    mCanvas->drawPosText(text, count << 1, reinterpret_cast<const SkPoint*>(positions), paint);
     drawTextDecorations(x, y, totalAdvance, paint);
 }
 
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index 8c3eea3..7bfa15a 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -166,6 +166,11 @@
         bounds.offset(x, y);
     }
 
+    // Set align to left for drawing, as we don't want individual
+    // glyphs centered or right-aligned; the offset above takes
+    // care of all alignment.
+    paint.setTextAlign(Paint::kLeft_Align);
+
     DrawTextFunctor f(layout, this, glyphs.get(), pos.get(),
             paint, x, y, bounds, layout.getAdvance());
     MinikinUtils::forFontRun(layout, &paint, f);
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index dc669f0..5dbda43 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -53,6 +53,7 @@
 } // namespace SaveFlags
 
 namespace uirenderer {
+class SkiaCanvasProxy;
 namespace VectorDrawable {
 class Tree;
 };
@@ -205,19 +206,6 @@
             float dstLeft, float dstTop, float dstRight, float dstBottom,
             const SkPaint* paint) = 0;
 
-    // Text
-    /**
-     * drawText: count is of glyphs
-     * totalAdvance: used to define width of text decorations (underlines, strikethroughs).
-     */
-    virtual void drawGlyphs(const uint16_t* glyphs, const float* positions, int count,
-            const SkPaint& paint, float x, float y,
-            float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
-            float totalAdvance) = 0;
-    /** drawTextOnPath: count is of glyphs */
-    virtual void drawGlyphsOnPath(const uint16_t* glyphs, int count, const SkPath& path,
-            float hOffset, float vOffset, const SkPaint& paint) = 0;
-
     /**
      * Specifies if the positions passed to ::drawText are absolute or relative
      * to the (x,y) value provided.
@@ -244,6 +232,22 @@
 
 protected:
     void drawTextDecorations(float x, float y, float length, const SkPaint& paint);
+
+    /**
+     * drawText: count is of glyphs
+     * totalAdvance: used to define width of text decorations (underlines, strikethroughs).
+     */
+    virtual void drawGlyphs(const uint16_t* glyphs, const float* positions, int count,
+            const SkPaint& paint, float x, float y,
+            float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
+            float totalAdvance) = 0;
+    /** drawTextOnPath: count is of glyphs */
+    virtual void drawGlyphsOnPath(const uint16_t* glyphs, int count, const SkPath& path,
+            float hOffset, float vOffset, const SkPaint& paint) = 0;
+
+    friend class DrawTextFunctor;
+    friend class DrawTextOnPathFunctor;
+    friend class uirenderer::SkiaCanvasProxy;
 };
 
 }; // namespace android
diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h
index 69c321c..9599c30 100644
--- a/libs/hwui/hwui/Paint.h
+++ b/libs/hwui/hwui/Paint.h
@@ -30,6 +30,7 @@
 public:
     Paint();
     Paint(const Paint& paint);
+    Paint(const SkPaint& paint);
     ~Paint();
 
     Paint& operator=(const Paint& other);
diff --git a/libs/hwui/hwui/PaintImpl.cpp b/libs/hwui/hwui/PaintImpl.cpp
index 1172a0e..b27672c 100644
--- a/libs/hwui/hwui/PaintImpl.cpp
+++ b/libs/hwui/hwui/PaintImpl.cpp
@@ -29,6 +29,11 @@
         mHyphenEdit(paint.mHyphenEdit) {
 }
 
+Paint::Paint(const SkPaint& paint) : SkPaint(paint),
+        mLetterSpacing(0), mFontFeatureSettings(), mMinikinLangListId(0),
+        mFontVariant(VARIANT_DEFAULT) {
+}
+
 Paint::~Paint() {
 }
 
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index a4aee61..e1c9319 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -16,6 +16,7 @@
 
 #include "TestUtils.h"
 
+#include "hwui/Paint.h"
 #include "DeferredLayerUpdater.h"
 #include "LayerRenderer.h"
 
@@ -87,50 +88,17 @@
     *outTotalAdvance = totalAdvance;
 }
 
-void TestUtils::drawUtf8ToCanvas(TestCanvas* canvas, const char* text,
+
+void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text,
         const SkPaint& paint, float x, float y) {
-    // drawing text requires GlyphID TextEncoding (which JNI layer would have done)
-    LOG_ALWAYS_FATAL_IF(paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding,
-            "must use glyph encoding");
-    SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry);
-    SkAutoGlyphCacheNoGamma autoCache(paint, &surfaceProps, &SkMatrix::I());
-
-    std::vector<glyph_t> glyphs;
-    std::vector<float> positions;
-    float totalAdvance;
-    Rect bounds;
-    layoutTextUnscaled(paint, text, &glyphs, &positions, &totalAdvance, &bounds);
-
-    // apply alignment via x parameter (which JNI layer would have done)
-    if (paint.getTextAlign() == SkPaint::kCenter_Align) {
-        x -= totalAdvance / 2;
-    } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
-        x -= totalAdvance;
-    }
-
-    bounds.translate(x, y);
-
-    // Force left alignment, since alignment offset is already baked in
-    SkPaint alignPaintCopy(paint);
-    alignPaintCopy.setTextAlign(SkPaint::kLeft_Align);
-    canvas->drawGlyphs(glyphs.data(), positions.data(), glyphs.size(), alignPaintCopy, x, y,
-                bounds.left, bounds.top, bounds.right, bounds.bottom, totalAdvance);
+    auto utf16 = asciiToUtf16(text);
+    canvas->drawText(utf16.get(), 0, strlen(text), strlen(text), x, y, 0, paint, nullptr);
 }
 
-void TestUtils::drawUtf8ToCanvas(TestCanvas* canvas, const char* text,
+void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text,
         const SkPaint& paint, const SkPath& path) {
-    // drawing text requires GlyphID TextEncoding (which JNI layer would have done)
-    LOG_ALWAYS_FATAL_IF(paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding,
-            "must use glyph encoding");
-    SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry);
-    SkAutoGlyphCacheNoGamma autoCache(paint, &surfaceProps, &SkMatrix::I());
-
-    std::vector<glyph_t> glyphs;
-    while (*text != '\0') {
-        SkUnichar unichar = SkUTF8_NextUnichar(&text);
-        glyphs.push_back(autoCache.getCache()->unicharToGlyph(unichar));
-    }
-    canvas->drawGlyphsOnPath(glyphs.data(), glyphs.size(), path, 0, 0, paint);
+    auto utf16 = asciiToUtf16(text);
+    canvas->drawTextOnPath(utf16.get(), strlen(text), 0, path, 0, 0, paint, nullptr);
 }
 
 void TestUtils::TestTask::run() {
@@ -143,12 +111,13 @@
     renderState.onGLContextDestroyed();
 }
 
-std::unique_ptr<uint16_t[]> TestUtils::utf8ToUtf16(const char* str) {
-    const size_t strLen = strlen(str);
-    const ssize_t utf16Len = utf8_to_utf16_length((uint8_t*) str, strLen);
-    std::unique_ptr<uint16_t[]> dst(new uint16_t[utf16Len + 1]);
-    utf8_to_utf16((uint8_t*) str, strLen, (char16_t*) dst.get());
-    return dst;
+std::unique_ptr<uint16_t[]> TestUtils::asciiToUtf16(const char* str) {
+    const int length = strlen(str);
+    std::unique_ptr<uint16_t[]> utf16(new uint16_t[length]);
+    for (int i = 0; i < length; i++) {
+        utf16.get()[i] = str[i];
+    }
+    return utf16;
 }
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index a5e7a5f..73027e2 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -207,13 +207,13 @@
             std::vector<glyph_t>* outGlyphs, std::vector<float>* outPositions,
             float* outTotalAdvance, Rect* outBounds);
 
-    static void drawUtf8ToCanvas(TestCanvas* canvas, const char* text,
+    static void drawUtf8ToCanvas(Canvas* canvas, const char* text,
             const SkPaint& paint, float x, float y);
 
-    static void drawUtf8ToCanvas(TestCanvas* canvas, const char* text,
+    static void drawUtf8ToCanvas(Canvas* canvas, const char* text,
             const SkPaint& paint, const SkPath& path);
 
-    static std::unique_ptr<uint16_t[]> utf8ToUtf16(const char* str);
+    static std::unique_ptr<uint16_t[]> asciiToUtf16(const char* str);
 
 private:
     static void syncHierarchyPropertiesAndDisplayListImpl(RenderNode* node) {
diff --git a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
index 52039ef..63c067b 100644
--- a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
@@ -42,7 +42,7 @@
     }
 
     void doFrame(int frameNr) override {
-        std::unique_ptr<uint16_t[]> text = TestUtils::utf8ToUtf16(
+        std::unique_ptr<uint16_t[]> text = TestUtils::asciiToUtf16(
                 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
         ssize_t textLength = 26 * 2;
 
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index 0aabfb1..99400c6 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -344,8 +344,9 @@
             EXPECT_TRUE(stroke.contains(fill));
             EXPECT_FALSE(fill.contains(stroke));
 
+            // outset by half the stroke width
             Rect outsetFill(fill);
-            outsetFill.outset(10);
+            outsetFill.outset(5);
             EXPECT_EQ(stroke, outsetFill);
         }
     };
diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
index 6ab5110..42dabd3 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -23,6 +23,9 @@
 #include <tests/common/TestUtils.h>
 #include <utils/Color.h>
 
+#include <SkGradientShader.h>
+#include <SkShader.h>
+
 namespace android {
 namespace uirenderer {
 
@@ -567,36 +570,69 @@
 
 TEST(RecordingCanvas, refPaint) {
     SkPaint paint;
-    paint.setAntiAlias(true);
-    paint.setTextSize(20);
-    paint.setTextAlign(SkPaint::kLeft_Align);
-    paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
 
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [&paint](RecordingCanvas& canvas) {
         paint.setColor(SK_ColorBLUE);
-        // first three should use same paint
+        // first two should use same paint
         canvas.drawRect(0, 0, 200, 10, paint);
         SkPaint paintCopy(paint);
         canvas.drawRect(0, 10, 200, 20, paintCopy);
-        TestUtils::drawUtf8ToCanvas(&canvas, "helloworld", paint, 50, 25);
 
         // only here do we use different paint ptr
         paint.setColor(SK_ColorRED);
         canvas.drawRect(0, 20, 200, 30, paint);
     });
     auto ops = dl->getOps();
-    ASSERT_EQ(4u, ops.size());
+    ASSERT_EQ(3u, ops.size());
 
-    // first three are the same
+    // first two are the same
     EXPECT_NE(nullptr, ops[0]->paint);
     EXPECT_NE(&paint, ops[0]->paint);
     EXPECT_EQ(ops[0]->paint, ops[1]->paint);
-    EXPECT_EQ(ops[0]->paint, ops[2]->paint);
 
     // last is different, but still copied / non-null
-    EXPECT_NE(nullptr, ops[3]->paint);
-    EXPECT_NE(ops[0]->paint, ops[3]->paint);
-    EXPECT_NE(&paint, ops[3]->paint);
+    EXPECT_NE(nullptr, ops[2]->paint);
+    EXPECT_NE(ops[0]->paint, ops[2]->paint);
+    EXPECT_NE(&paint, ops[2]->paint);
+}
+
+TEST(RecordingCanvas, refBitmapInShader_bitmapShader) {
+    SkBitmap bitmap = TestUtils::createSkBitmap(100, 100);
+    auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) {
+        SkPaint paint;
+        SkAutoTUnref<SkShader> shader(SkShader::CreateBitmapShader(bitmap,
+                SkShader::TileMode::kClamp_TileMode,
+                SkShader::TileMode::kClamp_TileMode));
+        paint.setShader(shader);
+        canvas.drawRoundRect(0, 0, 100, 100, 20.0f, 20.0f, paint);
+    });
+    auto& bitmaps = dl->getBitmapResources();
+    EXPECT_EQ(1u, bitmaps.size());
+}
+
+TEST(RecordingCanvas, refBitmapInShader_composeShader) {
+    SkBitmap bitmap = TestUtils::createSkBitmap(100, 100);
+    auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [&bitmap](RecordingCanvas& canvas) {
+        SkPaint paint;
+        SkAutoTUnref<SkShader> shader1(SkShader::CreateBitmapShader(bitmap,
+                SkShader::TileMode::kClamp_TileMode,
+                SkShader::TileMode::kClamp_TileMode));
+
+        SkPoint center;
+        center.set(50, 50);
+        SkColor colors[2];
+        colors[0] = Color::Black;
+        colors[1] = Color::White;
+        SkAutoTUnref<SkShader> shader2(SkGradientShader::CreateRadial(center, 50, colors, nullptr, 2,
+                SkShader::TileMode::kRepeat_TileMode));
+
+        SkAutoTUnref<SkShader> composeShader(SkShader::CreateComposeShader(shader1, shader2,
+                SkXfermode::Mode::kMultiply_Mode));
+        paint.setShader(composeShader);
+        canvas.drawRoundRect(0, 0, 100, 100, 20.0f, 20.0f, paint);
+    });
+    auto& bitmaps = dl->getBitmapResources();
+    EXPECT_EQ(1u, bitmaps.size());
 }
 
 TEST(RecordingCanvas, drawText) {
@@ -605,7 +641,7 @@
         paint.setAntiAlias(true);
         paint.setTextSize(20);
         paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-        std::unique_ptr<uint16_t[]> dst = TestUtils::utf8ToUtf16("HELLO");
+        std::unique_ptr<uint16_t[]> dst = TestUtils::asciiToUtf16("HELLO");
         canvas.drawText(dst.get(), 0, 5, 5, 25, 25, kBidi_Force_LTR, paint, NULL);
     });
 
@@ -629,7 +665,7 @@
         paint.setAntiAlias(true);
         paint.setTextSize(20);
         paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-        std::unique_ptr<uint16_t[]> dst = TestUtils::utf8ToUtf16("HELLO");
+        std::unique_ptr<uint16_t[]> dst = TestUtils::asciiToUtf16("HELLO");
         canvas.drawText(dst.get(), 0, 5, 5, 25, 25, kBidi_Force_LTR, paint, NULL);
     });
 
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index fe2796c..c1805cb 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -57,8 +57,9 @@
  * <h3>Standard Extra Data</h3>
  *
  * <p>These are the current standard fields that can be used as extra data via
- * {@link #subscribe(String, Bundle, SubscriptionCallback)}, {@link #unsubscribe(String, Bundle)},
- * and {@link SubscriptionCallback#onChildrenLoaded(String, List, Bundle)}.
+ * {@link #subscribe(String, Bundle, SubscriptionCallback)},
+ * {@link #unsubscribe(String, SubscriptionCallback)}, and
+ * {@link SubscriptionCallback#onChildrenLoaded(String, List, Bundle)}.
  *
  * <ul>
  *     <li> {@link #EXTRA_PAGE}
@@ -383,7 +384,7 @@
     }
 
     /**
-     * Unsubscribes for changes to the children of the specified media id.
+     * Unsubscribes for changes to the children of the specified media id through a callback.
      * <p>
      * The query callback will no longer be invoked for results associated with
      * this id once this method returns.
@@ -391,13 +392,13 @@
      *
      * @param parentId The id of the parent media item whose list of children
      *            will be unsubscribed.
-     * @param options A bundle sent to the media browse service to subscribe.
+     * @param callback A callback sent to the media browse service to subscribe.
      */
-    public void unsubscribe(@NonNull String parentId, @NonNull Bundle options) {
-        if (options == null) {
-            throw new IllegalArgumentException("options are null");
+    public void unsubscribe(@NonNull String parentId, @NonNull SubscriptionCallback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("callback is null");
         }
-        unsubscribeInternal(parentId, options);
+        unsubscribeInternal(parentId, callback);
     }
 
     /**
@@ -490,7 +491,7 @@
         }
     }
 
-    private void unsubscribeInternal(String parentId, Bundle options) {
+    private void unsubscribeInternal(String parentId, SubscriptionCallback callback) {
         // Check arguments.
         if (TextUtils.isEmpty(parentId)) {
             throw new IllegalArgumentException("parentId is empty.");
@@ -500,16 +501,21 @@
         Subscription sub = mSubscriptions.get(parentId);
 
         // Tell the service if necessary.
-        if (sub != null && sub.removeCallback(options) && mState == CONNECT_STATE_CONNECTED) {
+        if (mState == CONNECT_STATE_CONNECTED && sub != null) {
             try {
-                // NOTE: Do not call removeSubscriptionWithOptions when options are null. Otherwise,
-                // it will break the action of support library which expects removeSubscription will
-                // be called when options are null.
-                if (options == null) {
+                if (callback == null) {
                     mServiceBinder.removeSubscription(parentId, mServiceCallbacks);
                 } else {
-                    mServiceBinder.removeSubscriptionWithOptions(
-                            parentId, options, mServiceCallbacks);
+                    final List<SubscriptionCallback> callbacks = sub.getCallbacks();
+                    final List<Bundle> optionsList = sub.getOptionsList();
+                    for (int i = callbacks.size() - 1; i >= 0; --i) {
+                        if (callbacks.get(i) == callback) {
+                            mServiceBinder.removeSubscriptionWithOptions(
+                                    parentId, optionsList.get(i), mServiceCallbacks);
+                            callbacks.remove(i);
+                            optionsList.remove(i);
+                        }
+                    }
                 }
             } catch (RemoteException ex) {
                 // Process is crashing. We will disconnect, and upon reconnect we will
@@ -517,7 +523,8 @@
                 Log.d(TAG, "removeSubscription failed with RemoteException parentId=" + parentId);
             }
         }
-        if (sub != null && sub.isEmpty()) {
+
+        if (sub != null && (sub.isEmpty() || callback == null)) {
             mSubscriptions.remove(parentId);
         }
     }
@@ -1118,16 +1125,5 @@
             mCallbacks.add(callback);
             mOptionsList.add(options);
         }
-
-        public boolean removeCallback(Bundle options) {
-            for (int i = 0; i < mOptionsList.size(); ++i) {
-                if (MediaBrowserUtils.areSameOptions(mOptionsList.get(i), options)) {
-                    mCallbacks.remove(i);
-                    mOptionsList.remove(i);
-                    return true;
-                }
-            }
-            return false;
-        }
     }
 }
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 612a147..97ef6d8 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -1689,19 +1689,20 @@
         public abstract void onTune(Uri channelUri);
 
         /**
-         * Called when the application requests to tune to a given channel for TV program recording.
+         * Calls {@link #onTune(Uri)}. Override this method in order to handle domain-specific
+         * features that are only known between certain TV inputs and their clients.
          *
          * <p>The application may call this method before starting or after stopping recording, but
          * not during recording.
          *
-         * <p>The session must call {@link #notifyTuned()} if the tune request was fulfilled, or
+         * <p>The session must call {@link #notifyTuned(Uri)} if the tune request was fulfilled, or
          * {@link #notifyError(int)} otherwise.
          *
          * @param channelUri The URI of a channel.
-         * @param params Extra parameters.
-         * @hide
+         * @param params Domain-specific data for this tune request. Keys <em>must</em> be a scoped
+         *            name, i.e. prefixed with a package name you own, so that different developers
+         *            will not create conflicting keys.
          */
-        @SystemApi
         public void onTune(Uri channelUri, Bundle params) {
             onTune(channelUri);
         }
diff --git a/media/java/android/media/tv/TvRecordingClient.java b/media/java/android/media/tv/TvRecordingClient.java
index da1002d..a5ff29f 100644
--- a/media/java/android/media/tv/TvRecordingClient.java
+++ b/media/java/android/media/tv/TvRecordingClient.java
@@ -91,22 +91,23 @@
      * Tunes to a given channel for TV program recording. The first tune request will create a new
      * recording session for the corresponding TV input and establish a connection between the
      * application and the session. If recording has already started in the current recording
-     * session, this method throws an exception.
+     * session, this method throws an exception. This can be used to provide domain-specific
+     * features that are only known between certain client and their TV inputs.
      *
      * <p>The application may call this method before starting or after stopping recording, but not
      * during recording.
      *
      * <p>The recording session will respond by calling
-     * {@link RecordingCallback#onTuned()} if the tune request was fulfilled, or
+     * {@link RecordingCallback#onTuned(Uri)} if the tune request was fulfilled, or
      * {@link RecordingCallback#onError(int)} otherwise.
      *
      * @param inputId The ID of the TV input for the given channel.
      * @param channelUri The URI of a channel.
-     * @param params Extra parameters.
+     * @param params Domain-specific data for this tune request. Keys <em>must</em> be a scoped
+     *            name, i.e. prefixed with a package name you own, so that different developers will
+     *            not create conflicting keys.
      * @throws IllegalStateException If recording is already started.
-     * @hide
      */
-    @SystemApi
     public void tune(String inputId, Uri channelUri, Bundle params) {
         if (DEBUG) Log.d(TAG, "tune(" + channelUri + ")");
         if (TextUtils.isEmpty(inputId)) {
diff --git a/media/java/android/mtp/MtpDevice.java b/media/java/android/mtp/MtpDevice.java
index 0e7013c..aa99248 100644
--- a/media/java/android/mtp/MtpDevice.java
+++ b/media/java/android/mtp/MtpDevice.java
@@ -355,6 +355,18 @@
         }
     }
 
+    /**
+     * Returns object size in 64-bit integer.
+     *
+     * The object size stored in MtpObjectInfo is unsigned 32-bit integer.
+     * The methods reads 64-bit object size from the object property so that it can fetch 4GB+
+     * object size correctly.
+     * @hide
+     */
+    public long getObjectSizeLong(int handle, int format) throws IOException {
+        return native_get_object_size_long(handle, format);
+    }
+
     // used by the JNI code
     private long mNativeContext;
 
@@ -381,4 +393,5 @@
     private native int native_submit_event_request();
     private native MtpEvent native_reap_event_request(int handle);
     private native void native_discard_event_request(int handle);
+    private native long native_get_object_size_long(int handle, int format) throws IOException;
 }
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index f593685..ae86632 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -574,6 +574,9 @@
      * Remove the subscription.
      */
     private boolean removeSubscription(String id, ConnectionRecord connection, Bundle options) {
+        if (options == null) {
+            return connection.subscriptions.remove(id) != null;
+        }
         boolean removed = false;
         List<Bundle> optionsList = connection.subscriptions.get(id);
         if (optionsList != null) {
diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp
index 0ecb750..f7c6770 100644
--- a/media/jni/android_mtp_MtpDevice.cpp
+++ b/media/jni/android_mtp_MtpDevice.cpp
@@ -42,6 +42,7 @@
 #include "MtpDeviceInfo.h"
 #include "MtpStorageInfo.h"
 #include "MtpObjectInfo.h"
+#include "MtpProperty.h"
 
 using namespace android;
 
@@ -700,6 +701,40 @@
     device->discardEventRequest(seq);
 }
 
+static jlong android_mtp_MtpDevice_get_object_size_long(
+        JNIEnv *env, jobject thiz, jint handle, jint format) {
+    MtpDevice* const device = get_device_from_object(env, thiz);
+    if (!device) {
+        env->ThrowNew(clazz_io_exception, "Failed to obtain MtpDevice.");
+        return 0;
+    }
+
+    std::unique_ptr<MtpProperty> property(
+            device->getObjectPropDesc(MTP_PROPERTY_OBJECT_SIZE, format));
+    if (!property) {
+        env->ThrowNew(clazz_io_exception, "Failed to obtain property desc.");
+        return 0;
+    }
+
+    if (property->getDataType() != MTP_TYPE_UINT64) {
+        env->ThrowNew(clazz_io_exception, "Unexpected property data type.");
+        return 0;
+    }
+
+    if (!device->getObjectPropValue(handle, property.get())) {
+        env->ThrowNew(clazz_io_exception, "Failed to obtain property value.");
+        return 0;
+    }
+
+    const jlong object_size = static_cast<jlong>(property->getCurrentValue().u.u64);
+    if (object_size < 0) {
+        env->ThrowNew(clazz_io_exception, "Object size is too large to express as jlong.");
+        return 0;
+    }
+
+    return object_size;
+}
+
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod gMethods[] = {
@@ -733,6 +768,8 @@
     {"native_reap_event_request",   "(I)Landroid/mtp/MtpEvent;",
                                             (void *)android_mtp_MtpDevice_reap_event_request},
     {"native_discard_event_request", "(I)V", (void *)android_mtp_MtpDevice_discard_event_request},
+
+    {"native_get_object_size_long", "(II)J", (void *)android_mtp_MtpDevice_get_object_size_long},
 };
 
 int register_android_mtp_MtpDevice(JNIEnv *env)
diff --git a/packages/DocumentsUI/res/layout/dialog_file_name.xml b/packages/DocumentsUI/res/layout/dialog_file_name.xml
index 5ed476f..3a95a13 100644
--- a/packages/DocumentsUI/res/layout/dialog_file_name.xml
+++ b/packages/DocumentsUI/res/layout/dialog_file_name.xml
@@ -17,6 +17,7 @@
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:fitsSystemWindows="true"
     android:padding="?android:attr/listPreferredItemPaddingEnd">
 
     <EditText
diff --git a/packages/DocumentsUI/res/layout/fragment_save.xml b/packages/DocumentsUI/res/layout/fragment_save.xml
index 7aac620..a889b9f 100644
--- a/packages/DocumentsUI/res/layout/fragment_save.xml
+++ b/packages/DocumentsUI/res/layout/fragment_save.xml
@@ -21,6 +21,7 @@
     android:orientation="horizontal"
     android:baselineAligned="false"
     android:gravity="center_vertical"
+    android:fitsSystemWindows="true"
     android:minHeight="?android:attr/listPreferredItemHeightSmall">
 
     <FrameLayout
diff --git a/packages/DocumentsUI/res/values/styles.xml b/packages/DocumentsUI/res/values/styles.xml
index a548d89..b16554c 100644
--- a/packages/DocumentsUI/res/values/styles.xml
+++ b/packages/DocumentsUI/res/values/styles.xml
@@ -36,6 +36,8 @@
         <item name="android:windowActionBar">false</item>
         <item name="android:windowActionModeOverlay">true</item>
         <item name="android:windowNoTitle">true</item>
+        <item name="android:windowTranslucentStatus">true</item>
+        <item name="android:fitsSystemWindows">false</item>
 
         <item name="android:windowSoftInputMode">stateUnspecified|adjustUnspecified</item>
     </style>
@@ -43,7 +45,7 @@
     <style name="TrimmedHorizontalProgressBar" parent="android:Widget.Material.ProgressBar.Horizontal">
         <item name="android:indeterminateDrawable">@drawable/progress_indeterminate_horizontal_material_trimmed</item>
         <item name="android:minHeight">3dp</item>
-        <item name="android:maxHeight">3dp</item>    
+        <item name="android:maxHeight">3dp</item>
     </style>
 
 </resources>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index 2911027..54202d4 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -58,6 +58,7 @@
 import java.io.FileNotFoundException;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Date;
 import java.util.List;
 import java.util.concurrent.Executor;
 
@@ -79,6 +80,7 @@
     private int mLayoutId;
 
     private boolean mNavDrawerHasFocus;
+    private long mStartTime;
 
     public abstract void onDocumentPicked(DocumentInfo doc, Model model);
     public abstract void onDocumentsPicked(List<DocumentInfo> docs);
@@ -96,16 +98,14 @@
     @CallSuper
     @Override
     public void onCreate(Bundle icicle) {
+        // Record the time when onCreate is invoked for metric.
+        mStartTime = new Date().getTime();
+
         super.onCreate(icicle);
 
         final Intent intent = getIntent();
 
-        // If startup benchmark is requested by a whitelisted testing package, then close the
-        // activity once idle, and notify the testing activity.
-        if (intent.getBooleanExtra(EXTRA_BENCHMARK, false) &&
-                BENCHMARK_TESTING_PACKAGE.equals(getCallingPackage())) {
-            closeOnIdleForTesting();
-        }
+        addListenerForLaunchCompletion();
 
         setContentView(mLayoutId);
 
@@ -127,6 +127,7 @@
         mSearchManager = new SearchViewManager(this, icicle);
 
         DocumentsToolbar toolbar = (DocumentsToolbar) findViewById(R.id.toolbar);
+        Display.adjustToolbar(toolbar, this);
         setActionBar(toolbar);
         mNavigator = new NavigationView(
                 mDrawer,
@@ -604,12 +605,10 @@
         return super.onKeyDown(keyCode, event);
     }
 
-    @VisibleForTesting
     public void addEventListener(EventListener listener) {
         mEventListeners.add(listener);
     }
 
-    @VisibleForTesting
     public void removeEventListener(EventListener listener) {
         mEventListeners.remove(listener);
     }
@@ -673,6 +672,44 @@
         return false;
     }
 
+    /**
+     * Closes the activity when it's idle.
+     */
+    private void addListenerForLaunchCompletion() {
+        addEventListener(new EventListener() {
+            @Override
+            public void onDirectoryNavigated(Uri uri) {
+            }
+
+            @Override
+            public void onDirectoryLoaded(Uri uri) {
+                removeEventListener(this);
+                getMainLooper().getQueue().addIdleHandler(new IdleHandler() {
+                    @Override
+                    public boolean queueIdle() {
+                        // If startup benchmark is requested by a whitelisted testing package, then
+                        // close the activity once idle, and notify the testing activity.
+                        if (getIntent().getBooleanExtra(EXTRA_BENCHMARK, false) &&
+                                BENCHMARK_TESTING_PACKAGE.equals(getCallingPackage())) {
+                            setResult(RESULT_OK);
+                            finish();
+                        }
+
+                        Metrics.logStartupMs(
+                                BaseActivity.this, (int) (new Date().getTime() - mStartTime));
+
+                        // Remove the idle handler.
+                        return false;
+                    }
+                });
+                new Handler().post(new Runnable() {
+                    @Override public void run() {
+                    }
+                });
+            }
+        });
+    }
+
     private static final class PickRootTask extends PairedTask<BaseActivity, Void, DocumentInfo> {
         private RootInfo mRoot;
 
@@ -694,33 +731,6 @@
         }
     }
 
-    /**
-     * Closes the activity when it's idle. Used only for tests.
-     */
-    private void closeOnIdleForTesting() {
-        addEventListener(new EventListener() {
-            @Override
-            public void onDirectoryNavigated(Uri uri) {
-            }
-
-            @Override
-            public void onDirectoryLoaded(Uri uri) {
-                getMainLooper().getQueue().addIdleHandler(new IdleHandler() {
-                    @Override
-                    public boolean queueIdle() {
-                        setResult(RESULT_OK);
-                        finish();
-                        return false;
-                    }
-                });
-                new Handler().post(new Runnable() {
-                    @Override public void run() {
-                    }
-                });
-            }
-        });
-    }
-
     private static final class HandleRootsChangedTask
             extends PairedTask<BaseActivity, RootInfo, RootInfo> {
         DocumentInfo mDownloadsDocument;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Display.java b/packages/DocumentsUI/src/com/android/documentsui/Display.java
index bae2d58..d46a3ea 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Display.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Display.java
@@ -20,13 +20,15 @@
 import android.content.Context;
 import android.graphics.Point;
 import android.util.TypedValue;
+import android.view.WindowManager;
+import android.widget.Toolbar;
 
 /*
  * Convenience class for getting display related attributes
  */
 public final class Display {
     /*
-     * Returns the screen width in pixels.
+     * Returns the screen width in raw pixels.
      */
     public static float screenWidth(Activity activity) {
         Point size = new Point();
@@ -42,15 +44,44 @@
     }
 
     /*
-     * Returns action bar height in pixels.
+     * Returns action bar height in raw pixels.
      */
     public static float actionBarHeight(Context context) {
-        int actionBarHeight = 0;
+        int height = 0;
         TypedValue tv = new TypedValue();
         if (context.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) {
-            actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data,
+            height = TypedValue.complexToDimensionPixelSize(tv.data,
                     context.getResources().getDisplayMetrics());
         }
-        return actionBarHeight;
+        return height;
+    }
+
+    /*
+     * Returns status bar height in raw pixels.
+     */
+    private static int statusBarHeight(Context context) {
+        int height = 0;
+        int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen",
+                "android");
+        if (resourceId > 0) {
+            height = context.getResources().getDimensionPixelSize(resourceId);
+        }
+        return height;
+    }
+
+    /*
+     * Adjusts toolbar for the layout with translucent status bar. Increases the
+     * height of the toolbar and adds padding at the top to accommodate status bar visible above
+     * toolbar.
+     */
+    public static void adjustToolbar(Toolbar toolbar, Activity activity) {
+        if ((activity.getWindow().getAttributes().flags
+                & WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) != 0) {
+            int statusBarHeight = Display.statusBarHeight(activity);
+            toolbar.getLayoutParams().height = (int) (Display.actionBarHeight(activity)
+                    + statusBarHeight);
+            toolbar.setPadding(toolbar.getPaddingLeft(), statusBarHeight, toolbar.getPaddingRight(),
+                    toolbar.getPaddingBottom());
+        }
     }
 }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java b/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java
index 020f2c0..2dbb730 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java
@@ -55,7 +55,7 @@
 
         View drawer = activity.findViewById(R.id.drawer_roots);
         Toolbar toolbar = (Toolbar) activity.findViewById(R.id.roots_toolbar);
-
+        Display.adjustToolbar(toolbar, activity);
         drawer.getLayoutParams().width = calculateDrawerWidth(activity);
 
         ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index 2af6c46..bed8b29 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -381,7 +381,7 @@
             }
 
             // Open the Close drawer if it is closed and we're at the top of a root.
-            if (size == 1) {
+            if (size <= 1) {
                 mDrawer.setOpen(true);
                 // Remember so we don't just close it again if back is pressed again.
                 mDrawerLastFiddled = System.currentTimeMillis();
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Metrics.java b/packages/DocumentsUI/src/com/android/documentsui/Metrics.java
index 929d1e0..e6b22e6 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Metrics.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Metrics.java
@@ -64,6 +64,7 @@
     private static final String COUNT_FILEOP_SYSTEM = "docsui_fileop_system";
     private static final String COUNT_FILEOP_EXTERNAL = "docsui_fileop_external";
     private static final String COUNT_FILEOP_CANCELED = "docsui_fileop_canceled";
+    private static final String COUNT_STARTUP_MS = "docsui_startup_ms";
 
     // Indices for bucketing roots in the roots histogram. "Other" is the catch-all index for any
     // root that is not explicitly recognized by the Metrics code (see {@link
@@ -347,7 +348,7 @@
     }
 
     /**
-     * Log the cancellation of a file operation.  Call this when a Job is canceled.
+     * Logs the cancellation of a file operation.  Call this when a Job is canceled.
      * @param context
      * @param operationType
      */
@@ -355,6 +356,15 @@
         logHistogram(context, COUNT_FILEOP_CANCELED, toMetricsOpType(operationType));
     }
 
+    /**
+     * Logs startup time in milliseconds.
+     * @param context
+     * @param startupMs Startup time in milliseconds.
+     */
+    public static void logStartupMs(Context context, int startupMs) {
+        logHistogram(context, COUNT_STARTUP_MS, startupMs);
+    }
+
     private static void logInterProviderFileOps(
             Context context,
             String histogram,
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 062f2d1..4acf85e 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -1049,7 +1049,7 @@
         }
 
         // Can't copy folders to downloads, because we don't show folders there.
-        if (!root.isDownloads()) {
+        if (root.isDownloads()) {
             for (DocumentInfo docs : files) {
                 if (docs.isDirectory()) {
                     return false;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/FileOperations.java b/packages/DocumentsUI/src/com/android/documentsui/services/FileOperations.java
index b53e165..748da00 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/services/FileOperations.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/FileOperations.java
@@ -158,11 +158,11 @@
     }
 
     /**
-     * Starts the service for a move operation.
+     * Starts the service for a delete operation.
      *
      * @param jobId A unique jobid for this job.
      *     Use {@link #createJobId} if you don't have one handy.
-     * @param srcDocs A list of src files to copy.
+     * @param srcDocs A list of src files to delete.
      * @param srcParent Parent of all the source documents.
      * @return Id of the job.
      */
@@ -184,7 +184,7 @@
      *
      * @param jobId A unique jobid for this job.
      *     Use {@link #createJobId} if you don't have one handy.
-     * @param srcDocs A list of src files to copy.
+     * @param srcDocs A list of src files for an operation.
      * @return Id of the job.
      */
     public static Intent createBaseIntent(
diff --git a/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp b/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
index 2b44d51..1d4ed1d 100644
--- a/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
+++ b/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
@@ -51,6 +51,7 @@
 static jclass app_fuse_class;
 static jmethodID app_fuse_get_file_size;
 static jmethodID app_fuse_read_object_bytes;
+static jmethodID app_fuse_write_object_bytes;
 static jfieldID app_fuse_buffer;
 
 // NOTE:
@@ -140,6 +141,9 @@
             case FUSE_READ:
                 invoke_handler(fd, req, &AppFuse::handle_fuse_read);
                 return true;
+            case FUSE_WRITE:
+                invoke_handler(fd, req, &AppFuse::handle_fuse_write);
+                return true;
             case FUSE_RELEASE:
                 invoke_handler(fd, req, &AppFuse::handle_fuse_release);
                 return true;
@@ -289,6 +293,29 @@
         return 0;
     }
 
+    int handle_fuse_write(const fuse_in_header& /* header */,
+                          const fuse_write_in* in,
+                          FuseResponse<fuse_write_out>* out) {
+        if (in->size > MAX_WRITE) {
+            return -EINVAL;
+        }
+        const std::map<uint32_t, uint64_t>::iterator it = handles_.find(in->fh);
+        if (it == handles_.end()) {
+            return -EBADF;
+        }
+        const uint64_t offset = in->offset;
+        const uint32_t size = in->size;
+        const void* const buffer = reinterpret_cast<const uint8_t*>(in) + sizeof(fuse_write_in);
+        uint32_t written_size;
+        const int result = write_object_bytes(it->second, offset, size, buffer, &written_size);
+        if (result < 0) {
+            return result;
+        }
+        out->prepare_buffer();
+        out->data()->size = written_size;
+        return 0;
+    }
+
     int handle_fuse_release(const fuse_in_header& /* header */,
                             const fuse_release_in* in,
                             FuseResponse<void>* /* out */) {
@@ -355,6 +382,27 @@
         return read_size;
     }
 
+    int write_object_bytes(int inode, uint64_t offset, uint32_t size, const void* buffer,
+                           uint32_t* written_size) {
+        ScopedLocalRef<jbyteArray> array(
+                env_,
+                static_cast<jbyteArray>(env_->GetObjectField(self_, app_fuse_buffer)));
+        {
+            ScopedByteArrayRW bytes(env_, array.get());
+            if (bytes.get() == nullptr) {
+                return -EIO;
+            }
+            memcpy(bytes.get(), buffer, size);
+        }
+        *written_size = env_->CallIntMethod(
+                self_, app_fuse_write_object_bytes, inode, offset, size, array.get());
+        if (env_->ExceptionCheck()) {
+            env_->ExceptionClear();
+            return -EIO;
+        }
+        return 0;
+    }
+
     static void fuse_reply(int fd, int unique, int reply_code, void* reply_data,
                            size_t reply_size) {
         // Don't send any data for error case.
@@ -469,6 +517,28 @@
         return -1;
     }
 
+    app_fuse_write_object_bytes = env->GetMethodID(app_fuse_class, "writeObjectBytes", "(IJI[B)I");
+    if (app_fuse_write_object_bytes == nullptr) {
+        ALOGE("Can't find getWriteObjectBytes");
+        return -1;
+    }
+
+    app_fuse_buffer = env->GetFieldID(app_fuse_class, "mBuffer", "[B");
+    if (app_fuse_buffer == nullptr) {
+        ALOGE("Can't find mBuffer");
+        return -1;
+    }
+
+    const jfieldID read_max_fied = env->GetStaticFieldID(app_fuse_class, "MAX_READ", "I");
+    if (static_cast<int>(env->GetStaticIntField(app_fuse_class, read_max_fied)) != MAX_READ) {
+        return -1;
+    }
+
+    const jfieldID write_max_fied = env->GetStaticFieldID(app_fuse_class, "MAX_WRITE", "I");
+    if (static_cast<int>(env->GetStaticIntField(app_fuse_class, write_max_fied)) != MAX_WRITE) {
+        return -1;
+    }
+
     const int result = android::AndroidRuntime::registerNativeMethods(
             env, "com/android/mtp/AppFuse", gMethods, NELEM(gMethods));
     if (result < 0) {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java b/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java
index 38435f4..1fd471c 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java
@@ -38,8 +38,12 @@
      * Max read amount specified at the FUSE kernel implementation.
      * The value is copied from sdcard.c.
      */
+    @UsedByNative("com_android_mtp_AppFuse.cpp")
     static final int MAX_READ = 128 * 1024;
 
+    @UsedByNative("com_android_mtp_AppFuse.cpp")
+    static final int MAX_WRITE = 256 * 1024;
+
     private final String mName;
     private final Callback mCallback;
 
@@ -47,7 +51,7 @@
      * Buffer for read bytes request.
      * Don't use the buffer from the out of AppFuseMessageThread.
      */
-    private byte[] mBuffer = new byte[MAX_READ];
+    private byte[] mBuffer = new byte[Math.max(MAX_READ, MAX_WRITE)];
 
     private Thread mMessageThread;
     private ParcelFileDescriptor mDeviceFd;
@@ -79,11 +83,22 @@
         }
     }
 
-    public ParcelFileDescriptor openFile(int i) throws FileNotFoundException {
+    /**
+     * Opens a file on app fuse and returns ParcelFileDescriptor.
+     *
+     * @param i ID for opened file.
+     * @param mode Mode for opening file.
+     * @see ParcelFileDescriptor#MODE_READ_ONLY
+     * @see ParcelFileDescriptor#MODE_WRITE_ONLY
+     */
+    public ParcelFileDescriptor openFile(int i, int mode) throws FileNotFoundException {
+        Preconditions.checkArgument(
+                mode == ParcelFileDescriptor.MODE_READ_ONLY ||
+                mode == ParcelFileDescriptor.MODE_WRITE_ONLY);
         return ParcelFileDescriptor.open(new File(
                 getMountPoint(),
                 Integer.toString(i)),
-                ParcelFileDescriptor.MODE_READ_ONLY);
+                mode);
     }
 
     File getMountPoint() {
@@ -100,7 +115,7 @@
         long getFileSize(int inode) throws FileNotFoundException;
 
         /**
-         * Returns flie bytes for the give inode.
+         * Returns file bytes for the give inode.
          * @param inode
          * @param offset Offset for file bytes.
          * @param size Size for file bytes.
@@ -109,6 +124,17 @@
          * @throws IOException
          */
         long readObjectBytes(int inode, long offset, long size, byte[] bytes) throws IOException;
+
+        /**
+         * Handles writing bytes for the give inode.
+         * @param inode
+         * @param offset Offset for file bytes.
+         * @param size Size for file bytes.
+         * @param bytes Buffer to store file bytes.
+         * @return Number of read bytes. Must not be negative.
+         * @throws IOException
+         */
+        int writeObjectBytes(int inode, long offset, int size, byte[] bytes) throws IOException;
     }
 
     @UsedByNative("com_android_mtp_AppFuse.cpp")
@@ -138,6 +164,15 @@
         }
     }
 
+    @UsedByNative("com_android_mtp_AppFuse.cpp")
+    @WorkerThread
+    private /* unsgined */ int writeObjectBytes(int inode,
+                                                /* unsigned */ long offset,
+                                                /* unsigned */ int size,
+                                                byte[] bytes) throws IOException {
+        return mCallback.writeObjectBytes(inode, offset, size, bytes);
+    }
+
     private native boolean native_start_app_fuse_loop(int fd);
 
     private class AppFuseMessageThread extends Thread {
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index cf5bee5..4152d1e 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -242,7 +242,8 @@
                     // extension.
                     if (MtpDeviceRecord.isPartialReadSupported(
                             device.operationsSupported, fileSize)) {
-                        return mAppFuse.openFile(Integer.parseInt(documentId));
+                        return mAppFuse.openFile(
+                                Integer.parseInt(documentId), ParcelFileDescriptor.MODE_READ_ONLY);
                     } else {
                         return getPipeManager(identifier).readDocument(mMtpManager, identifier);
                     }
@@ -606,5 +607,12 @@
         public long getFileSize(int inode) throws FileNotFoundException {
             return MtpDocumentsProvider.this.getFileSize(String.valueOf(inode));
         }
+
+        @Override
+        public int writeObjectBytes(int inode, long offset, int size, byte[] bytes)
+                throws IOException {
+            // TODO: Implement it.
+            throw new IOException();
+        }
     }
 }
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/annotations/UsedByNative.java b/packages/MtpDocumentsProvider/src/com/android/mtp/annotations/UsedByNative.java
index a7f295f..2ded925 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/annotations/UsedByNative.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/annotations/UsedByNative.java
@@ -22,7 +22,7 @@
 /**
  * Annotation that shows the method is used by JNI.
  */
-@Target(ElementType.METHOD)
+@Target({ElementType.METHOD, ElementType.FIELD})
 public @interface UsedByNative {
     /**
      * JNI file name that uses the method.
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/AppFuseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/AppFuseTest.java
index c0973bd..3b92506 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/AppFuseTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/AppFuseTest.java
@@ -56,7 +56,8 @@
                     }
                 });
         appFuse.mount(storageManager);
-        final ParcelFileDescriptor fd = appFuse.openFile(INODE);
+        final ParcelFileDescriptor fd = appFuse.openFile(
+                INODE, ParcelFileDescriptor.MODE_READ_ONLY);
         fd.close();
         appFuse.close();
     }
@@ -67,11 +68,21 @@
         final AppFuse appFuse = new AppFuse("test", new TestCallback());
         appFuse.mount(storageManager);
         try {
-            appFuse.openFile(INODE);
+            appFuse.openFile(INODE, ParcelFileDescriptor.MODE_READ_ONLY);
             fail();
-        } catch (Throwable t) {
-            assertTrue(t instanceof FileNotFoundException);
-        }
+        } catch (FileNotFoundException exp) {}
+        appFuse.close();
+    }
+
+    public void testOpenFile_illegalMode() throws IOException {
+        final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
+        final int INODE = 10;
+        final AppFuse appFuse = new AppFuse("test", new TestCallback());
+        appFuse.mount(storageManager);
+        try {
+            appFuse.openFile(INODE, ParcelFileDescriptor.MODE_READ_WRITE);
+            fail();
+        } catch (IllegalArgumentException exp) {}
         appFuse.close();
     }
 
@@ -105,7 +116,8 @@
                     }
                 });
         appFuse.mount(storageManager);
-        final ParcelFileDescriptor fd = appFuse.openFile(fileInode);
+        final ParcelFileDescriptor fd = appFuse.openFile(
+                fileInode, ParcelFileDescriptor.MODE_READ_ONLY);
         try (final ParcelFileDescriptor.AutoCloseInputStream stream =
                 new ParcelFileDescriptor.AutoCloseInputStream(fd)) {
             final byte[] buffer = new byte[1024];
@@ -115,6 +127,71 @@
         appFuse.close();
     }
 
+    public void testWriteFile() throws IOException {
+        final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
+        final int INODE = 10;
+        final byte[] resultBytes = new byte[5];
+        final AppFuse appFuse = new AppFuse(
+                "test",
+                new TestCallback() {
+                    @Override
+                    public long getFileSize(int inode) throws FileNotFoundException {
+                        if (inode != INODE) {
+                            throw new FileNotFoundException();
+                        }
+                        return resultBytes.length;
+                    }
+
+                    @Override
+                    public int writeObjectBytes(int inode, long offset, int size, byte[] bytes) {
+                        for (int i = 0; i < size; i++) {
+                            resultBytes[(int)(offset + i)] = bytes[i];
+                        }
+                        return size;
+                    }
+                });
+        appFuse.mount(storageManager);
+        final ParcelFileDescriptor fd = appFuse.openFile(
+                INODE, ParcelFileDescriptor.MODE_WRITE_ONLY);
+        try (final ParcelFileDescriptor.AutoCloseOutputStream stream =
+                new ParcelFileDescriptor.AutoCloseOutputStream(fd)) {
+            stream.write('a');
+            stream.write('b');
+            stream.write('c');
+            stream.write('d');
+            stream.write('e');
+        }
+        final byte[] BYTES = new byte[] { 'a', 'b', 'c', 'd', 'e' };
+        assertTrue(Arrays.equals(BYTES, resultBytes));
+        appFuse.close();
+    }
+
+    public void testWriteFile_writeError() throws IOException {
+        final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
+        final int INODE = 10;
+        final AppFuse appFuse = new AppFuse(
+                "test",
+                new TestCallback() {
+                    @Override
+                    public long getFileSize(int inode) throws FileNotFoundException {
+                        if (inode != INODE) {
+                            throw new FileNotFoundException();
+                        }
+                        return 5;
+                    }
+                });
+        appFuse.mount(storageManager);
+        final ParcelFileDescriptor fd = appFuse.openFile(
+                INODE, ParcelFileDescriptor.MODE_WRITE_ONLY);
+        try (final ParcelFileDescriptor.AutoCloseOutputStream stream =
+                new ParcelFileDescriptor.AutoCloseOutputStream(fd)) {
+            stream.write('a');
+            fail();
+        } catch (IOException e) {
+        }
+        appFuse.close();
+    }
+
     private static class TestCallback implements AppFuse.Callback {
         @Override
         public long getFileSize(int inode) throws FileNotFoundException {
@@ -126,5 +203,11 @@
                 throws IOException {
             throw new IOException();
         }
+
+        @Override
+        public int writeObjectBytes(int inode, long offset, int size, byte[] bytes)
+                throws IOException {
+            throw new IOException();
+        }
     }
 }
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
index 0210693..cd1d540 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
@@ -208,7 +208,7 @@
             }
         }
 
-        CharSequence status = printJob.getStatus();
+        CharSequence status = printJob.getStatus(mContext.getPackageManager());
         if (status != null) {
             builder.setContentText(status);
         } else {
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
index 18160ff..9b1ab0e 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
@@ -19,6 +19,7 @@
 import android.annotation.FloatRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.StringRes;
 import android.app.Service;
 import android.content.ComponentName;
 import android.content.Context;
@@ -575,19 +576,35 @@
         }
     }
 
-   /**
-    * Set the status for a print job.
-    *
-    * @param printJobId ID of the print job to update
-    * @param status the new status
-    */
-   public void setStatus(@NonNull PrintJobId printJobId, @Nullable CharSequence status) {
-       synchronized (mLock) {
-           getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY).setStatus(status);
+    /**
+     * Set the status for a print job.
+     *
+     * @param printJobId ID of the print job to update
+     * @param status the new status
+     */
+    public void setStatus(@NonNull PrintJobId printJobId, @Nullable CharSequence status) {
+        synchronized (mLock) {
+            getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY).setStatus(status);
 
-           mNotificationController.onUpdateNotifications(mPrintJobs);
-       }
-   }
+            mNotificationController.onUpdateNotifications(mPrintJobs);
+        }
+    }
+
+    /**
+     * Set the status for a print job.
+     *
+     * @param printJobId ID of the print job to update
+     * @param status the new status as a string resource
+     * @param appPackageName app package the resource belongs to
+     */
+    public void setStatus(@NonNull PrintJobId printJobId, @StringRes int status,
+            @Nullable CharSequence appPackageName) {
+        synchronized (mLock) {
+            getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY).setStatus(status, appPackageName);
+
+            mNotificationController.onUpdateNotifications(mPrintJobs);
+        }
+    }
 
     public boolean hasActivePrintJobsLocked() {
         final int printJobCount = mPrintJobs.size();
@@ -884,7 +901,7 @@
                         serializer.attribute(null, ATTR_PROGRESS, String.valueOf(progress));
                     }
 
-                    CharSequence status = printJob.getStatus();
+                    CharSequence status = printJob.getStatus(getPackageManager());
                     if (!TextUtils.isEmpty(status)) {
                         serializer.attribute(null, ATTR_STATUS, status.toString());
                     }
@@ -1041,7 +1058,9 @@
             try {
                 in = mStatePersistFile.openRead();
             } catch (FileNotFoundException e) {
-                Log.i(LOG_TAG, "No existing print spooler state.");
+                if (DEBUG_PERSISTENCE) {
+                    Log.d(LOG_TAG, "No existing print spooler state.");
+                }
                 return;
             }
             try {
@@ -1433,6 +1452,13 @@
             PrintSpoolerService.this.setStatus(printJobId, status);
         }
 
+        @Override
+        public void setStatusRes(@NonNull PrintJobId printJobId, @StringRes int status,
+                @NonNull CharSequence appPackageName) throws RemoteException {
+            PrintSpoolerService.this.setStatus(printJobId, status, appPackageName);
+        }
+
+
         public PrintSpoolerService getService() {
             return PrintSpoolerService.this;
         }
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index a2a3fd3..fe3ef1a 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -812,4 +812,18 @@
     <!-- HTTP proxy settings. The hint text field for the hostname. -->
     <string name="proxy_hostname_hint" translatable="false">proxy.example.com</string>
 
+    <!-- Description for the screen zoom level that makes interface elements small. [CHAR LIMIT=24] -->
+    <string name="screen_zoom_summary_small">Small</string>
+    <!-- Description for the device's default screen zoom level. [CHAR LIMIT=24] -->
+    <string name="screen_zoom_summary_default">Default</string>
+    <!-- Description for the screen zoom level that makes interface elements large. [CHAR LIMIT=24] -->
+    <string name="screen_zoom_summary_large">Large</string>
+    <!-- Description for the screen zoom level that makes interface elements larger. [CHAR LIMIT=24] -->
+    <string name="screen_zoom_summary_very_large">Larger</string>
+    <!-- Description for the screen zoom level that makes interface elements largest. [CHAR LIMIT=24] -->
+    <string name="screen_zoom_summary_extremely_large">Largest</string>
+    <!-- Description for a custom screen zoom level. This shows the requested display
+         density in raw pixels per inch rather than using a relative description. [CHAR LIMIT=24] -->
+    <string name="screen_zoom_summary_custom">Custom (<xliff:g id="densityDpi" example="160">%d</xliff:g>)</string>
+
 </resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
index 3b818c8..0e3e0d5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
@@ -60,7 +60,7 @@
 public class StorageMeasurement {
     private static final String TAG = "StorageMeasurement";
 
-    private static final boolean LOCAL_LOGV = true;
+    private static final boolean LOCAL_LOGV = false;
     static final boolean LOGV = LOCAL_LOGV && Log.isLoggable(TAG, Log.VERBOSE);
 
     private static final String DEFAULT_CONTAINER_PACKAGE = "com.android.defcontainer";
diff --git a/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java b/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java
new file mode 100644
index 0000000..78d7c56
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.display;
+
+import com.android.settingslib.R;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.AsyncTask;
+import android.os.RemoteException;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.MathUtils;
+import android.view.Display;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
+
+import java.util.Arrays;
+
+/**
+ * Utility methods for working with display density.
+ */
+public class DisplayDensityUtils {
+    private static final String LOG_TAG = "DisplayDensityUtils";
+
+    /** Minimum increment between density scales. */
+    private static final float MIN_SCALE_INTERVAL = 0.09f;
+
+    /** Minimum density scale. This is available on all devices. */
+    private static final float MIN_SCALE = 0.85f;
+
+    /** Maximum density scale. The actual scale used depends on the device. */
+    private static final float MAX_SCALE = 1.50f;
+
+    /** Summary used for "default" scale. */
+    public static final int SUMMARY_DEFAULT = R.string.screen_zoom_summary_default;
+
+    /** Summary used for "custom" scale. */
+    private static final int SUMMARY_CUSTOM = R.string.screen_zoom_summary_custom;
+
+    /**
+     * Summaries for scales smaller than "default" in order of smallest to
+     * largest.
+     */
+    private static final int[] SUMMARIES_SMALLER = new int[] {
+            R.string.screen_zoom_summary_small
+    };
+
+    /**
+     * Summaries for scales larger than "default" in order of smallest to
+     * largest.
+     */
+    private static final int[] SUMMARIES_LARGER = new int[] {
+            R.string.screen_zoom_summary_large,
+            R.string.screen_zoom_summary_very_large,
+            R.string.screen_zoom_summary_extremely_large,
+    };
+
+    /**
+     * Minimum allowed screen dimension, corresponds to resource qualifiers
+     * "small" or "sw320dp". This value must be at least the minimum screen
+     * size required by the CDD so that we meet developer expectations.
+     */
+    private static final int MIN_DIMENSION_DP = 320;
+
+    private final String[] mEntries;
+    private final int[] mValues;
+
+    private final int mDefaultDensity;
+    private final int mCurrentIndex;
+
+    public DisplayDensityUtils(Context context) {
+        final int defaultDensity = DisplayDensityUtils.getDefaultDisplayDensity(
+                Display.DEFAULT_DISPLAY);
+        if (defaultDensity <= 0) {
+            mEntries = null;
+            mValues = null;
+            mDefaultDensity = 0;
+            mCurrentIndex = -1;
+            return;
+        }
+
+        final Resources res = context.getResources();
+        final DisplayMetrics metrics = res.getDisplayMetrics();
+        final int currentDensity = metrics.densityDpi;
+        int currentDensityIndex = -1;
+
+        // Compute number of "larger" and "smaller" scales for this display.
+        final int minDimensionPx = Math.min(metrics.widthPixels, metrics.heightPixels);
+        final int maxDensity = DisplayMetrics.DENSITY_MEDIUM * minDimensionPx / MIN_DIMENSION_DP;
+        final float maxScale = Math.min(MAX_SCALE, maxDensity / (float) defaultDensity);
+        final float minScale = MIN_SCALE;
+        final int numLarger = (int) MathUtils.constrain((maxScale - 1) / MIN_SCALE_INTERVAL,
+                0, SUMMARIES_LARGER.length);
+        final int numSmaller = (int) MathUtils.constrain((1 - minScale) / MIN_SCALE_INTERVAL,
+                0, SUMMARIES_SMALLER.length);
+
+        String[] entries = new String[1 + numSmaller + numLarger];
+        int[] values = new int[entries.length];
+        int curIndex = 0;
+
+        if (numSmaller > 0) {
+            final float interval = (1 - minScale) / numSmaller;
+            for (int i = numSmaller - 1; i >= 0; i--) {
+                // Round down to a multiple of 2 by truncating the low bit.
+                final int density = ((int) (defaultDensity * (1 - (i + 1) * interval))) & ~1;
+                if (currentDensity == density) {
+                    currentDensityIndex = curIndex;
+                }
+                entries[curIndex] = res.getString(SUMMARIES_SMALLER[i]);
+                values[curIndex] = density;
+                curIndex++;
+            }
+        }
+
+        if (currentDensity == defaultDensity) {
+            currentDensityIndex = curIndex;
+        }
+        values[curIndex] = defaultDensity;
+        entries[curIndex] = res.getString(SUMMARY_DEFAULT);
+        curIndex++;
+
+        if (numLarger > 0) {
+            final float interval = (maxScale - 1) / numLarger;
+            for (int i = 0; i < numLarger; i++) {
+                // Round down to a multiple of 2 by truncating the low bit.
+                final int density = ((int) (defaultDensity * (1 + (i + 1) * interval))) & ~1;
+                if (currentDensity == density) {
+                    currentDensityIndex = curIndex;
+                }
+                values[curIndex] = density;
+                entries[curIndex] = res.getString(SUMMARIES_LARGER[i]);
+                curIndex++;
+            }
+        }
+
+        final int displayIndex;
+        if (currentDensityIndex >= 0) {
+            displayIndex = currentDensityIndex;
+        } else {
+            // We don't understand the current density. Must have been set by
+            // someone else. Make room for another entry...
+            int newLength = values.length + 1;
+            values = Arrays.copyOf(values, newLength);
+            values[curIndex] = currentDensity;
+
+            entries = Arrays.copyOf(entries, newLength);
+            entries[curIndex] = res.getString(SUMMARY_CUSTOM, currentDensity);
+
+            displayIndex = curIndex;
+        }
+
+        mDefaultDensity = defaultDensity;
+        mCurrentIndex = displayIndex;
+        mEntries = entries;
+        mValues = values;
+    }
+
+    public String[] getEntries() {
+        return mEntries;
+    }
+
+    public int[] getValues() {
+        return mValues;
+    }
+
+    public int getCurrentIndex() {
+        return mCurrentIndex;
+    }
+
+    public int getDefaultDensity() {
+        return mDefaultDensity;
+    }
+
+    /**
+     * Returns the default density for the specified display.
+     *
+     * @param displayId the identifier of the display
+     * @return the default density of the specified display, or {@code -1} if
+     *         the display does not exist or the density could not be obtained
+     */
+    private static int getDefaultDisplayDensity(int displayId) {
+       try {
+           final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+           return wm.getInitialDisplayDensity(displayId);
+       } catch (RemoteException exc) {
+           return -1;
+       }
+    }
+
+    /**
+     * Asynchronously applies display density changes to the specified display.
+     *
+     * @param displayId the identifier of the display to modify
+     */
+    public static void clearForcedDisplayDensity(final int displayId) {
+        AsyncTask.execute(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+                    wm.clearForcedDisplayDensity(displayId);
+                } catch (RemoteException exc) {
+                    Log.w(LOG_TAG, "Unable to clear forced display density setting");
+                }
+            }
+        });
+    }
+
+    /**
+     * Asynchronously applies display density changes to the specified display.
+     *
+     * @param displayId the identifier of the display to modify
+     * @param density the density to force for the specified display
+     */
+    public static void setForcedDisplayDensity(final int displayId, final int density) {
+        AsyncTask.execute(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+                    wm.setForcedDisplayDensity(displayId, density);
+                } catch (RemoteException exc) {
+                    Log.w(LOG_TAG, "Unable to save forced display density setting");
+                }
+            }
+        });
+    }
+}
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index c9582ea..c131fa5 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -409,10 +409,11 @@
 
         final BugreportInfo info = new BugreportInfo(mContext, id, pid, name, max);
         if (mProcesses.indexOfKey(id) >= 0) {
+            // BUGREPORT_STARTED intent was already received; ignore it.
             Log.w(TAG, "ID " + id + " already watched");
-        } else {
-            mProcesses.put(info.id, info);
+            return true;
         }
+        mProcesses.put(info.id, info);
         // Take initial screenshot.
         takeScreenshot(id, false);
         updateProgress(info);
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_fade_in.xml b/packages/SystemUI/res/anim/tv_pip_controls_fade_in.xml
new file mode 100644
index 0000000..89e4aac
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_pip_controls_fade_in.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <objectAnimator
+        android:propertyName="translationY"
+        android:valueTo="10dp"
+        android:interpolator="@android:interpolator/linear_out_slow_in"
+        android:duration="@integer/recents_tv_pip_focus_anim_duration" />
+    <objectAnimator
+        android:propertyName="alpha"
+        android:valueTo="1.0"
+        android:interpolator="@android:interpolator/linear_out_slow_in"
+        android:duration="@integer/recents_tv_pip_focus_anim_duration" />
+</set>
diff --git a/packages/SystemUI/res/anim/tv_pip_controls_fade_out.xml b/packages/SystemUI/res/anim/tv_pip_controls_fade_out.xml
new file mode 100644
index 0000000..c73fed6
--- /dev/null
+++ b/packages/SystemUI/res/anim/tv_pip_controls_fade_out.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <objectAnimator
+        android:propertyName="translationY"
+        android:valueTo="0dp"
+        android:interpolator="@android:interpolator/fast_out_linear_in"
+        android:duration="@integer/recents_tv_pip_focus_anim_duration" />
+    <objectAnimator
+        android:propertyName="alpha"
+        android:valueTo="0.0"
+        android:interpolator="@android:interpolator/fast_out_linear_in"
+        android:duration="@integer/recents_tv_pip_focus_anim_duration" />
+</set>
diff --git a/packages/SystemUI/res/layout/recents_on_tv.xml b/packages/SystemUI/res/layout/recents_on_tv.xml
index f0bfebe..0f8c77c 100644
--- a/packages/SystemUI/res/layout/recents_on_tv.xml
+++ b/packages/SystemUI/res/layout/recents_on_tv.xml
@@ -19,8 +19,7 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:clipChildren="false"
-    android:clipToPadding="false"
-    android:layoutDirection="rtl">
+    android:clipToPadding="false">
     <com.android.systemui.recents.tv.views.TaskStackHorizontalGridView
         android:id="@+id/task_list"
         android:layout_width="wrap_content"
@@ -29,20 +28,21 @@
         android:clipToPadding="false"
         android:descendantFocusability="beforeDescendants"
         android:layout_marginTop="@dimen/recents_tv_gird_row_top_margin"
-        android:focusable="true" />
+        android:focusable="true"
+        android:layoutDirection="rtl" />
+
     <View
         android:id="@+id/pip_shade"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:visibility="gone"
-        android:background="#76000000"/>
+        android:background="#76000000" />
 
-    <!-- Placeholder view to handle key events for PIP when it's focused.
-         Size and positions will be adjusted to comply with the PIP bounds -->
-    <View
-        android:id="@+id/pip"
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        android:visibility="gone"
-        android:focusable="true" />
+    <include
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="top|center_horizontal"
+        android:layout_marginTop="132dp"
+        layout="@layout/tv_pip_controls" />
+
 </com.android.systemui.recents.tv.views.RecentsTvView>
diff --git a/packages/SystemUI/res/layout/recents_task_view_header.xml b/packages/SystemUI/res/layout/recents_task_view_header.xml
index 59a2bd9..2b3c5df 100644
--- a/packages/SystemUI/res/layout/recents_task_view_header.xml
+++ b/packages/SystemUI/res/layout/recents_task_view_header.xml
@@ -14,28 +14,28 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
+<!-- The layouts params are calculated in TaskViewHeader.java -->
 <com.android.systemui.recents.views.TaskViewHeader
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/task_view_bar"
     android:layout_width="match_parent"
-    android:layout_height="@dimen/recents_task_view_header_height"
+    android:layout_height="wrap_content"
     android:layout_gravity="top|center_horizontal">
     <com.android.systemui.recents.views.FixedSizeImageView
         android:id="@+id/icon"
         android:contentDescription="@string/recents_app_info_button_label"
-        android:layout_width="@dimen/recents_task_view_header_icon_width"
-        android:layout_height="@dimen/recents_task_view_header_icon_height"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
         android:layout_gravity="center_vertical|start"
         android:paddingTop="8dp"
         android:paddingBottom="8dp"
         android:paddingStart="16dp"
         android:paddingEnd="12dp" />
     <LinearLayout
+        android:id="@+id/title_container"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="center_vertical|start"
-        android:layout_marginStart="56dp"
-        android:layout_marginEnd="56dp"
         android:orientation="vertical">
         <TextView
             android:id="@+id/title"
@@ -67,21 +67,20 @@
     </LinearLayout>
     <com.android.systemui.recents.views.FixedSizeImageView
         android:id="@+id/move_task"
-        android:layout_width="@dimen/recents_task_view_header_button_width"
-        android:layout_height="@dimen/recents_task_view_header_button_height"
-        android:layout_marginEnd="@dimen/recents_task_view_header_button_width"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
         android:layout_gravity="center_vertical|end"
-        android:padding="16dp"
+        android:padding="@dimen/recents_task_view_header_button_padding"
         android:src="@drawable/star"
         android:background="?android:selectableItemBackground"
         android:alpha="0"
         android:visibility="gone" />
     <com.android.systemui.recents.views.FixedSizeImageView
         android:id="@+id/dismiss_task"
-        android:layout_width="@dimen/recents_task_view_header_button_width"
-        android:layout_height="@dimen/recents_task_view_header_button_height"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
         android:layout_gravity="center_vertical|end"
-        android:padding="16dp"
+        android:padding="@dimen/recents_task_view_header_button_padding"
         android:src="@drawable/recents_dismiss_light"
         android:background="?android:selectableItemBackground"
         android:alpha="0"
diff --git a/packages/SystemUI/res/layout/recents_task_view_header_overlay.xml b/packages/SystemUI/res/layout/recents_task_view_header_overlay.xml
index 433e69e..cf09b1d 100644
--- a/packages/SystemUI/res/layout/recents_task_view_header_overlay.xml
+++ b/packages/SystemUI/res/layout/recents_task_view_header_overlay.xml
@@ -13,6 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
+<!-- The layouts params are calculated in TaskViewHeader.java -->
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
@@ -20,8 +21,8 @@
     <com.android.systemui.recents.views.FixedSizeImageView
         android:id="@+id/app_icon"
         android:contentDescription="@string/recents_app_info_button_label"
-        android:layout_width="@dimen/recents_task_view_header_icon_width"
-        android:layout_height="@dimen/recents_task_view_header_icon_height"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
         android:layout_gravity="center_vertical|start"
         android:paddingTop="8dp"
         android:paddingBottom="8dp"
@@ -32,8 +33,6 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="center_vertical|start"
-        android:layout_marginStart="56dp"
-        android:layout_marginEnd="112dp"
         android:textSize="16sp"
         android:textColor="#ffffffff"
         android:text="@string/recents_empty_message"
@@ -44,10 +43,10 @@
         android:fadingEdge="horizontal" />
     <com.android.systemui.recents.views.FixedSizeImageView
         android:id="@+id/app_info"
-        android:layout_width="@dimen/recents_task_view_header_button_width"
-        android:layout_height="@dimen/recents_task_view_header_button_height"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
         android:layout_gravity="center_vertical|end"
-        android:padding="16dp"
+        android:padding="@dimen/recents_task_view_header_button_padding"
         android:background="?android:selectableItemBackground"
         android:src="@drawable/recents_info_light" />
 </FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/tv_pip_controls.xml b/packages/SystemUI/res/layout/tv_pip_controls.xml
new file mode 100644
index 0000000..2e0c9e7
--- /dev/null
+++ b/packages/SystemUI/res/layout/tv_pip_controls.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.tv.pip.PipControlsView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/pip_controls"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:gravity="center"
+    android:orientation="horizontal">
+
+    <LinearLayout
+        android:layout_width="100dp"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:gravity="center">
+
+        <ImageView android:id="@+id/full_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:focusable="true"
+            android:src="@drawable/tv_pip_full_button" />
+
+        <TextView android:id="@+id/full_desc"
+            android:layout_width="100dp"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="3dp"
+            android:gravity="center"
+            android:visibility="invisible"
+            android:text="@string/pip_fullscreen"
+            android:fontFamily="sans-serif"
+            android:textSize="12sp"
+            android:textColor="#EEEEEE" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="100dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="-50dp"
+        android:orientation="vertical"
+        android:gravity="center">
+
+        <ImageView android:id="@+id/close_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:focusable="true"
+            android:src="@drawable/tv_pip_close_button" />
+
+        <TextView android:id="@+id/close_desc"
+            android:layout_width="100dp"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="3dp"
+            android:gravity="center"
+            android:visibility="invisible"
+            android:text="@string/pip_close"
+            android:fontFamily="sans-serif"
+            android:textSize="12sp"
+            android:textColor="#EEEEEE" />
+    </LinearLayout>
+
+    <LinearLayout android:id="@+id/play_pause"
+        android:layout_width="100dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="-50dp"
+        android:orientation="vertical"
+        android:gravity="center" >
+
+        <ImageView android:id="@+id/play_pause_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:focusable="true"
+            android:src="@drawable/tv_pip_pause_button" />
+
+        <TextView android:id="@+id/play_pause_desc"
+            android:layout_width="100dp"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="3dp"
+            android:gravity="center"
+            android:visibility="invisible"
+            android:text="@string/pip_pause"
+            android:fontFamily="sans-serif"
+            android:textSize="12sp"
+            android:textColor="#EEEEEE" />
+    </LinearLayout>
+</com.android.systemui.tv.pip.PipControlsView>
diff --git a/packages/SystemUI/res/layout/tv_pip_menu.xml b/packages/SystemUI/res/layout/tv_pip_menu.xml
index 1fec49e..c2c83ff 100644
--- a/packages/SystemUI/res/layout/tv_pip_menu.xml
+++ b/packages/SystemUI/res/layout/tv_pip_menu.xml
@@ -26,85 +26,7 @@
     android:gravity="top|center_horizontal"
     android:clipChildren="false">
 
-    <LinearLayout
-        android:layout_width="34dp"
-        android:layout_height="wrap_content"
-        android:layout_marginEnd="3dp"
-        android:orientation="vertical"
-        android:gravity="center"
-        android:clipChildren="false">
-
-        <ImageView android:id="@+id/full_button"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:focusable="true"
-            android:src="@drawable/tv_pip_full_button" />
-
-        <TextView android:id="@+id/full_desc"
-            android:layout_width="100dp"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="3dp"
-            android:gravity="center"
-            android:visibility="invisible"
-            android:text="@string/pip_fullscreen"
-            android:fontFamily="sans-serif"
-            android:textSize="12sp"
-            android:textColor="#EEEEEE"
-            android:clipChildren="false" />
-    </LinearLayout>
-
-    <LinearLayout android:id="@+id/play_pause"
-        android:layout_width="34dp"
-        android:layout_height="wrap_content"
-        android:layout_marginStart="3dp"
-        android:layout_marginEnd="3dp"
-        android:orientation="vertical"
-        android:gravity="center"
-        android:clipChildren="false">
-
-        <ImageView android:id="@+id/play_pause_button"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:focusable="true"
-            android:src="@drawable/tv_pip_pause_button" />
-
-        <TextView android:id="@+id/play_pause_desc"
-            android:layout_width="100dp"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="3dp"
-            android:gravity="center"
-            android:visibility="invisible"
-            android:text="@string/pip_pause"
-            android:fontFamily="sans-serif"
-            android:textSize="12sp"
-            android:textColor="#EEEEEE"
-            android:clipChildren="false" />
-    </LinearLayout>
-
-    <LinearLayout
-        android:layout_width="34dp"
-        android:layout_height="wrap_content"
-        android:layout_marginStart="3dp"
-        android:orientation="vertical"
-        android:gravity="center"
-        android:clipChildren="false">
-
-        <ImageView android:id="@+id/close_button"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:focusable="true"
-            android:src="@drawable/tv_pip_close_button" />
-
-        <TextView android:id="@+id/close_desc"
-            android:layout_width="100dp"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="3dp"
-            android:gravity="center"
-            android:visibility="invisible"
-            android:text="@string/pip_close"
-            android:fontFamily="sans-serif"
-            android:textSize="12sp"
-            android:textColor="#EEEEEE"
-            android:clipChildren="false" />
-    </LinearLayout>
+    <include
+        layout="@layout/tv_pip_controls"
+        android:clipChildren="false" />
 </LinearLayout>
diff --git a/packages/SystemUI/res/layout/tv_pip_overlay.xml b/packages/SystemUI/res/layout/tv_pip_overlay.xml
index 1ba423b..c5c7e84 100644
--- a/packages/SystemUI/res/layout/tv_pip_overlay.xml
+++ b/packages/SystemUI/res/layout/tv_pip_overlay.xml
@@ -46,16 +46,17 @@
         android:layout_centerHorizontal="true"
         android:orientation="horizontal">
         <ImageView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+            android:layout_width="19dp"
+            android:layout_height="19dp"
             android:src="@drawable/ic_fullscreen_white_24dp" />
         <ImageView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:src="@drawable/ic_pause_white_24dp" />
-        <ImageView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+            android:layout_width="19dp"
+            android:layout_height="19dp"
             android:src="@drawable/ic_close_white" />
+        <ImageView
+            android:id="@+id/guide_button_play_pause"
+            android:layout_width="19dp"
+            android:layout_height="19dp"
+            android:src="@drawable/ic_pause_white_24dp" />
     </LinearLayout>
 </RelativeLayout>
diff --git a/packages/SystemUI/res/values/config_tv.xml b/packages/SystemUI/res/values/config_tv.xml
new file mode 100644
index 0000000..22b7578
--- /dev/null
+++ b/packages/SystemUI/res/values/config_tv.xml
@@ -0,0 +1,29 @@
+<?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>
+    <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
+         when the PIP menu is shown in center. -->
+    <string translatable="false" name="pip_menu_bounds">"596 280 1324 690"</string>
+
+    <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
+         when the PIP is shown in Recents without focus. -->
+    <string translatable="false" name="pip_recents_bounds">"800 54 1120 234"</string>
+
+    <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
+         when the PIP is shown in Recents with focus. -->
+    <string translatable="false" name="pip_recents_focused_bounds">"775 54 1145 262"</string>
+</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 5417480..88230bc 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -604,16 +604,14 @@
 
 <!-- Recents Views -->
 
-    <!-- The height of a task view bar. -->
+    <!-- The height of a task view bar.  This has to be large enough to cover the action bar
+         height in either orientation at this smallest width. -->
     <dimen name="recents_task_view_header_height">56dp</dimen>
+    <dimen name="recents_task_view_header_height_tablet_land">64dp</dimen>
 
-    <!-- The size of the icon in the recents task view header. -->
-    <dimen name="recents_task_view_header_icon_width">@dimen/recents_task_view_header_height</dimen>
-    <dimen name="recents_task_view_header_icon_height">@dimen/recents_task_view_header_height</dimen>
-
-    <!-- The size of a button in the recents task view header. -->
-    <dimen name="recents_task_view_header_button_width">@dimen/recents_task_view_header_height</dimen>
-    <dimen name="recents_task_view_header_button_height">@dimen/recents_task_view_header_height</dimen>
+    <!-- The padding of a button in the recents task view header. -->
+    <dimen name="recents_task_view_header_button_padding">16dp</dimen>
+    <dimen name="recents_task_view_header_button_padding_tablet_land">20dp</dimen>
 
     <!-- The radius of the rounded corners on a task view and its shadow (which can be larger
          to create a softer corner effect. -->
diff --git a/packages/SystemUI/res/values/integers_tv.xml b/packages/SystemUI/res/values/integers_tv.xml
index c60c245..20cd330 100644
--- a/packages/SystemUI/res/values/integers_tv.xml
+++ b/packages/SystemUI/res/values/integers_tv.xml
@@ -17,4 +17,5 @@
     <integer name="item_scale_anim_duration">150</integer>
     <integer name="dismiss_short_duration">200</integer>
     <integer name="dismiss_long_duration">400</integer>
-</resources>
\ No newline at end of file
+    <integer name="recents_tv_pip_focus_anim_duration">200</integer>
+</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index a08b2c1..8f79bda 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -274,10 +274,7 @@
                             mWatchLongPress = new Runnable() {
                                 @Override
                                 public void run() {
-                                    float pos = getPos(ev);
-                                    float delta = pos - mInitialTouchPos;
-                                    if (mCurrView != null && !mLongPressSent
-                                            && Math.abs(delta) < mPagingTouchSlop) {
+                                    if (mCurrView != null && !mLongPressSent) {
                                         mLongPressSent = true;
                                         mCurrView.sendAccessibilityEvent(
                                                 AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
index 6d8b476..51efbf0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
@@ -36,12 +36,12 @@
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 import com.android.systemui.statusbar.policy.SecurityController;
 
+import static android.provider.Settings.ACTION_VPN_SETTINGS;
+
 public class QSFooter implements OnClickListener, DialogInterface.OnClickListener {
     protected static final String TAG = "QSFooter";
     protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    private static final String ACTION_VPN_SETTINGS = "android.net.vpn.SETTINGS";
-
     private final View mRootView;
     private final TextView mFooterText;
     private final ImageView mFooterIcon;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 3fb3106..1261a9a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -391,7 +391,7 @@
     public void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
 
-        EventBus.getDefault().post(new ConfigurationChangedEvent());
+        EventBus.getDefault().send(new ConfigurationChangedEvent());
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
index f67f099..043510e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsDebugFlags.java
@@ -34,7 +34,7 @@
         // This disables the bitmap and icon caches
         public static final boolean DisableBackgroundCache = false;
         // Enables the task affiliations
-        public static final boolean EnableAffiliatedTaskGroups = true;
+        public static final boolean EnableAffiliatedTaskGroups = false;
         // TODO: To be repurposed
         public static final boolean EnableStackActionButton = false;
         // Overrides the Tuner flags and enables the timeout
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 09b76ed..3ff33a8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -557,8 +557,13 @@
                 com.android.internal.R.dimen.navigation_bar_height);
         mNavBarWidth = res.getDimensionPixelSize(
                 com.android.internal.R.dimen.navigation_bar_width);
-        mTaskBarHeight = res.getDimensionPixelSize(
-                R.dimen.recents_task_view_header_height);
+        mTaskBarHeight = TaskStackLayoutAlgorithm.getDimensionForDevice(res,
+                R.dimen.recents_task_view_header_height,
+                R.dimen.recents_task_view_header_height,
+                R.dimen.recents_task_view_header_height,
+                R.dimen.recents_task_view_header_height_tablet_land,
+                R.dimen.recents_task_view_header_height,
+                R.dimen.recents_task_view_header_height_tablet_land);
         mDummyStackView = new TaskStackView(mContext);
         mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
                 null, false);
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 8f149d7..ea4888d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -78,6 +78,7 @@
 import com.android.systemui.recents.RecentsDebugFlags;
 import com.android.systemui.recents.RecentsImpl;
 import com.android.systemui.recents.tv.RecentsTvImpl;
+import com.android.systemui.recents.model.ThumbnailData;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -513,44 +514,47 @@
     }
 
     /** Returns the top task thumbnail for the given task id */
-    public Bitmap getTaskThumbnail(int taskId) {
+    public ThumbnailData getTaskThumbnail(int taskId) {
         if (mAm == null) return null;
+        ThumbnailData thumbnailData = new ThumbnailData();
 
         // If we are mocking, then just return a dummy thumbnail
         if (RecentsDebugFlags.Static.EnableMockTasks) {
-            Bitmap thumbnail = Bitmap.createBitmap(mDummyThumbnailWidth, mDummyThumbnailHeight,
-                    Bitmap.Config.ARGB_8888);
-            thumbnail.eraseColor(0xff333333);
-            return thumbnail;
+            thumbnailData.thumbnail = Bitmap.createBitmap(mDummyThumbnailWidth,
+                    mDummyThumbnailHeight, Bitmap.Config.ARGB_8888);
+            thumbnailData.thumbnail.eraseColor(0xff333333);
+            return thumbnailData;
         }
 
-        Bitmap thumbnail = getThumbnail(taskId);
-        if (thumbnail != null) {
-            thumbnail.setHasAlpha(false);
+        getThumbnail(taskId, thumbnailData);
+        if (thumbnailData.thumbnail != null) {
+            thumbnailData.thumbnail.setHasAlpha(false);
             // We use a dumb heuristic for now, if the thumbnail is purely transparent in the top
             // left pixel, then assume the whole thumbnail is transparent. Generally, proper
             // screenshots are always composed onto a bitmap that has no alpha.
-            if (Color.alpha(thumbnail.getPixel(0, 0)) == 0) {
-                mBgProtectionCanvas.setBitmap(thumbnail);
-                mBgProtectionCanvas.drawRect(0, 0, thumbnail.getWidth(), thumbnail.getHeight(),
-                        mBgProtectionPaint);
+            if (Color.alpha(thumbnailData.thumbnail.getPixel(0, 0)) == 0) {
+                mBgProtectionCanvas.setBitmap(thumbnailData.thumbnail);
+                mBgProtectionCanvas.drawRect(0, 0, thumbnailData.thumbnail.getWidth(),
+                        thumbnailData.thumbnail.getHeight(), mBgProtectionPaint);
                 mBgProtectionCanvas.setBitmap(null);
                 Log.e(TAG, "Invalid screenshot detected from getTaskThumbnail()");
             }
         }
-        return thumbnail;
+        return thumbnailData;
     }
 
     /**
      * Returns a task thumbnail from the activity manager
      */
-    public Bitmap getThumbnail(int taskId) {
+    public void getThumbnail(int taskId, ThumbnailData thumbnailDataOut) {
         if (mAm == null) {
-            return null;
+            return;
         }
 
         ActivityManager.TaskThumbnail taskThumbnail = mAm.getTaskThumbnail(taskId);
-        if (taskThumbnail == null) return null;
+        if (taskThumbnail == null) {
+            return;
+        }
 
         Bitmap thumbnail = taskThumbnail.mainThumbnail;
         ParcelFileDescriptor descriptor = taskThumbnail.thumbnailFileDescriptor;
@@ -564,7 +568,8 @@
             } catch (IOException e) {
             }
         }
-        return thumbnail;
+        thumbnailDataOut.thumbnail = thumbnail;
+        thumbnailDataOut.thumbnailInfo = taskThumbnail.thumbnailInfo;
     }
 
     /**
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 c863471..76ca6ca 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -160,8 +160,15 @@
                 long parentTaskLastActiveTime = parentTask != null
                         ? parentTask.lastActiveTime
                         : prevLastActiveTime;
-                t.lastActiveTime = parentTaskLastActiveTime +
-                        affiliatedTaskCounts.get(t.affiliatedTaskId, 0) + 1;
+                if (RecentsDebugFlags.Static.EnableAffiliatedTaskGroups) {
+                    t.lastActiveTime = parentTaskLastActiveTime +
+                            affiliatedTaskCounts.get(t.affiliatedTaskId, 0) + 1;
+                } else {
+                    if (t.lastActiveTime == 0) {
+                        t.lastActiveTime = parentTaskLastActiveTime -
+                                affiliatedTaskCounts.get(t.affiliatedTaskId, 0) - 1;
+                    }
+                }
             }
 
             // Compose the task key
@@ -201,7 +208,6 @@
             allTasks.add(task);
             affiliatedTaskCounts.put(taskKey.id, affiliatedTaskCounts.get(taskKey.id, 0) + 1);
             affiliatedTasks.put(taskKey.id, taskKey);
-
             prevLastActiveTime = t.lastActiveTime;
         }
         if (newLastStackActiveTime != -1) {
@@ -240,8 +246,8 @@
 
             if (opts.loadIcons && (isRunningTask || isVisibleTask)) {
                 if (task.icon == null) {
-                    task.icon = loader.getAndUpdateActivityIcon(taskKey, task.taskDescription,
-                            res, true);
+                    task.icon = loader.getAndUpdateActivityIcon(taskKey, task.taskDescription, res,
+                            true);
                 }
             }
             if (opts.loadThumbnails && (isRunningTask || isVisibleThumbnail)) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index ee4d95e..dbb692c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -95,7 +95,7 @@
 
     TaskResourceLoadQueue mLoadQueue;
     TaskKeyLruCache<Drawable> mIconCache;
-    TaskKeyLruCache<Bitmap> mThumbnailCache;
+    TaskKeyLruCache<ThumbnailData> mThumbnailCache;
     Bitmap mDefaultThumbnail;
     BitmapDrawable mDefaultIcon;
 
@@ -104,7 +104,7 @@
 
     /** Constructor, creates a new loading thread that loads task resources in the background */
     public BackgroundTaskLoader(TaskResourceLoadQueue loadQueue,
-            TaskKeyLruCache<Drawable> iconCache, TaskKeyLruCache<Bitmap> thumbnailCache,
+            TaskKeyLruCache<Drawable> iconCache, TaskKeyLruCache<ThumbnailData> thumbnailCache,
             Bitmap defaultThumbnail, BitmapDrawable defaultIcon) {
         mLoadQueue = loadQueue;
         mIconCache = iconCache;
@@ -165,7 +165,7 @@
                     final Task t = mLoadQueue.nextTask();
                     if (t != null) {
                         Drawable cachedIcon = mIconCache.get(t.key);
-                        Bitmap cachedThumbnail = mThumbnailCache.get(t.key);
+                        ThumbnailData cachedThumbnailData = mThumbnailCache.get(t.key);
 
                         // Load the icon if it is stale or we haven't cached one yet
                         if (cachedIcon == null) {
@@ -190,30 +190,32 @@
                             mIconCache.put(t.key, cachedIcon);
                         }
                         // Load the thumbnail if it is stale or we haven't cached one yet
-                        if (cachedThumbnail == null) {
+                        if (cachedThumbnailData == null) {
                             if (config.svelteLevel < RecentsConfiguration.SVELTE_DISABLE_LOADING) {
                                 if (DEBUG) Log.d(TAG, "Loading thumbnail: " + t.key);
-                                cachedThumbnail = ssp.getTaskThumbnail(t.key.id);
+                                cachedThumbnailData = ssp.getTaskThumbnail(t.key.id);
                             }
-                            if (cachedThumbnail == null) {
-                                cachedThumbnail = mDefaultThumbnail;
+
+                            if (cachedThumbnailData.thumbnail == null) {
+                                cachedThumbnailData.thumbnail = mDefaultThumbnail;
                             }
+
                             // When svelte, we trim the memory to just the visible thumbnails when
                             // leaving, so don't thrash the cache as the user scrolls (just load
                             // them from scratch each time)
                             if (config.svelteLevel < RecentsConfiguration.SVELTE_LIMIT_CACHE) {
-                                mThumbnailCache.put(t.key, cachedThumbnail);
+                                mThumbnailCache.put(t.key, cachedThumbnailData);
                             }
                         }
                         if (!mCancelled) {
                             // Notify that the task data has changed
                             final Drawable newIcon = cachedIcon;
-                            final Bitmap newThumbnail = cachedThumbnail == mDefaultThumbnail
-                                    ? null : cachedThumbnail;
+                            final ThumbnailData newThumbnailData = cachedThumbnailData;
                             mMainThreadHandler.post(new Runnable() {
                                 @Override
                                 public void run() {
-                                    t.notifyTaskDataLoaded(newThumbnail, newIcon);
+                                    t.notifyTaskDataLoaded(newThumbnailData.thumbnail, newIcon,
+                                            newThumbnailData.thumbnailInfo);
                                 }
                             });
                         }
@@ -252,7 +254,7 @@
     // package in the cache has been updated, so that we may remove it.
     private final LruCache<ComponentName, ActivityInfo> mActivityInfoCache;
     private final TaskKeyLruCache<Drawable> mIconCache;
-    private final TaskKeyLruCache<Bitmap> mThumbnailCache;
+    private final TaskKeyLruCache<ThumbnailData> mThumbnailCache;
     private final TaskKeyLruCache<String> mActivityLabelCache;
     private final TaskKeyLruCache<String> mContentDescriptionCache;
     private final TaskResourceLoadQueue mLoadQueue;
@@ -356,9 +358,16 @@
      */
     public void loadTaskData(Task t, boolean fetchAndInvalidateThumbnails) {
         Drawable icon = mIconCache.getAndInvalidateIfModified(t.key);
-        Bitmap thumbnail = mDefaultThumbnail;
+        Bitmap thumbnail = null;
+        ActivityManager.TaskThumbnailInfo thumbnailInfo = null;
         if (fetchAndInvalidateThumbnails) {
-            thumbnail = mThumbnailCache.getAndInvalidateIfModified(t.key);
+            ThumbnailData thumbnailData = mThumbnailCache.getAndInvalidateIfModified(t.key);
+            if (thumbnailData != null) {
+                thumbnail = thumbnailData.thumbnail;
+                thumbnailInfo = thumbnailData.thumbnailInfo;
+            }
+        } else {
+            thumbnail = mDefaultThumbnail;
         }
 
         // Grab the thumbnail/icon from the cache, if either don't exist, then trigger a reload and
@@ -368,7 +377,8 @@
         if (requiresLoad) {
             mLoadQueue.addTask(t);
         }
-        t.notifyTaskDataLoaded(thumbnail == mDefaultThumbnail ? null : thumbnail, icon);
+        t.notifyTaskDataLoaded(thumbnail == mDefaultThumbnail ? null : thumbnail, icon,
+                thumbnailInfo);
     }
 
     /** Releases the task resource data back into the pool. */
@@ -535,19 +545,19 @@
         SystemServicesProxy ssp = Recents.getSystemServices();
 
         // Return the cached thumbnail if it exists
-        Bitmap thumbnail = mThumbnailCache.getAndInvalidateIfModified(taskKey);
-        if (thumbnail != null) {
-            return thumbnail;
+        ThumbnailData thumbnailData = mThumbnailCache.getAndInvalidateIfModified(taskKey);
+        if (thumbnailData != null) {
+            return thumbnailData.thumbnail;
         }
 
         if (loadIfNotCached) {
             RecentsConfiguration config = Recents.getConfiguration();
             if (config.svelteLevel < RecentsConfiguration.SVELTE_DISABLE_LOADING) {
                 // Load the thumbnail from the system
-                thumbnail = ssp.getTaskThumbnail(taskKey.id);
-                if (thumbnail != null) {
-                    mThumbnailCache.put(taskKey, thumbnail);
-                    return thumbnail;
+                thumbnailData = ssp.getTaskThumbnail(taskKey.id);
+                if (thumbnailData.thumbnail != null) {
+                    mThumbnailCache.put(taskKey, thumbnailData);
+                    return thumbnailData.thumbnail;
                 }
             }
         }
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 3e858a8..d5d5aa0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -40,7 +40,7 @@
     /* Task callbacks */
     public interface TaskCallbacks {
         /* Notifies when a task has been bound */
-        public void onTaskDataLoaded(Task task);
+        public void onTaskDataLoaded(Task task, ActivityManager.TaskThumbnailInfo thumbnailInfo);
         /* Notifies when a task has been unbound */
         public void onTaskDataUnloaded();
         /* Notifies when a task's stack id has changed. */
@@ -217,6 +217,7 @@
         this.colorBackground = o.colorBackground;
         this.useLightOnPrimaryColor = o.useLightOnPrimaryColor;
         this.bounds = o.bounds;
+        this.taskDescription = o.taskDescription;
         this.isLaunchTarget = o.isLaunchTarget;
         this.isStackTask = o.isStackTask;
         this.isSystemApp = o.isSystemApp;
@@ -264,12 +265,13 @@
     }
 
     /** Notifies the callback listeners that this task has been loaded */
-    public void notifyTaskDataLoaded(Bitmap thumbnail, Drawable applicationIcon) {
+    public void notifyTaskDataLoaded(Bitmap thumbnail, Drawable applicationIcon,
+            ActivityManager.TaskThumbnailInfo thumbnailInfo) {
         this.icon = applicationIcon;
         this.thumbnail = thumbnail;
         int callbackCount = mCallbacks.size();
         for (int i = 0; i < callbackCount; i++) {
-            mCallbacks.get(i).onTaskDataLoaded(this);
+            mCallbacks.get(i).onTaskDataLoaded(this, thumbnailInfo);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 930e2d0..a930791 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -429,12 +429,14 @@
         mStackTaskList.setFilter(new TaskFilter() {
             @Override
             public boolean acceptTask(SparseArray<Task> taskIdMap, Task t, int index) {
-                if (t.isAffiliatedTask()) {
-                    // If this task is affiliated with another parent in the stack, then the
-                    // historical state of this task depends on the state of the parent task
-                    Task parentTask = taskIdMap.get(t.affiliationTaskId);
-                    if (parentTask != null) {
-                        t = parentTask;
+                if (RecentsDebugFlags.Static.EnableAffiliatedTaskGroups) {
+                    if (t.isAffiliatedTask()) {
+                        // If this task is affiliated with another parent in the stack, then the
+                        // historical state of this task depends on the state of the parent task
+                        Task parentTask = taskIdMap.get(t.affiliationTaskId);
+                        if (parentTask != null) {
+                            t = parentTask;
+                        }
                     }
                 }
                 return t.isStackTask;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/ThumbnailData.java b/packages/SystemUI/src/com/android/systemui/recents/model/ThumbnailData.java
new file mode 100644
index 0000000..d0cdae5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/ThumbnailData.java
@@ -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.
+ */
+
+package com.android.systemui.recents.model;
+
+import android.app.ActivityManager;
+import android.graphics.Bitmap;
+
+/**
+ * Data for a single thumbnail.
+ */
+public class ThumbnailData {
+    public Bitmap thumbnail;
+    public ActivityManager.TaskThumbnailInfo thumbnailInfo;
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
index d8e82d9..2c34523 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
@@ -15,6 +15,8 @@
  */
 package com.android.systemui.recents.tv;
 
+import android.animation.AnimatorInflater;
+import android.animation.AnimatorSet;
 import android.app.Activity;
 import android.app.ActivityOptions;
 import android.content.Intent;
@@ -57,6 +59,7 @@
 import com.android.systemui.recents.tv.views.TaskStackHorizontalViewAdapter;
 import com.android.systemui.statusbar.BaseStatusBar;
 import com.android.systemui.tv.pip.PipManager;
+import com.android.systemui.tv.pip.PipControlsView;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -77,8 +80,10 @@
     private boolean mIgnoreAltTabRelease;
 
     private RecentsTvView mRecentsView;
-    private View mPipView;
+    private PipControlsView mPipControlsView;
     private View mPipShadeView;
+    private AnimatorSet mPipControlsViewFadeInAnimator;
+    private AnimatorSet mPipControlsViewFadeOutAnimator;
     private TaskStackHorizontalViewAdapter mTaskStackViewAdapter;
     private FinishRecentsRunnable mFinishLaunchHomeRunnable;
 
@@ -95,7 +100,9 @@
         }
 
         @Override
-        public void onShowPipMenu() { }
+        public void onShowPipMenu() {
+            updatePipUI();
+        }
 
         @Override
         public void onMoveToFullscreen() { }
@@ -106,7 +113,6 @@
         @Override
         public void onMediaControllerChanged() { }
     };
-    private boolean mHasPip;
 
     /**
      * A common Runnable to finish Recents by launching Home with an animation depending on the
@@ -253,8 +259,22 @@
         mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
                 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
                 View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
-        mPipView = findViewById(R.id.pip);
+        mPipControlsView = (PipControlsView) findViewById(R.id.pip_controls);
+        mPipControlsView.setListener(new PipControlsView.Listener() {
+            @Override
+            public void onClosed() {
+                dismissRecentsToLaunchTargetTaskOrHome();
+            }
+        });
         mPipShadeView = findViewById(R.id.pip_shade);
+
+        mPipControlsViewFadeInAnimator = (AnimatorSet) AnimatorInflater.loadAnimator(this,
+                R.anim.tv_pip_controls_fade_in);
+        mPipControlsViewFadeInAnimator.setTarget(mPipControlsView);
+        mPipControlsViewFadeOutAnimator = (AnimatorSet) AnimatorInflater.loadAnimator(this,
+                R.anim.tv_pip_controls_fade_out);
+        mPipControlsViewFadeOutAnimator.setTarget(mPipControlsView);
+
         getWindow().getAttributes().privateFlags |=
                 WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
 
@@ -265,7 +285,6 @@
                 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
         mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent);
 
-        mHasPip = false;
         updatePipUI();
         mPipManager.addListener(mPipListener);
     }
@@ -456,37 +475,25 @@
     }
 
     private void updatePipUI() {
-        if (mHasPip == mPipManager.isPipShown()) {
-            return;
-        }
-        mHasPip = mPipManager.isPipShown();
-        if (mHasPip) {
-            // Place mPipView at the PIP bounds for fine tuned focus handling.
-            Rect pipBounds = mPipManager.getPipBounds();
-            LayoutParams lp = (LayoutParams) mPipView.getLayoutParams();
-            lp.width = pipBounds.width();
-            lp.height = pipBounds.height();
-            lp.leftMargin = pipBounds.left;
-            lp.topMargin = pipBounds.top;
-            mPipView.setLayoutParams(lp);
-
-            mPipView.setVisibility(View.VISIBLE);
-            mPipView.setOnClickListener(new View.OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    mPipManager.resizePinnedStack(PipManager.STATE_PIP_MENU);
-                }
-            });
-            mPipView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+        if (mPipManager.isPipShown()) {
+            mPipControlsView.setAlpha(0);
+            mPipControlsView.setVisibility(View.VISIBLE);
+            mPipShadeView.setVisibility(View.INVISIBLE);
+            mPipControlsView.setOnChildFocusChangeListener(new View.OnFocusChangeListener() {
                 @Override
                 public void onFocusChange(View v, boolean hasFocus) {
                     mPipManager.onPipViewFocusChangedInRecents(hasFocus);
+                    if (hasFocus) {
+                        mPipControlsViewFadeInAnimator.start();
+                    } else {
+                        mPipControlsViewFadeOutAnimator.start();
+                    }
                     mPipShadeView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
                 }
             });
             mPipShadeView.setVisibility(View.GONE);
         } else {
-            mPipView.setVisibility(View.GONE);
+            mPipControlsView.setVisibility(View.GONE);
             mPipShadeView.setVisibility(View.GONE);
         }
     }
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 93c5fc9..83f8b7e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -963,11 +963,22 @@
         }
 
         // Ensure that the new width is at most the smaller display edge size
-        Rect displayRect = Recents.getSystemServices().getDisplayRect();
+        SystemServicesProxy ssp = Recents.getSystemServices();
+        Rect displayRect = ssp.getDisplayRect();
         int sideMargin = getScaleForExtent(windowRect, displayRect, mBaseSideMargin, mMinMargin,
                 WIDTH);
-        int targetStackWidth = Math.min(taskStackBounds.width() - 2 * sideMargin,
-                Math.min(displayRect.width(), displayRect.height()));
+        int targetStackWidth = taskStackBounds.width() - 2 * sideMargin;
+        if (ssp.getDisplayOrientation() == Configuration.ORIENTATION_LANDSCAPE) {
+            // If we are in landscape, calculate the width of the stack in portrait and ensure that
+            // we are not larger than that size
+            Rect portraitDisplayRect = new Rect(0, 0,
+                    Math.min(displayRect.width(), displayRect.height()),
+                    Math.max(displayRect.width(), displayRect.height()));
+            int portraitSideMargin = getScaleForExtent(portraitDisplayRect, portraitDisplayRect,
+                    mBaseSideMargin, mMinMargin, WIDTH);
+            targetStackWidth = Math.min(targetStackWidth,
+                    portraitDisplayRect.width() - 2 * portraitSideMargin);
+        }
         taskStackBounds.inset((taskStackBounds.width() - targetStackWidth) / 2, 0);
     }
 
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 107d8d4..0c47b13 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -22,6 +22,7 @@
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
+import android.app.ActivityManager;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Outline;
@@ -128,8 +129,6 @@
 
     @ViewDebug.ExportedProperty(category="recents")
     float mDimAlpha;
-    PorterDuffColorFilter mDimColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_ATOP);
-    Paint mDimLayerPaint = new Paint();
     float mActionButtonTranslationZ;
 
     @ViewDebug.ExportedProperty(deepExport=true, prefix="task_")
@@ -575,9 +574,9 @@
     }
 
     @Override
-    public void onTaskDataLoaded(Task task) {
+    public void onTaskDataLoaded(Task task, ActivityManager.TaskThumbnailInfo thumbnailInfo) {
         // Bind each of the views to the new task data
-        mThumbnailView.rebindToTask(mTask, mIsDisabledInSafeMode);
+        mThumbnailView.rebindToTask(mTask, thumbnailInfo, mIsDisabledInSafeMode);
         mHeaderView.rebindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode);
         mTaskDataLoaded = true;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index a2e9573..62995a6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -34,9 +34,11 @@
 import android.os.CountDownTimer;
 import android.support.v4.graphics.ColorUtils;
 import android.util.AttributeSet;
+import android.view.Gravity;
 import android.view.View;
 import android.view.ViewAnimationUtils;
 import android.view.ViewDebug;
+import android.view.ViewGroup;
 import android.view.ViewStub;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
@@ -153,6 +155,8 @@
     // Header drawables
     @ViewDebug.ExportedProperty(category="recents")
     Rect mTaskViewRect = new Rect();
+    int mHeaderBarHeight;
+    int mHeaderButtonPadding;
     int mCornerRadius;
     int mHighlightHeight;
     @ViewDebug.ExportedProperty(category="recents")
@@ -245,6 +249,57 @@
         }
         mFocusTimerIndicatorStub = (ViewStub) findViewById(R.id.focus_timer_indicator_stub);
         mAppOverlayViewStub = (ViewStub) findViewById(R.id.app_overlay_stub);
+
+        // Update the dimensions of everything in the header. We do this because we need to use
+        // resources for the display, and not the current configuration.
+        Resources res = getResources();
+        mHeaderBarHeight = TaskStackLayoutAlgorithm.getDimensionForDevice(res,
+                R.dimen.recents_task_view_header_height,
+                R.dimen.recents_task_view_header_height,
+                R.dimen.recents_task_view_header_height,
+                R.dimen.recents_task_view_header_height_tablet_land,
+                R.dimen.recents_task_view_header_height,
+                R.dimen.recents_task_view_header_height_tablet_land);
+        mHeaderButtonPadding = TaskStackLayoutAlgorithm.getDimensionForDevice(res,
+                R.dimen.recents_task_view_header_button_padding,
+                R.dimen.recents_task_view_header_button_padding,
+                R.dimen.recents_task_view_header_button_padding,
+                R.dimen.recents_task_view_header_button_padding_tablet_land,
+                R.dimen.recents_task_view_header_button_padding,
+                R.dimen.recents_task_view_header_button_padding_tablet_land);
+        updateLayoutParams(mIconView, findViewById(R.id.title_container), mMoveTaskButton,
+                mDismissButton);
+    }
+
+    /**
+     * Programmatically sets the layout params for a header bar layout.  This is necessary because
+     * we can't get resources based on the current configuration, but instead need to get them
+     * based on the device configuration.
+     */
+    private void updateLayoutParams(View icon, View title, View secondaryButton, View button) {
+        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT, mHeaderBarHeight, Gravity.TOP);
+        setLayoutParams(lp);
+        lp = new FrameLayout.LayoutParams(mHeaderBarHeight, mHeaderBarHeight, Gravity.START);
+        icon.setLayoutParams(lp);
+        lp = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.START | Gravity.CENTER_VERTICAL);
+        lp.leftMargin = mHeaderBarHeight;
+        lp.rightMargin = mMoveTaskButton != null
+                ? 2 * mHeaderBarHeight
+                : mHeaderBarHeight;
+        title.setLayoutParams(lp);
+        if (secondaryButton != null) {
+            lp = new FrameLayout.LayoutParams(mHeaderBarHeight, mHeaderBarHeight, Gravity.END);
+            lp.rightMargin = mHeaderBarHeight;
+            secondaryButton.setLayoutParams(lp);
+            secondaryButton.setPadding(mHeaderButtonPadding, mHeaderButtonPadding,
+                    mHeaderButtonPadding, mHeaderButtonPadding);
+        }
+        lp = new FrameLayout.LayoutParams(mHeaderBarHeight, mHeaderBarHeight, Gravity.END);
+        button.setLayoutParams(lp);
+        button.setPadding(mHeaderButtonPadding, mHeaderButtonPadding, mHeaderButtonPadding,
+                mHeaderButtonPadding);
     }
 
     @Override
@@ -579,6 +634,7 @@
             mAppInfoView = (ImageView) mAppOverlayView.findViewById(R.id.app_info);
             mAppInfoView.setOnClickListener(this);
             mAppTitleView = (TextView) mAppOverlayView.findViewById(R.id.app_title);
+            updateLayoutParams(mAppIconView, mAppTitleView, null, mAppInfoView);
         }
 
         // Update the overlay contents for the current app
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 e46708e..e9c09ac 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -16,7 +16,9 @@
 
 package com.android.systemui.recents.views;
 
+import android.app.ActivityManager;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.graphics.BitmapShader;
 import android.graphics.Canvas;
@@ -34,6 +36,8 @@
 import android.view.ViewDebug;
 
 import com.android.systemui.R;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.model.Task;
 
 
@@ -43,17 +47,24 @@
  */
 public class TaskViewThumbnail extends View {
 
-
     private static final ColorMatrix TMP_FILTER_COLOR_MATRIX = new ColorMatrix();
     private static final ColorMatrix TMP_BRIGHTNESS_COLOR_MATRIX = new ColorMatrix();
 
     private Task mTask;
 
+    private Rect mDisplayRect = new Rect();
+    private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
+
     // Drawing
     @ViewDebug.ExportedProperty(category="recents")
+    Rect mTaskViewRect = new Rect();
+    @ViewDebug.ExportedProperty(category="recents")
     Rect mThumbnailRect = new Rect();
     @ViewDebug.ExportedProperty(category="recents")
-    Rect mTaskViewRect = new Rect();
+    float mThumbnailScale;
+    float mFullscreenThumbnailScale;
+    ActivityManager.TaskThumbnailInfo mThumbnailInfo;
+
     int mCornerRadius;
     @ViewDebug.ExportedProperty(category="recents")
     float mDimAlpha;
@@ -97,6 +108,8 @@
         mCornerRadius = getResources().getDimensionPixelSize(
                 R.dimen.recents_task_view_rounded_corners_radius);
         mBgFillPaint.setColor(Color.WHITE);
+        mFullscreenThumbnailScale = context.getResources().getFraction(
+                com.android.internal.R.fraction.thumbnail_fullscreen_scale, 1, 1);
     }
 
     /**
@@ -114,57 +127,75 @@
     }
 
     @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        SystemServicesProxy ssp = Recents.getSystemServices();
+        mOrientation = ssp.getDisplayOrientation();
+        mDisplayRect = ssp.getDisplayRect();
+    }
+
+    @Override
     protected void onDraw(Canvas canvas) {
         if (mInvisible) {
             return;
         }
 
-        int thumbnailHeight = (int) (((float) mTaskViewRect.width() / mThumbnailRect.width()) *
-                mThumbnailRect.height());
-        if (thumbnailHeight >= mTaskViewRect.height()) {
-            // The thumbnail fills the full task view bounds, so just draw it
-            canvas.drawRoundRect(0, 0, mTaskViewRect.width(), mTaskViewRect.height(),
-                    mCornerRadius, mCornerRadius, mDrawPaint);
-        } else {
-            int count = 0;
-            if (thumbnailHeight > 0) {
-                // The thumbnail only covers part of the task view bounds, so fill in the
-                // non-thumbnail space with the default background color.  This is the equivalent of
-                // the GL border texture mode.
-                count = canvas.save();
+        if (mBitmapShader != null) {
+            int viewWidth = mTaskViewRect.width();
+            int viewHeight = mTaskViewRect.height();
+
+            // We are drawing the thumbnail in the same orientation, so just fit the width
+            int thumbnailWidth = (int) (mThumbnailRect.width() * mThumbnailScale);
+            int thumbnailHeight = (int) (mThumbnailRect.height() * mThumbnailScale);
+
+            if (thumbnailWidth >= viewWidth && thumbnailHeight >= viewHeight) {
+                // Thumbnail fills the full task view bounds, so just draw it
+                canvas.drawRoundRect(0, 0, viewWidth, viewHeight, mCornerRadius, mCornerRadius,
+                        mDrawPaint);
+            } else {
+                // Thumbnail does not fill the full task view bounds, so just draw it and fill the
+                // empty areas with the background color
+                int count = canvas.save();
 
                 // Since we only want the top corners to be rounded, draw slightly beyond the
                 // thumbnail height, but clip to the thumbnail height
-                canvas.clipRect(0, 0, mTaskViewRect.width(), thumbnailHeight, Region.Op.REPLACE);
-                canvas.drawRoundRect(0, 0, mTaskViewRect.width(), thumbnailHeight + mCornerRadius,
+                canvas.clipRect(0, 0, thumbnailWidth, thumbnailHeight, Region.Op.REPLACE);
+                canvas.drawRoundRect(0, 0,
+                        thumbnailWidth + (thumbnailWidth < viewWidth ? mCornerRadius : 0),
+                        thumbnailHeight + (thumbnailHeight < viewHeight ? mCornerRadius : 0),
                         mCornerRadius, mCornerRadius, mDrawPaint);
-            }
 
-            // In the remaining space, draw the background color
-            canvas.clipRect(0, thumbnailHeight, mTaskViewRect.width(), mTaskViewRect.height(),
-                    Region.Op.REPLACE);
-            canvas.drawRoundRect(0, Math.max(0, thumbnailHeight - mCornerRadius),
-                    mTaskViewRect.width(), mTaskViewRect.height(), mCornerRadius, mCornerRadius,
-                    mBgFillPaint);
+                // In the remaining space, draw the background color
+                if (thumbnailWidth < viewWidth) {
+                    canvas.clipRect(thumbnailWidth, 0, viewWidth, viewHeight, Region.Op.REPLACE);
+                    canvas.drawRoundRect(Math.max(0, thumbnailWidth - mCornerRadius), 0,
+                            viewWidth, viewHeight, mCornerRadius, mCornerRadius, mBgFillPaint);
+                }
+                if (thumbnailWidth > 0 && thumbnailHeight < viewHeight) {
+                    canvas.clipRect(0, thumbnailHeight, viewWidth, viewHeight, Region.Op.REPLACE);
+                    canvas.drawRoundRect(0, Math.max(0, thumbnailHeight - mCornerRadius),
+                            viewWidth, viewHeight, mCornerRadius, mCornerRadius, mBgFillPaint);
+                }
 
-            if (thumbnailHeight > 0) {
                 canvas.restoreToCount(count);
             }
         }
     }
 
     /** Sets the thumbnail to a given bitmap. */
-    void setThumbnail(Bitmap bm) {
+    void setThumbnail(Bitmap bm, ActivityManager.TaskThumbnailInfo thumbnailInfo) {
         if (bm != null) {
-            mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP,
-                    Shader.TileMode.CLAMP);
+            mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
             mDrawPaint.setShader(mBitmapShader);
             mThumbnailRect.set(0, 0, bm.getWidth(), bm.getHeight());
+            mThumbnailInfo = thumbnailInfo;
             updateThumbnailScale();
         } else {
             mBitmapShader = null;
             mDrawPaint.setShader(null);
             mThumbnailRect.setEmpty();
+            mThumbnailInfo = null;
         }
     }
 
@@ -210,20 +241,46 @@
      * Updates the scale of the bitmap relative to this view.
      */
     public void updateThumbnailScale() {
+        mThumbnailScale = 1f;
         if (mBitmapShader != null) {
-            float thumbnailScale;
-            if (!mTask.isFreeformTask() || mTask.bounds == null) {
-                // If this is a stack task, or a stack task moved into the freeform workspace, then
-                // just scale this thumbnail to fit the width of the view
-                thumbnailScale = (float) mTaskViewRect.width() / mThumbnailRect.width();
+
+            if (mThumbnailInfo != null) {
+                System.out.println(mTask.title + " bounds: " + mThumbnailInfo.taskWidth + "x" + mThumbnailInfo.taskHeight + ", " + mThumbnailInfo.screenOrientation);
+            }
+
+            // We consider this a stack task if it is not freeform (ie. has no bounds) or has been
+            // dragged into the stack from the freeform workspace
+            boolean isStackTask = !mTask.isFreeformTask() || mTask.bounds == null;
+            if (mTaskViewRect.isEmpty() || mThumbnailInfo == null ||
+                    mThumbnailInfo.taskWidth == 0 || mThumbnailInfo.taskHeight == 0) {
+                // If we haven't measured or the thumbnail is invalid, skip the thumbnail drawing
+                // and only draw the background color
+                mThumbnailScale = 0f;
+            } else if (isStackTask) {
+                float invThumbnailScale = 1f / mFullscreenThumbnailScale;
+                if (mOrientation == Configuration.ORIENTATION_PORTRAIT) {
+                    if (mThumbnailInfo.screenOrientation == Configuration.ORIENTATION_PORTRAIT) {
+                        // If we are in the same orientation as the screenshot, just scale it to the
+                        // width of the task view
+                        mThumbnailScale = (float) mTaskViewRect.width() / mThumbnailRect.width();
+                    } else {
+                        // Scale the landscape thumbnail up to app size, then scale that to the task
+                        // view size to match other portrait screenshots
+                        mThumbnailScale = invThumbnailScale *
+                                ((float) mTaskViewRect.width() / mDisplayRect.width());
+                    }
+                } else {
+                    // Otherwise, scale the screenshot to fit 1:1 in the current orientation
+                    mThumbnailScale = invThumbnailScale;
+                }
             } else {
                 // Otherwise, if this is a freeform task with task bounds, then scale the thumbnail
                 // to fit the entire bitmap into the task bounds
-                thumbnailScale = Math.min(
+                mThumbnailScale = Math.min(
                         (float) mTaskViewRect.width() / mThumbnailRect.width(),
                         (float) mTaskViewRect.height() / mThumbnailRect.height());
             }
-            mScaleMatrix.setScale(thumbnailScale, thumbnailScale);
+            mScaleMatrix.setScale(mThumbnailScale, mThumbnailScale);
             mBitmapShader.setLocalMatrix(mScaleMatrix);
         }
         if (!mInvisible) {
@@ -261,22 +318,23 @@
     }
 
     /** Binds the thumbnail view to the task */
-    void rebindToTask(Task t, boolean disabledInSafeMode) {
+    void rebindToTask(Task t, ActivityManager.TaskThumbnailInfo thumbnailInfo,
+            boolean disabledInSafeMode) {
         mTask = t;
         mDisabledInSafeMode = disabledInSafeMode;
         if (t.thumbnail != null) {
-            setThumbnail(t.thumbnail);
+            setThumbnail(t.thumbnail, thumbnailInfo);
             if (t.colorBackground != 0) {
                 mBgFillPaint.setColor(t.colorBackground);
             }
         } else {
-            setThumbnail(null);
+            setThumbnail(null, null);
         }
     }
 
     /** Unbinds the thumbnail view from the task */
     void unbindFromTask() {
         mTask = null;
-        setThumbnail(null);
+        setThumbnail(null, null);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 560dbac..d9a0f43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -202,6 +202,7 @@
             notifyBarPanelExpansionChanged();
         }
     };
+    private NotificationGroupManager mGroupManager;
 
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -415,6 +416,11 @@
             if (!(child instanceof ExpandableNotificationRow)) {
                 continue;
             }
+            boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
+                    ((ExpandableNotificationRow) child).getStatusBarNotification());
+            if (suppressedSummary) {
+                continue;
+            }
             availableSpace -= child.getMinHeight() + notificationPadding;
             if (availableSpace >= 0 && count < maximum) {
                 count++;
@@ -2298,4 +2304,8 @@
         List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
         return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName());
     }
+
+    public void setGroupManager(NotificationGroupManager groupManager) {
+        mGroupManager = groupManager;
+    }
 }
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 86031e1..9b1f338 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -708,6 +708,7 @@
         mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(
                 R.id.notification_panel);
         mNotificationPanel.setStatusBar(this);
+        mNotificationPanel.setGroupManager(mGroupManager);
 
         mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);
         mStatusBarView.setBar(this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 632ce21..b5030e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -2063,6 +2063,14 @@
         }
     }
 
+    @Override
+    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+        super.requestDisallowInterceptTouchEvent(disallowIntercept);
+        if (disallowIntercept) {
+            mSwipeHelper.removeLongPressCallback();
+        }
+    }
+
     private void onViewRemovedInternal(View child) {
         if (mChangePositionInProgress) {
             // This is only a position change, don't do anything special
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java
new file mode 100644
index 0000000..0e64dcd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java
@@ -0,0 +1,251 @@
+/*
+ * 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.tv.pip;
+
+import android.app.Activity;
+import android.content.Context;
+import android.media.session.MediaController;
+import android.media.session.PlaybackState;
+import android.view.View;
+import android.view.View.OnFocusChangeListener;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.LinearLayout;
+import android.util.AttributeSet;
+
+import com.android.systemui.R;
+
+import static android.media.session.PlaybackState.ACTION_PAUSE;
+import static android.media.session.PlaybackState.ACTION_PLAY;
+
+import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_PLAYING;
+import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_PAUSED;
+import static com.android.systemui.tv.pip.PipManager.PLAYBACK_STATE_UNAVAILABLE;
+
+
+/**
+ * A view containing PIP controls including fullscreen, close, and media controls.
+ */
+public class PipControlsView extends LinearLayout implements PipManager.Listener {
+    /**
+     * An interface to listen user action.
+     */
+    public interface Listener {
+        /**
+         * Called when an user clicks close PIP button.
+         */
+        void onClosed();
+    }
+
+    private final PipManager mPipManager = PipManager.getInstance();
+    private MediaController mMediaController;
+    private Listener mListener;
+
+    private View mFullButtonView;
+    private View mFullDescriptionView;
+    private View mPlayPauseView;
+    private ImageView mPlayPauseButtonImageView;
+    private TextView mPlayPauseDescriptionTextView;
+    private View mCloseButtonView;
+    private View mCloseDescriptionView;
+
+    private boolean mHasFocus;
+    private OnFocusChangeListener mOnChildFocusChangeListener;
+
+    private MediaController.Callback mMediaControllerCallback = new MediaController.Callback() {
+        @Override
+        public void onPlaybackStateChanged(PlaybackState state) {
+            updatePlayPauseView();
+        }
+    };
+
+    public PipControlsView(Context context) {
+        this(context, null, 0, 0);
+    }
+
+    public PipControlsView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0, 0);
+    }
+
+    public PipControlsView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public PipControlsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    public void onFinishInflate() {
+        super.onFinishInflate();
+
+        mFullButtonView = findViewById(R.id.full_button);
+        mFullDescriptionView = findViewById(R.id.full_desc);
+        mFullButtonView.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mPipManager.movePipToFullscreen();
+            }
+        });
+        mFullButtonView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+            @Override
+            public void onFocusChange(View v, boolean hasFocus) {
+                mFullDescriptionView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
+                onChildViewFocusChanged();
+            }
+        });
+
+        mPlayPauseView = findViewById(R.id.play_pause);
+        mPlayPauseButtonImageView = (ImageView) findViewById(R.id.play_pause_button);
+        mPlayPauseDescriptionTextView = (TextView) findViewById(R.id.play_pause_desc);
+        mPlayPauseButtonImageView.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (mMediaController == null || mMediaController.getPlaybackState() == null) {
+                    return;
+                }
+                long actions = mMediaController.getPlaybackState().getActions();
+                int state = mMediaController.getPlaybackState().getState();
+                if (mPipManager.getPlaybackState() == PLAYBACK_STATE_PAUSED) {
+                    mMediaController.getTransportControls().play();
+                } else if (mPipManager.getPlaybackState() == PLAYBACK_STATE_PLAYING) {
+                    mMediaController.getTransportControls().pause();
+                }
+                // View will be updated later in {@link mMediaControllerCallback}
+            }
+        });
+        mPlayPauseButtonImageView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+            @Override
+            public void onFocusChange(View v, boolean hasFocus) {
+                mPlayPauseDescriptionTextView.setVisibility(
+                        hasFocus ? View.VISIBLE : View.INVISIBLE);
+                onChildViewFocusChanged();
+            }
+        });
+
+        mCloseButtonView = findViewById(R.id.close_button);
+        mCloseDescriptionView = findViewById(R.id.close_desc);
+        mCloseButtonView.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mPipManager.closePip();
+                mListener.onClosed();
+            }
+        });
+        mCloseButtonView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+            @Override
+            public void onFocusChange(View v, boolean hasFocus) {
+                mCloseDescriptionView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
+                onChildViewFocusChanged();
+            }
+        });
+    }
+
+    @Override
+    public void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        updateMediaController();
+        mPipManager.addListener(this);
+    }
+
+    @Override
+    public void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mPipManager.removeListener(this);
+        if (mMediaController != null) {
+            mMediaController.unregisterCallback(mMediaControllerCallback);
+        }
+    }
+
+    private void updateMediaController() {
+        MediaController newController = mPipManager.getMediaController();
+        if (mMediaController == newController) {
+            return;
+        }
+        if (mMediaController != null) {
+            mMediaController.unregisterCallback(mMediaControllerCallback);
+        }
+        mMediaController = newController;
+        if (mMediaController != null) {
+            mMediaController.registerCallback(mMediaControllerCallback);
+        }
+        updatePlayPauseView();
+    }
+
+    private void updatePlayPauseView() {
+        int state = mPipManager.getPlaybackState();
+        if (state == PLAYBACK_STATE_UNAVAILABLE) {
+            mPlayPauseView.setVisibility(View.GONE);
+        } else {
+            mPlayPauseView.setVisibility(View.VISIBLE);
+            if (state == PLAYBACK_STATE_PLAYING) {
+                mPlayPauseButtonImageView.setImageResource(R.drawable.tv_pip_pause_button);
+                mPlayPauseDescriptionTextView.setText(R.string.pip_pause);
+            } else {
+                mPlayPauseButtonImageView.setImageResource(R.drawable.tv_pip_play_button);
+                mPlayPauseDescriptionTextView.setText(R.string.pip_play);
+            }
+        }
+    }
+
+    /**
+     * Sets a listener to be invoked when {@link android.view.View.hasFocus()} is changed.
+     */
+    public void setOnChildFocusChangeListener(OnFocusChangeListener listener) {
+        mOnChildFocusChangeListener = listener;
+    }
+
+    private void onChildViewFocusChanged() {
+        // At this moment, hasFocus() returns true although there's no focused child.
+        boolean hasFocus = (mFullButtonView != null && mFullButtonView.isFocused())
+                || (mPlayPauseButtonImageView != null && mPlayPauseButtonImageView.isFocused())
+                || (mCloseButtonView != null && mCloseButtonView.isFocused());
+        if (mHasFocus != hasFocus) {
+            mHasFocus = hasFocus;
+            if (mOnChildFocusChangeListener != null) {
+                mOnChildFocusChangeListener.onFocusChange(getFocusedChild(), mHasFocus);
+            }
+        }
+    }
+
+    /**
+     * Sets the {@link Listener} to listen user actions.
+     */
+    public void setListener(Listener listener) {
+        mListener = listener;
+    }
+
+    @Override
+    public void onPipEntered() { }
+
+    @Override
+    public void onPipActivityClosed() { }
+
+    @Override
+    public void onShowPipMenu() { }
+
+    @Override
+    public void onMoveToFullscreen() { }
+
+    @Override
+    public void onMediaControllerChanged() {
+        updateMediaController();
+    }
+
+    @Override
+    public void onPipResizeAboutToStart() { }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
index acb1a7f..68e0883 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
@@ -30,6 +30,7 @@
 import android.graphics.Rect;
 import android.media.session.MediaController;
 import android.media.session.MediaSessionManager;
+import android.media.session.PlaybackState;
 import android.os.Debug;
 import android.os.Handler;
 import android.os.RemoteException;
@@ -37,6 +38,7 @@
 import android.util.Log;
 
 import com.android.systemui.Prefs;
+import com.android.systemui.R;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
 
@@ -70,12 +72,23 @@
     public static final int SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH = 0x1;
     public static final int SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_OVERLAY_ACTIVITY_FINISH = 0x2;
 
+    /**
+     * PIPed activity is playing a media and it can be paused.
+     */
+    static final int PLAYBACK_STATE_PLAYING = 0;
+    /**
+     * PIPed activity has a paused media and it can be played.
+     */
+    static final int PLAYBACK_STATE_PAUSED = 1;
+    /**
+     * Users are unable to control PIPed activity's media playback.
+     */
+    static final int PLAYBACK_STATE_UNAVAILABLE = 2;
+
     private static final int CLOSE_PIP_WHEN_MEDIA_SESSION_GONE_TIMEOUT_MS = 3000;
 
     private int mSuspendPipResizingReason;
 
-    private static final float SCALE_FACTOR = 1.1f;
-
     private Context mContext;
     private IActivityManager mActivityManager;
     private MediaSessionManager mMediaSessionManager;
@@ -87,6 +100,7 @@
     private Rect mMenuModePipBounds;
     private Rect mRecentsPipBounds;
     private Rect mRecentsFocusedPipBounds;
+    private int mRecentsFocusChangedAnimationDurationMs;
     private boolean mInitialized;
     private int mPipTaskId = TASK_ID_NO_PIP;
     private ComponentName mPipComponentName;
@@ -148,15 +162,13 @@
         mPipBounds = Rect.unflattenFromString(res.getString(
                 com.android.internal.R.string.config_defaultPictureInPictureBounds));
         mMenuModePipBounds = Rect.unflattenFromString(res.getString(
-                com.android.internal.R.string.config_centeredPictureInPictureBounds));
+                R.string.pip_menu_bounds));
         mRecentsPipBounds = Rect.unflattenFromString(res.getString(
-                com.android.internal.R.string.config_pictureInPictureBoundsInRecents));
-        float scaleBy = (SCALE_FACTOR - 1.0f) / 2;
-        mRecentsFocusedPipBounds = new Rect(
-                (int) (mRecentsPipBounds.left - scaleBy * mRecentsPipBounds.width()),
-                (int) (mRecentsPipBounds.top - scaleBy * mRecentsPipBounds.height()),
-                (int) (mRecentsPipBounds.right + scaleBy * mRecentsPipBounds.width()),
-                (int) (mRecentsPipBounds.bottom + scaleBy * mRecentsPipBounds.height()));
+                R.string.pip_recents_bounds));
+        mRecentsFocusedPipBounds = Rect.unflattenFromString(res.getString(
+                R.string.pip_recents_focused_bounds));
+        mRecentsFocusChangedAnimationDurationMs = res.getInteger(
+                R.integer.recents_tv_pip_focus_anim_duration);
 
         mActivityManager = ActivityManagerNative.getDefault();
         SystemServicesProxy.getInstance(context).registerTaskStackListener(mTaskStackListener);
@@ -279,6 +291,7 @@
                             mSuspendPipResizingReason);
             return;
         }
+        int animationDurationMs = -1;
         switch (mState) {
             case STATE_NO_PIP:
                 mCurrentPipBounds = null;
@@ -288,6 +301,10 @@
                 break;
             case STATE_PIP_OVERLAY:
                 if (mIsRecentsShown) {
+                    if (mCurrentPipBounds == mRecentsFocusedPipBounds
+                            || mCurrentPipBounds == mRecentsFocusedPipBounds) {
+                        animationDurationMs = mRecentsFocusChangedAnimationDurationMs;
+                    }
                     if (mIsPipFocusedInRecent) {
                         mCurrentPipBounds = mRecentsFocusedPipBounds;
                     } else {
@@ -302,7 +319,8 @@
                 break;
         }
         try {
-            mActivityManager.resizeStack(PINNED_STACK_ID, mCurrentPipBounds, true, true, true, -1);
+            mActivityManager.resizeStack(PINNED_STACK_ID, mCurrentPipBounds,
+                    true, true, true, animationDurationMs);
         } catch (RemoteException e) {
             Log.e(TAG, "resizeStack failed", e);
         }
@@ -364,9 +382,17 @@
     }
 
     /**
+     * Returns {@code true} if the PIP view in
+     * {@link com.android.systemui.recents.tv.RecentsTvActivity} is focused in Recents.
+     * This API is valid only when {@link isRecentsShown()} returns {@code true}.
+     */
+    boolean isPipViewFocusdInRecents() {
+        return mIsPipFocusedInRecent;
+    }
+
+    /**
      * Shows PIP menu UI by launching {@link PipMenuActivity}. It also locates the pinned
-     * stack to the centered PIP bound {@link com.android.internal.R.string
-     * .config_centeredPictureInPictureBounds}.
+     * stack to the centered PIP bound {@link R.config_centeredPictureInPictureBounds}.
      */
     private void showPipMenu() {
         if (DEBUG) Log.d(TAG, "showPipMenu()");
@@ -478,6 +504,32 @@
         return mPipMediaController;
     }
 
+    /**
+     * Returns the PIPed activity's playback state.
+     * This returns one of {@link PLAYBACK_STATE_PLAYING}, {@link PLAYBACK_STATE_PAUSED},
+     * or {@link PLAYBACK_STATE_UNAVAILABLE}.
+     */
+    int getPlaybackState() {
+        if (mPipMediaController == null || mPipMediaController.getPlaybackState() == null) {
+            return PLAYBACK_STATE_UNAVAILABLE;
+        }
+        int state = mPipMediaController.getPlaybackState().getState();
+        boolean isPlaying = (state == PlaybackState.STATE_BUFFERING
+                || state == PlaybackState.STATE_CONNECTING
+                || state == PlaybackState.STATE_PLAYING
+                || state == PlaybackState.STATE_FAST_FORWARDING
+                || state == PlaybackState.STATE_REWINDING
+                || state == PlaybackState.STATE_SKIPPING_TO_PREVIOUS
+                || state == PlaybackState.STATE_SKIPPING_TO_NEXT);
+        long actions = mPipMediaController.getPlaybackState().getActions();
+        if (!isPlaying && ((actions & PlaybackState.ACTION_PLAY) != 0)) {
+            return PLAYBACK_STATE_PAUSED;
+        } else if (isPlaying && ((actions & PlaybackState.ACTION_PAUSE) != 0)) {
+            return PLAYBACK_STATE_PLAYING;
+        }
+        return PLAYBACK_STATE_UNAVAILABLE;
+    }
+
     TaskStackListener mTaskStackListener = new TaskStackListener() {
         @Override
         public void onTaskStackChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
index b8b837a..ea9275f 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
@@ -17,12 +17,7 @@
 package com.android.systemui.tv.pip;
 
 import android.app.Activity;
-import android.media.session.MediaController;
-import android.media.session.PlaybackState;
 import android.os.Bundle;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
 
 import com.android.systemui.R;
 import com.android.systemui.SystemUI;
@@ -31,8 +26,6 @@
 
 import static android.content.pm.PackageManager.FEATURE_LEANBACK;
 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
-import static android.media.session.PlaybackState.ACTION_PAUSE;
-import static android.media.session.PlaybackState.ACTION_PLAY;
 
 /**
  * Activity to show the PIP menu to control PIP.
@@ -41,131 +34,17 @@
     private static final String TAG = "PipMenuActivity";
 
     private final PipManager mPipManager = PipManager.getInstance();
-    private MediaController mMediaController;
 
-    private View mFullButtonView;
-    private View mFullDescriptionView;
-    private View mPlayPauseView;
-    private ImageView mPlayPauseButtonImageView;
-    private TextView mPlayPauseDescriptionTextView;
-    private View mCloseButtonView;
-    private View mCloseDescriptionView;
+    private PipControlsView mPipControlsView;
     private boolean mPipMovedToFullscreen;
 
-    private MediaController.Callback mMediaControllerCallback = new MediaController.Callback() {
-        @Override
-        public void onPlaybackStateChanged(PlaybackState state) {
-            updatePlayPauseView(state);
-        }
-    };
-
     @Override
     protected void onCreate(Bundle bundle) {
         super.onCreate(bundle);
         setContentView(R.layout.tv_pip_menu);
         mPipManager.addListener(this);
-        mFullButtonView = findViewById(R.id.full_button);
-        mFullDescriptionView = findViewById(R.id.full_desc);
-        mFullButtonView.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                mPipManager.movePipToFullscreen();
-                mPipMovedToFullscreen = true;
-                finish();
-            }
-        });
-        mFullButtonView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
-            @Override
-            public void onFocusChange(View v, boolean hasFocus) {
-                mFullDescriptionView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
-            }
-        });
 
-        mPlayPauseView = findViewById(R.id.play_pause);
-        mPlayPauseButtonImageView = (ImageView) findViewById(R.id.play_pause_button);
-        mPlayPauseDescriptionTextView = (TextView) findViewById(R.id.play_pause_desc);
-        mPlayPauseButtonImageView.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                if (mMediaController == null || mMediaController.getPlaybackState() == null) {
-                    return;
-                }
-                long actions = mMediaController.getPlaybackState().getActions();
-                int state = mMediaController.getPlaybackState().getState();
-                if (((actions & ACTION_PLAY) != 0) && !isPlaying(state)) {
-                    mMediaController.getTransportControls().play();
-                } else if ((actions & ACTION_PAUSE) != 0 && isPlaying(state)) {
-                    mMediaController.getTransportControls().pause();
-                }
-                // View will be updated later in {@link mMediaControllerCallback}
-            }
-        });
-        mPlayPauseButtonImageView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
-            @Override
-            public void onFocusChange(View v, boolean hasFocus) {
-                mPlayPauseDescriptionTextView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
-            }
-        });
-
-        mCloseButtonView = findViewById(R.id.close_button);
-        mCloseDescriptionView = findViewById(R.id.close_desc);
-        mCloseButtonView.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                mPipManager.closePip();
-                finish();
-            }
-        });
-        mCloseButtonView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
-            @Override
-            public void onFocusChange(View v, boolean hasFocus) {
-                mCloseDescriptionView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
-            }
-        });
-        updateMediaController();
-    }
-
-    private void updateMediaController() {
-        MediaController newController = mPipManager.getMediaController();
-        if (mMediaController == newController) {
-            return;
-        }
-        if (mMediaController != null) {
-            mMediaController.unregisterCallback(mMediaControllerCallback);
-        }
-        mMediaController = newController;
-        if (mMediaController != null) {
-            mMediaController.registerCallback(mMediaControllerCallback);
-            updatePlayPauseView(mMediaController.getPlaybackState());
-        } else {
-            updatePlayPauseView(null);
-        }
-    }
-
-    private void updatePlayPauseView(PlaybackState playbackState) {
-        if (playbackState != null
-                && (playbackState.getActions() & (ACTION_PLAY | ACTION_PAUSE)) != 0) {
-            mPlayPauseView.setVisibility(View.VISIBLE);
-            if (isPlaying(playbackState.getState())) {
-                mPlayPauseButtonImageView.setImageResource(R.drawable.tv_pip_pause_button);
-                mPlayPauseDescriptionTextView.setText(R.string.pip_pause);
-            } else {
-                mPlayPauseButtonImageView.setImageResource(R.drawable.tv_pip_play_button);
-                mPlayPauseDescriptionTextView.setText(R.string.pip_play);
-            }
-        } else {
-            mPlayPauseView.setVisibility(View.GONE);
-        }
-    }
-
-    private boolean isPlaying(int state) {
-        return state == PlaybackState.STATE_BUFFERING
-                || state == PlaybackState.STATE_CONNECTING
-                || state == PlaybackState.STATE_PLAYING
-                || state == PlaybackState.STATE_FAST_FORWARDING
-                || state == PlaybackState.STATE_REWINDING
-                || state == PlaybackState.STATE_SKIPPING_TO_PREVIOUS
-                || state == PlaybackState.STATE_SKIPPING_TO_NEXT;
+        mPipControlsView = (PipControlsView) findViewById(R.id.pip_controls);
     }
 
     private void restorePipAndFinish() {
@@ -184,9 +63,6 @@
     @Override
     protected void onDestroy() {
         super.onDestroy();
-        if (mMediaController != null) {
-            mMediaController.unregisterCallback(mMediaControllerCallback);
-        }
         mPipManager.removeListener(this);
         mPipManager.resumePipResizing(
                 PipManager.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH);
@@ -210,13 +86,12 @@
 
     @Override
     public void onMoveToFullscreen() {
+        mPipMovedToFullscreen = true;
         finish();
     }
 
     @Override
-    public void onMediaControllerChanged() {
-        updateMediaController();
-    }
+    public void onMediaControllerChanged() { }
 
     @Override
     public void onPipResizeAboutToStart() {
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
index 1de321d..12cb4cd 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
@@ -23,6 +23,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.view.View;
+import android.widget.ImageView;
 
 import com.android.systemui.R;
 
@@ -46,9 +47,10 @@
     private final Handler mHandler = new Handler();
     private View mGuideOverlayView;
     private View mGuideButtonsView;
+    private ImageView mGuideButtonPlayPauseImageView;
     private final Runnable mHideGuideOverlayRunnable = new Runnable() {
         public void run() {
-            mGuideOverlayView.setVisibility(View.INVISIBLE);
+            mGuideOverlayView.setVisibility(View.GONE);
         }
     };
 
@@ -71,6 +73,7 @@
         setContentView(R.layout.tv_pip_overlay);
         mGuideOverlayView = findViewById(R.id.guide_overlay);
         mGuideButtonsView = findViewById(R.id.guide_buttons);
+        mGuideButtonPlayPauseImageView = (ImageView) findViewById(R.id.guide_button_play_pause);
         mPipManager.addListener(this);
 
         sPipOverlayActivity = this;
@@ -80,12 +83,17 @@
     protected void onResume() {
         super.onResume();
         // TODO: Implement animation for this
-        if (!mPipManager.isRecentsShown()) {
-            mGuideOverlayView.setVisibility(View.VISIBLE);
-            mGuideButtonsView.setVisibility(View.INVISIBLE);
+        if (mPipManager.isRecentsShown()) {
+            mGuideOverlayView.setVisibility(View.GONE);
+            if (mPipManager.isPipViewFocusdInRecents()) {
+                mGuideButtonsView.setVisibility(View.GONE);
+            } else {
+                mGuideButtonsView.setVisibility(View.VISIBLE);
+                updateGuideButtonsView();
+            }
         } else {
-            mGuideOverlayView.setVisibility(View.INVISIBLE);
-            mGuideButtonsView.setVisibility(View.VISIBLE);
+            mGuideOverlayView.setVisibility(View.VISIBLE);
+            mGuideButtonsView.setVisibility(View.GONE);
         }
         mHandler.removeCallbacks(mHideGuideOverlayRunnable);
         mHandler.postDelayed(mHideGuideOverlayRunnable, SHOW_GUIDE_OVERLAY_VIEW_DURATION_MS);
@@ -134,11 +142,30 @@
     }
 
     @Override
-    public void onMediaControllerChanged() { }
+    public void onMediaControllerChanged() {
+        updateGuideButtonsView();
+    }
 
     @Override
     public void finish() {
         sPipOverlayActivity = null;
         super.finish();
     }
+
+    private void updateGuideButtonsView() {
+        switch (mPipManager.getPlaybackState()) {
+            case PipManager.PLAYBACK_STATE_PLAYING:
+                mGuideButtonPlayPauseImageView.setVisibility(View.VISIBLE);
+                mGuideButtonPlayPauseImageView.setImageResource(R.drawable.ic_pause_white_24dp);
+                break;
+            case PipManager.PLAYBACK_STATE_PAUSED:
+                mGuideButtonPlayPauseImageView.setVisibility(View.VISIBLE);
+                mGuideButtonPlayPauseImageView.setImageResource(
+                        R.drawable.ic_play_arrow_white_24dp);
+                break;
+            case PipManager.PLAYBACK_STATE_UNAVAILABLE:
+                mGuideButtonPlayPauseImageView.setVisibility(View.GONE);
+                break;
+        }
+    }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index e710ded..e3dac28 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1812,6 +1812,87 @@
         return mKeyEventDispatcher;
     }
 
+    /**
+     * Enables accessibility service specified by {@param componentName} for the {@param userId}.
+     */
+    public void enableAccessibilityService(ComponentName componentName, int userId) {
+        synchronized(mLock) {
+            if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+                throw new SecurityException("only SYSTEM can call enableAccessibilityService.");
+            }
+
+            SettingsStringHelper settingsHelper = new SettingsStringHelper(
+                    Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, userId);
+            settingsHelper.addService(componentName);
+            settingsHelper.writeToSettings();
+
+            UserState userState = getUserStateLocked(userId);
+            if (userState.mEnabledServices.add(componentName)) {
+                onUserStateChangedLocked(userState);
+            }
+        }
+    }
+
+    /**
+     * Disables accessibility service specified by {@param componentName} for the {@param userId}.
+     */
+    public void disableAccessibilityService(ComponentName componentName, int userId) {
+        synchronized(mLock) {
+            if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+                throw new SecurityException("only SYSTEM can call disableAccessibility");
+            }
+
+            SettingsStringHelper settingsHelper = new SettingsStringHelper(
+                    Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, userId);
+            settingsHelper.deleteService(componentName);
+            settingsHelper.writeToSettings();
+
+            UserState userState = getUserStateLocked(userId);
+            if (userState.mEnabledServices.remove(componentName)) {
+                onUserStateChangedLocked(userState);
+            }
+        }
+    }
+
+    private class SettingsStringHelper {
+        private static final String SETTINGS_DELIMITER = ":";
+        private ContentResolver mContentResolver;
+        private final String mSettingsName;
+        private Set<String> mServices;
+        private final int mUserId;
+
+        public SettingsStringHelper(String name, int userId) {
+            mUserId = userId;
+            mSettingsName = name;
+            mContentResolver = mContext.getContentResolver();
+            String servicesString = Settings.Secure.getStringForUser(
+                    mContentResolver, mSettingsName, userId);
+            mServices = new HashSet();
+            if (!TextUtils.isEmpty(servicesString)) {
+                final TextUtils.SimpleStringSplitter colonSplitter =
+                        new TextUtils.SimpleStringSplitter(SETTINGS_DELIMITER.charAt(0));
+                colonSplitter.setString(servicesString);
+                while (colonSplitter.hasNext()) {
+                    final String serviceName = colonSplitter.next();
+                    mServices.add(serviceName);
+                }
+            }
+        }
+
+        public void addService(ComponentName component) {
+            mServices.add(component.flattenToString());
+        }
+
+        public void deleteService(ComponentName component) {
+            mServices.remove(component.flattenToString());
+        }
+
+        public void writeToSettings() {
+            Settings.Secure.putStringForUser(mContentResolver, mSettingsName,
+                    TextUtils.join(SETTINGS_DELIMITER, mServices), mUserId);
+        }
+    }
+
     @Override
     public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
         mSecurityPolicy.enforceCallingPermission(Manifest.permission.DUMP, FUNCTION_DUMP);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index b1398f1..95d3cc3 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -71,6 +71,7 @@
 import android.net.RouteInfo;
 import android.net.UidRange;
 import android.net.Uri;
+import android.net.metrics.ConnectivityServiceChangeEvent;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -125,7 +126,6 @@
 import com.android.server.connectivity.NetworkMonitor;
 import com.android.server.connectivity.PacManager;
 import com.android.server.connectivity.PermissionMonitor;
-import com.android.server.connectivity.ApfFilter;
 import com.android.server.connectivity.Tethering;
 import com.android.server.connectivity.Vpn;
 import com.android.server.net.BaseNetworkObserver;
@@ -354,13 +354,6 @@
      */
     private static final int EVENT_REGISTER_NETWORK_LISTENER_WITH_INTENT = 31;
 
-    /**
-     * used to push APF program to NetworkAgent
-     * replyTo = NetworkAgent message handler
-     * obj = byte[] of APF program
-     */
-    private static final int EVENT_PUSH_APF_PROGRAM_TO_NETWORK = 32;
-
     /** Handler thread used for both of the handlers below. */
     @VisibleForTesting
     protected final HandlerThread mHandlerThread;
@@ -1802,20 +1795,6 @@
         }
     }
 
-    private void dumpApf(IndentingPrintWriter pw) {
-        pw.println("APF filters:");
-        pw.increaseIndent();
-        for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
-            if (nai.apfFilter != null) {
-                pw.println(nai.name() + ":");
-                pw.increaseIndent();
-                nai.apfFilter.dump(pw);
-                pw.decreaseIndent();
-            }
-        }
-        pw.decreaseIndent();
-    }
-
     @Override
     protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
@@ -1833,11 +1812,6 @@
             return;
         }
 
-        if (argsContain(args, "apf")) {
-            dumpApf(pw);
-            return;
-        }
-
         pw.print("NetworkFactories for:");
         for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
             pw.print(" " + nfi.name);
@@ -1901,7 +1875,6 @@
         mKeepaliveTracker.dump(pw);
 
         pw.println();
-        dumpApf(pw);
 
         if (mInetLog != null && mInetLog.size() > 0) {
             pw.println();
@@ -2223,7 +2196,6 @@
             mKeepaliveTracker.handleStopAllKeepalives(nai,
                     ConnectivityManager.PacketKeepalive.ERROR_INVALID_NETWORK);
             nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED);
-            if (nai.apfFilter != null) nai.apfFilter.shutdown();
             mNetworkAgentInfos.remove(msg.replyTo);
             updateClat(null, nai.linkProperties, nai);
             synchronized (mNetworkForNetId) {
@@ -2438,13 +2410,6 @@
                 accept ? 1 : 0, always ? 1: 0, network));
     }
 
-    public void pushApfProgramToNetwork(NetworkAgentInfo nai, byte[] program) {
-        enforceConnectivityInternalPermission();
-        Message msg = mHandler.obtainMessage(EVENT_PUSH_APF_PROGRAM_TO_NETWORK, program);
-        msg.replyTo = nai.messenger;
-        mHandler.sendMessage(msg);
-    }
-
     private void handleSetAcceptUnvalidated(Network network, boolean accept, boolean always) {
         if (DBG) log("handleSetAcceptUnvalidated network=" + network +
                 " accept=" + accept + " always=" + always);
@@ -2594,16 +2559,6 @@
                     handleMobileDataAlwaysOn();
                     break;
                 }
-                case EVENT_PUSH_APF_PROGRAM_TO_NETWORK: {
-                    NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
-                    if (nai == null) {
-                        loge("EVENT_PUSH_APF_PROGRAM_TO_NETWORK from unknown NetworkAgent");
-                    } else {
-                         nai.asyncChannel.sendMessage(NetworkAgent.CMD_PUSH_APF_PROGRAM,
-                                 (byte[]) msg.obj);
-                    }
-                    break;
-                }
                 // Sent by KeepaliveTracker to process an app request on the state machine thread.
                 case NetworkAgent.CMD_START_PACKET_KEEPALIVE: {
                     mKeepaliveTracker.handleStartKeepalive(msg);
@@ -4185,9 +4140,6 @@
         if (networkAgent.clatd != null) {
             networkAgent.clatd.fixupLinkProperties(oldLp);
         }
-        if (networkAgent.apfFilter != null) {
-            networkAgent.apfFilter.updateFilter();
-        }
 
         updateInterfaces(newLp, oldLp, netId);
         updateMtu(newLp, oldLp);
@@ -4511,6 +4463,7 @@
 
     private void makeDefault(NetworkAgentInfo newNetwork) {
         if (DBG) log("Switching to new default network: " + newNetwork);
+        ConnectivityServiceChangeEvent.logEvent(newNetwork.network.netId);
         setupDataActivityTracking(newNetwork);
         try {
             mNetd.setDefaultNetId(newNetwork.network.netId);
@@ -5141,5 +5094,4 @@
             NetworkAgentInfo nai, NetworkRequest defaultRequest) {
         return new NetworkMonitor(context, handler, nai, defaultRequest);
     }
-
 }
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index b57e14e..4749417 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -684,13 +684,18 @@
         public long SMS_TEMP_APP_WHITELIST_DURATION;
 
         private final ContentResolver mResolver;
+        private final boolean mHasWatch;
         private final KeyValueListParser mParser = new KeyValueListParser(',');
 
         public Constants(Handler handler, ContentResolver resolver) {
             super(handler);
             mResolver = resolver;
-            mResolver.registerContentObserver(
-                    Settings.Global.getUriFor(Settings.Global.DEVICE_IDLE_CONSTANTS), false, this);
+            mHasWatch = getContext().getPackageManager().hasSystemFeature(
+                    PackageManager.FEATURE_WATCH);
+            mResolver.registerContentObserver(Settings.Global.getUriFor(
+                    mHasWatch ? Settings.Global.DEVICE_IDLE_CONSTANTS_WATCH
+                              : Settings.Global.DEVICE_IDLE_CONSTANTS),
+                    false, this);
             updateConstants();
         }
 
@@ -703,7 +708,8 @@
             synchronized (DeviceIdleController.this) {
                 try {
                     mParser.setString(Settings.Global.getString(mResolver,
-                            Settings.Global.DEVICE_IDLE_CONSTANTS));
+                            mHasWatch ? Settings.Global.DEVICE_IDLE_CONSTANTS_WATCH
+                                      : Settings.Global.DEVICE_IDLE_CONSTANTS));
                 } catch (IllegalArgumentException e) {
                     // Failed to parse the settings string, log this and move on
                     // with defaults.
@@ -724,8 +730,9 @@
                 MIN_DEEP_MAINTENANCE_TIME = mParser.getLong(
                         KEY_MIN_DEEP_MAINTENANCE_TIME,
                         !COMPRESS_TIME ? 30 * 1000L : 5 * 1000L);
+                long inactiveTimeoutDefault = (mHasWatch ? 15 : 30) * 60 * 1000L;
                 INACTIVE_TIMEOUT = mParser.getLong(KEY_INACTIVE_TIMEOUT,
-                        !COMPRESS_TIME ? 30 * 60 * 1000L : 3 * 60 * 1000L);
+                        !COMPRESS_TIME ? inactiveTimeoutDefault : (inactiveTimeoutDefault / 10));
                 SENSING_TIMEOUT = mParser.getLong(KEY_SENSING_TIMEOUT,
                         !DEBUG ? 4 * 60 * 1000L : 60 * 1000L);
                 LOCATING_TIMEOUT = mParser.getLong(KEY_LOCATING_TIMEOUT,
@@ -733,8 +740,10 @@
                 LOCATION_ACCURACY = mParser.getFloat(KEY_LOCATION_ACCURACY, 20);
                 MOTION_INACTIVE_TIMEOUT = mParser.getLong(KEY_MOTION_INACTIVE_TIMEOUT,
                         !COMPRESS_TIME ? 10 * 60 * 1000L : 60 * 1000L);
+                long idleAfterInactiveTimeout = (mHasWatch ? 15 : 30) * 60 * 1000L;
                 IDLE_AFTER_INACTIVE_TIMEOUT = mParser.getLong(KEY_IDLE_AFTER_INACTIVE_TIMEOUT,
-                        !COMPRESS_TIME ? 30 * 60 * 1000L : 3 * 60 * 1000L);
+                        !COMPRESS_TIME ? idleAfterInactiveTimeout
+                                       : (idleAfterInactiveTimeout / 10));
                 IDLE_PENDING_TIMEOUT = mParser.getLong(KEY_IDLE_PENDING_TIMEOUT,
                         !COMPRESS_TIME ? 5 * 60 * 1000L : 30 * 1000L);
                 MAX_IDLE_PENDING_TIMEOUT = mParser.getLong(KEY_MAX_IDLE_PENDING_TIMEOUT,
diff --git a/services/core/java/com/android/server/GraphicsStatsService.java b/services/core/java/com/android/server/GraphicsStatsService.java
index 044bb04..e29515f 100644
--- a/services/core/java/com/android/server/GraphicsStatsService.java
+++ b/services/core/java/com/android/server/GraphicsStatsService.java
@@ -51,17 +51,15 @@
  * 2) ASHMEM_SIZE (for scratch space used during dumping)
  * 3) ASHMEM_SIZE * HISTORY_SIZE
  *
- * Currently ASHMEM_SIZE is 256 bytes and HISTORY_SIZE is 20. Assuming
- * the system then also has 10 active rendering processes in the worst case
- * this would end up using under 14KiB (12KiB for the buffers, plus some overhead
- * for userId, pid, package name, and a couple other objects)
+ * This is currently under 16KiB total memory in the worst case of
+ * 20 processes in history + 10 unique active processes.
  *
  *  @hide */
 public class GraphicsStatsService extends IGraphicsStats.Stub {
     public static final String GRAPHICS_STATS_SERVICE = "graphicsstats";
 
     private static final String TAG = "GraphicsStatsService";
-    private static final int ASHMEM_SIZE = 256;
+    private static final int ASHMEM_SIZE = 296;
     private static final int HISTORY_SIZE = 20;
 
     private final Context mContext;
diff --git a/services/core/java/com/android/server/HardwarePropertiesManagerService.java b/services/core/java/com/android/server/HardwarePropertiesManagerService.java
index 575d99e..23cf64a 100644
--- a/services/core/java/com/android/server/HardwarePropertiesManagerService.java
+++ b/services/core/java/com/android/server/HardwarePropertiesManagerService.java
@@ -23,6 +23,8 @@
 import android.os.CpuUsageInfo;
 import android.os.IHardwarePropertiesManager;
 import android.os.Process;
+import android.os.UserHandle;
+import com.android.server.vr.VrManagerInternal;
 
 import java.util.Arrays;
 
@@ -78,14 +80,15 @@
      *
      * @param callingPackage The calling package name.
      *
-     * @throws SecurityException if a non profile or device owner or system tries to retrieve
-     * information provided by the service.
+     * @throws SecurityException if something other than the profile or device owner, or the
+     *        current VR service tries to retrieve information provided by this service.
      */
     private void enforceHardwarePropertiesRetrievalAllowed(String callingPackage)
             throws SecurityException {
         final PackageManager pm = mContext.getPackageManager();
+        int uid = 0;
         try {
-            final int uid = pm.getPackageUid(callingPackage, 0);
+            uid = pm.getPackageUid(callingPackage, 0);
             if (Binder.getCallingUid() != uid) {
                 throw new SecurityException("The caller has faked the package name.");
             }
@@ -93,10 +96,13 @@
             throw new SecurityException("The caller has faked the package name.");
         }
 
+        final int userId = UserHandle.getUserId(uid);
+        final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
         final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
         if (!dpm.isDeviceOwnerApp(callingPackage) && !dpm.isProfileOwnerApp(callingPackage)
-                && Binder.getCallingUid() != Process.SYSTEM_UID) {
-            throw new SecurityException("The caller is not a device or profile owner or system.");
+                && !vrService.isCurrentVrListener(callingPackage, userId)) {
+            throw new SecurityException("The caller is not a device or profile owner or bound "
+                + "VrListenerService.");
         }
     }
 }
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 898d5b73..e042483 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -215,7 +215,7 @@
     // Ongoing notification
     private NotificationManager mNotificationManager;
     private KeyguardManager mKeyguardManager;
-    private StatusBarManagerService mStatusBar;
+    private @Nullable StatusBarManagerService mStatusBar;
     private Notification.Builder mImeSwitcherNotification;
     private PendingIntent mImeSwitchPendingIntent;
     private boolean mShowOngoingImeSwitcherForPhones;
@@ -1070,7 +1070,9 @@
                 mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
                 mNotificationManager = mContext.getSystemService(NotificationManager.class);
                 mStatusBar = statusBar;
-                statusBar.setIconVisibility(mSlotIme, false);
+                if (mStatusBar != null) {
+                    mStatusBar.setIconVisibility(mSlotIme, false);
+                }
                 updateSystemUiLocked(mCurToken, mImeWindowVis, mBackDisposition);
                 mShowOngoingImeSwitcherForPhones = mRes.getBoolean(
                         com.android.internal.R.bool.show_ongoing_ime_switcher);
diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java
index 43d10c7..3fdcceb 100644
--- a/services/core/java/com/android/server/IntentResolver.java
+++ b/services/core/java/com/android/server/IntentResolver.java
@@ -779,11 +779,11 @@
             }
         }
 
-        if (hasNonDefaults) {
+        if (debug && hasNonDefaults) {
             if (dest.size() == 0) {
-                Slog.w(TAG, "resolveIntent failed: found match, but none with CATEGORY_DEFAULT");
+                Slog.v(TAG, "resolveIntent failed: found match, but none with CATEGORY_DEFAULT");
             } else if (dest.size() > 1) {
-                Slog.w(TAG, "resolveIntent: multiple matches, only some with CATEGORY_DEFAULT");
+                Slog.v(TAG, "resolveIntent: multiple matches, only some with CATEGORY_DEFAULT");
             }
         }
     }
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 548b662..c6d536d 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -209,9 +209,12 @@
     /** Set of interfaces with active alerts. */
     @GuardedBy("mQuotaLock")
     private HashMap<String, Long> mActiveAlerts = Maps.newHashMap();
-    /** Set of UIDs with active reject rules. */
+    /** Set of UIDs blacklisted on metered networks. */
     @GuardedBy("mQuotaLock")
-    private SparseBooleanArray mUidRejectOnQuota = new SparseBooleanArray();
+    private SparseBooleanArray mUidRejectOnMetered = new SparseBooleanArray();
+    /** Set of UIDs whitelisted on metered networks. */
+    @GuardedBy("mQuotaLock")
+    private SparseBooleanArray mUidAllowOnMetered = new SparseBooleanArray();
     /** Set of UIDs with cleartext penalties. */
     @GuardedBy("mQuotaLock")
     private SparseIntArray mUidCleartextPolicy = new SparseIntArray();
@@ -240,6 +243,9 @@
     @GuardedBy("mQuotaLock")
     final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray();
 
+    @GuardedBy("mQuotaLock")
+    private boolean mDataSaverMode;
+
     private Object mIdleTimerLock = new Object();
     /** Set of interfaces with active idle timers. */
     private static class IdleTimerParams {
@@ -513,6 +519,28 @@
         }
     }
 
+    // Sync the state of the given chain with the native daemon.
+    private void syncFirewallChainLocked(int chain, SparseIntArray uidFirewallRules, String name) {
+        int size = uidFirewallRules.size();
+        if (size > 0) {
+            // Make a copy of the current rules, and then clear them. This is because
+            // setFirewallUidRuleInternal only pushes down rules to the native daemon if they are
+            // different from the current rules stored in the mUidFirewall*Rules array for the
+            // specified chain. If we don't clear the rules, setFirewallUidRuleInternal will do
+            // nothing.
+            final SparseIntArray rules = uidFirewallRules.clone();
+            uidFirewallRules.clear();
+
+            // Now push the rules. setFirewallUidRuleInternal will push each of these down to the
+            // native daemon, and also add them to the mUidFirewall*Rules array for the specified
+            // chain.
+            if (DBG) Slog.d(TAG, "Pushing " + size + " active firewall " + name + "UID rules");
+            for (int i = 0; i < rules.size(); i++) {
+                setFirewallUidRuleInternal(chain, rules.keyAt(i), rules.valueAt(i));
+            }
+        }
+    }
+
     /**
      * Prepare native daemon once connected, enabling modules and pushing any
      * existing in-memory rules.
@@ -561,6 +589,9 @@
 
         // push any existing quota or UID rules
         synchronized (mQuotaLock) {
+
+            setDataSaverModeEnabled(mDataSaverMode);
+
             int size = mActiveQuotas.size();
             if (size > 0) {
                 if (DBG) Slog.d(TAG, "Pushing " + size + " active quota rules");
@@ -581,13 +612,25 @@
                 }
             }
 
-            size = mUidRejectOnQuota.size();
+            size = mUidRejectOnMetered.size();
             if (size > 0) {
-                if (DBG) Slog.d(TAG, "Pushing " + size + " active UID rules");
-                final SparseBooleanArray uidRejectOnQuota = mUidRejectOnQuota;
-                mUidRejectOnQuota = new SparseBooleanArray();
+                if (DBG) Slog.d(TAG, "Pushing " + size + " UIDs to metered whitelist rules");
+                final SparseBooleanArray uidRejectOnQuota = mUidRejectOnMetered;
+                mUidRejectOnMetered = new SparseBooleanArray();
                 for (int i = 0; i < uidRejectOnQuota.size(); i++) {
-                    setUidNetworkRules(uidRejectOnQuota.keyAt(i), uidRejectOnQuota.valueAt(i));
+                    setUidMeteredNetworkBlacklist(uidRejectOnQuota.keyAt(i),
+                            uidRejectOnQuota.valueAt(i));
+                }
+            }
+
+            size = mUidAllowOnMetered.size();
+            if (size > 0) {
+                if (DBG) Slog.d(TAG, "Pushing " + size + " UIDs to metered blacklist rules");
+                final SparseBooleanArray uidAcceptOnQuota = mUidAllowOnMetered;
+                mUidAllowOnMetered = new SparseBooleanArray();
+                for (int i = 0; i < uidAcceptOnQuota.size(); i++) {
+                    setUidMeteredNetworkWhitelist(uidAcceptOnQuota.keyAt(i),
+                            uidAcceptOnQuota.valueAt(i));
                 }
             }
 
@@ -603,55 +646,18 @@
 
             setFirewallEnabled(mFirewallEnabled || LockdownVpnTracker.isEnabled());
 
-            size = mUidFirewallRules.size();
-            if (size > 0) {
-                if (DBG) Slog.d(TAG, "Pushing " + size + " active firewall UID rules");
-                final SparseIntArray uidFirewallRules = mUidFirewallRules;
-                mUidFirewallRules = new SparseIntArray();
-                for (int i = 0; i < uidFirewallRules.size(); i++) {
-                    setFirewallUidRuleInternal(FIREWALL_CHAIN_NONE, uidFirewallRules.keyAt(i),
-                            uidFirewallRules.valueAt(i));
-                }
-            }
+            syncFirewallChainLocked(FIREWALL_CHAIN_NONE, mUidFirewallRules, "");
+            syncFirewallChainLocked(FIREWALL_CHAIN_STANDBY, mUidFirewallStandbyRules, "standby ");
+            syncFirewallChainLocked(FIREWALL_CHAIN_DOZABLE, mUidFirewallDozableRules, "dozable ");
+            syncFirewallChainLocked(FIREWALL_CHAIN_POWERSAVE, mUidFirewallPowerSaveRules,
+                    "powersave ");
 
-            size = mUidFirewallStandbyRules.size();
-            if (size > 0) {
-                if (DBG) Slog.d(TAG, "Pushing " + size + " active firewall standby UID rules");
-                final SparseIntArray uidFirewallRules = mUidFirewallStandbyRules;
-                mUidFirewallStandbyRules = new SparseIntArray();
-                for (int i = 0; i < uidFirewallRules.size(); i++) {
-                    setFirewallUidRuleInternal(FIREWALL_CHAIN_STANDBY, uidFirewallRules.keyAt(i),
-                            uidFirewallRules.valueAt(i));
-                }
-            }
             if (mFirewallChainStates.get(FIREWALL_CHAIN_STANDBY)) {
                 setFirewallChainEnabled(FIREWALL_CHAIN_STANDBY, true);
             }
-
-            size = mUidFirewallDozableRules.size();
-            if (size > 0) {
-                if (DBG) Slog.d(TAG, "Pushing " + size + " active firewall dozable UID rules");
-                final SparseIntArray uidFirewallRules = mUidFirewallDozableRules;
-                mUidFirewallDozableRules = new SparseIntArray();
-                for (int i = 0; i < uidFirewallRules.size(); i++) {
-                    setFirewallUidRuleInternal(FIREWALL_CHAIN_DOZABLE, uidFirewallRules.keyAt(i),
-                            uidFirewallRules.valueAt(i));
-                }
-            }
             if (mFirewallChainStates.get(FIREWALL_CHAIN_DOZABLE)) {
                 setFirewallChainEnabled(FIREWALL_CHAIN_DOZABLE, true);
             }
-
-            size = mUidFirewallPowerSaveRules.size();
-            if (size > 0) {
-                Slog.d(TAG, "Pushing " + size + " active firewall powersave UID rules");
-                final SparseIntArray uidFirewallRules = mUidFirewallPowerSaveRules;
-                mUidFirewallPowerSaveRules = new SparseIntArray();
-                for (int i = 0; i < uidFirewallRules.size(); i++) {
-                    setFirewallUidRuleInternal(FIREWALL_CHAIN_POWERSAVE, uidFirewallRules.keyAt(i),
-                            uidFirewallRules.valueAt(i));
-                }
-            }
             if (mFirewallChainStates.get(FIREWALL_CHAIN_POWERSAVE)) {
                 setFirewallChainEnabled(FIREWALL_CHAIN_POWERSAVE, true);
             }
@@ -738,6 +744,7 @@
     private class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks {
         @Override
         public void onDaemonConnected() {
+            Slog.i(TAG, "onDaemonConnected()");
             // event is dispatched from internal NDC thread, so we prepare the
             // daemon back on main thread.
             if (mConnectedSignal != null) {
@@ -1698,28 +1705,30 @@
         }
     }
 
-    @Override
-    public void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) {
+    private void setUidOnMeteredNetworkList(SparseBooleanArray quotaList, int uid,
+            boolean blacklist, boolean enable) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 
         // silently discard when control disabled
         // TODO: eventually migrate to be always enabled
         if (!mBandwidthControlEnabled) return;
 
+        final String chain = blacklist ? "naughtyapps" : "niceapps";
+        final String suffix = enable ? "add" : "remove";
+
         synchronized (mQuotaLock) {
-            final boolean oldRejectOnQuota = mUidRejectOnQuota.get(uid, false);
-            if (oldRejectOnQuota == rejectOnQuotaInterfaces) {
+            final boolean oldEnable = quotaList.get(uid, false);
+            if (oldEnable == enable) {
                 // TODO: eventually consider throwing
                 return;
             }
 
             try {
-                mConnector.execute("bandwidth",
-                        rejectOnQuotaInterfaces ? "addnaughtyapps" : "removenaughtyapps", uid);
-                if (rejectOnQuotaInterfaces) {
-                    mUidRejectOnQuota.put(uid, true);
+                mConnector.execute("bandwidth", suffix + chain, uid);
+                if (enable) {
+                    quotaList.put(uid, true);
                 } else {
-                    mUidRejectOnQuota.delete(uid);
+                    quotaList.delete(uid);
                 }
             } catch (NativeDaemonConnectorException e) {
                 throw e.rethrowAsParcelableException();
@@ -1728,6 +1737,39 @@
     }
 
     @Override
+    public void setUidMeteredNetworkBlacklist(int uid, boolean enable) {
+        setUidOnMeteredNetworkList(mUidRejectOnMetered, uid, true, enable);
+    }
+
+    @Override
+    public void setUidMeteredNetworkWhitelist(int uid, boolean enable) {
+        setUidOnMeteredNetworkList(mUidAllowOnMetered, uid, false, enable);
+    }
+
+    @Override
+    public boolean setDataSaverModeEnabled(boolean enable) {
+        if (DBG) Log.d(TAG, "setDataSaverMode: " + enable);
+        synchronized (mQuotaLock) {
+            if (mDataSaverMode == enable) {
+                Log.w(TAG, "setDataSaverMode(): already " + mDataSaverMode);
+                return true;
+            }
+            try {
+                final boolean changed = mNetdService.bandwidthEnableDataSaver(enable);
+                if (changed) {
+                    mDataSaverMode = enable;
+                } else {
+                    Log.w(TAG, "setDataSaverMode(" + enable + "): netd command silently failed");
+                }
+                return changed;
+            } catch (RemoteException e) {
+                Log.w(TAG, "setDataSaverMode(" + enable + "): netd command failed", e);
+                return false;
+            }
+        }
+    }
+
+    @Override
     public void setUidCleartextNetworkPolicy(int uid, int policy) {
         if (Binder.getCallingUid() != uid) {
             mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
@@ -2221,29 +2263,22 @@
         synchronized (mQuotaLock) {
             pw.print("Active quota ifaces: "); pw.println(mActiveQuotas.toString());
             pw.print("Active alert ifaces: "); pw.println(mActiveAlerts.toString());
-        }
-
-        synchronized (mUidRejectOnQuota) {
-            pw.print("UID reject on quota ifaces: [");
-            final int size = mUidRejectOnQuota.size();
-            for (int i = 0; i < size; i++) {
-                pw.print(mUidRejectOnQuota.keyAt(i));
-                if (i < size - 1) pw.print(",");
-            }
-            pw.println("]");
+            pw.print("Data saver mode: "); pw.println(mDataSaverMode);
+            dumpUidRuleOnQuotaLocked(pw, "blacklist", mUidRejectOnMetered);
+            dumpUidRuleOnQuotaLocked(pw, "whitelist", mUidAllowOnMetered);
         }
 
         synchronized (mUidFirewallRules) {
             dumpUidFirewallRule(pw, "", mUidFirewallRules);
         }
 
-        pw.println("UID firewall standby chain enabled: " +
+        pw.print("UID firewall standby chain enabled: "); pw.println(
                 mFirewallChainStates.get(FIREWALL_CHAIN_STANDBY));
         synchronized (mUidFirewallStandbyRules) {
             dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_STANDBY, mUidFirewallStandbyRules);
         }
 
-        pw.println("UID firewall dozable chain enabled: " +
+        pw.print("UID firewall dozable chain enabled: "); pw.println(
                 mFirewallChainStates.get(FIREWALL_CHAIN_DOZABLE));
         synchronized (mUidFirewallDozableRules) {
             dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_DOZABLE, mUidFirewallDozableRules);
@@ -2267,6 +2302,29 @@
         }
 
         pw.print("Firewall enabled: "); pw.println(mFirewallEnabled);
+        pw.print("Netd service status: " );
+        if (mNetdService == null) {
+            pw.println("disconnected");
+        } else {
+            try {
+                final boolean alive = mNetdService.isAlive();
+                pw.println(alive ? "alive": "dead");
+            } catch (RemoteException e) {
+                pw.println("unreachable");
+            }
+        }
+    }
+
+    private void dumpUidRuleOnQuotaLocked(PrintWriter pw, String name, SparseBooleanArray list) {
+        pw.print("UID bandwith control ");
+        pw.print(name);
+        pw.print(" rule: [");
+        final int size = list.size();
+        for (int i = 0; i < size; i++) {
+            pw.print(list.keyAt(i));
+            if (i < size - 1) pw.print(",");
+        }
+        pw.println("]");
     }
 
     private void dumpUidFirewallRule(PrintWriter pw, String name, SparseIntArray rules) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 66008c9..1abb5ff 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -526,6 +526,7 @@
 
     // Determines whether to take full screen screenshots
     static final boolean TAKE_FULLSCREEN_SCREENSHOTS = true;
+    public static final float FULLSCREEN_SCREENSHOT_SCALE = 0.6f;
 
     private static native int nativeMigrateToBoost();
     private static native int nativeMigrateFromBoost();
@@ -1491,6 +1492,7 @@
     /** The dimensions of the thumbnails in the Recents UI. */
     int mThumbnailWidth;
     int mThumbnailHeight;
+    float mFullscreenThumbnailScale;
 
     final ServiceThread mHandlerThread;
     final MainHandler mHandler;
@@ -11422,18 +11424,16 @@
                 try {
                     final int currentUserId = mUserController.getCurrentUserIdLocked();
                     // Get the focused task before launching launcher.
-                    final int taskId = (mFocusedActivity == null)
-                            ? -1 : mFocusedActivity.task.taskId;
-                    startHomeActivityLocked(currentUserId, "notifyLockedProfile");
+
                     if (mUserController.isLockScreenDisabled(currentUserId)) {
-                        // If there is no device lock, we first go to launcher and then resume the
-                        // original task. Work challenge will be shown because we intercepted
-                        // startActivityFromRecentsInner and the reason why we switch to home stack
-                        // first is to prevent pressing back button brings user back to the work
-                        // app.
-                        if (taskId != -1) {
-                            startActivityFromRecentsInner(taskId, null);
+                        // If there is no device lock, we will show the profile's credential page.
+                        // startActivityFromRecentsInner is intercepted and will forward user to it.
+                        if (mFocusedActivity != null) {
+                            startActivityFromRecentsInner(mFocusedActivity.task.taskId, null);
                         }
+                    } else {
+                        // Showing launcher to avoid user entering credential twice.
+                        startHomeActivityLocked(currentUserId, "notifyLockedProfile");
                     }
                 } finally {
                     Binder.restoreCallingIdentity(ident);
@@ -12592,6 +12592,8 @@
                     com.android.internal.R.dimen.thumbnail_width);
             mThumbnailHeight = res.getDimensionPixelSize(
                     com.android.internal.R.dimen.thumbnail_height);
+            mFullscreenThumbnailScale = res.getFraction(
+                    com.android.internal.R.fraction.thumbnail_fullscreen_scale, 1, 1);
             mDefaultPinnedStackBounds = Rect.unflattenFromString(res.getString(
                     com.android.internal.R.string.config_defaultPictureInPictureBounds));
             mAppErrors.loadAppsNotReportingCrashesFromConfigLocked(res.getString(
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index bbc37cd..5cb04c8 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -929,7 +929,7 @@
             // use within SystemUI while keeping memory usage low.
             if (ActivityManagerService.TAKE_FULLSCREEN_SCREENSHOTS) {
                 w = h = -1;
-                scale = 0.5f;
+                scale = mService.mFullscreenThumbnailScale;
             }
             return mWindowManager.screenshotApplications(who.appToken, Display.DEFAULT_DISPLAY,
                     w, h, scale);
diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
index 13d90e3..76dfd01 100644
--- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
@@ -26,6 +26,7 @@
 import static android.content.Intent.EXTRA_TASK_ID;
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
 import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
 
 import android.app.KeyguardManager;
@@ -192,14 +193,14 @@
                 Binder.getCallingUid(), userId, null, null, 0, new Intent[]{ intent },
                 new String[]{ resolvedType },
                 FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE, null);
-        final int flags = intent.getFlags();
         final KeyguardManager km = (KeyguardManager) mService.mContext
                 .getSystemService(KEYGUARD_SERVICE);
         final Intent newIntent = km.createConfirmDeviceCredentialIntent(null, null, userId);
         if (newIntent == null) {
             return null;
         }
-        newIntent.setFlags(flags | FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+        newIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS |
+                FLAG_ACTIVITY_TASK_ON_HOME);
         newIntent.putExtra(EXTRA_PACKAGE_NAME, aInfo.packageName);
         newIntent.putExtra(EXTRA_INTENT, new IntentSender(target));
         return newIntent;
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 71a1f97..ffa3b5b 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1170,7 +1170,8 @@
         }
 
         if (useCheckinFormat) {
-            List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(0);
+            List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(
+                    PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.MATCH_ALL);
             if (isRealCheckin) {
                 // For a real checkin, first we want to prefer to use the last complete checkin
                 // file if there is one.
diff --git a/services/core/java/com/android/server/am/HealthStatsBatteryStatsWriter.java b/services/core/java/com/android/server/am/HealthStatsBatteryStatsWriter.java
index 39c6ce6..9fb51c1 100644
--- a/services/core/java/com/android/server/am/HealthStatsBatteryStatsWriter.java
+++ b/services/core/java/com/android/server/am/HealthStatsBatteryStatsWriter.java
@@ -122,7 +122,8 @@
             // Battery Stats stores the GPS sensors with a bogus key in this API. Pull it out
             // as a separate metric here so as to not expose that in the API.
             if (sensorId == BatteryStats.Uid.Sensor.GPS) {
-                addTimer(uidWriter, UidHealthStats.TIMER_GPS_SENSOR, sensors.valueAt(i).getSensorTime());
+                addTimer(uidWriter, UidHealthStats.TIMER_GPS_SENSOR,
+                        sensors.valueAt(i).getSensorTime());
             } else {
                 addTimers(uidWriter, UidHealthStats.TIMERS_SENSORS, Integer.toString(sensorId),
                         sensors.valueAt(i).getSensorTime());
@@ -131,7 +132,7 @@
 
         // STATS_PIDS
         pids = uid.getPidStats();
-        N = sensors.size();
+        N = pids.size();
         for (int i=0; i<N; i++) {
             final HealthStatsWriter writer = new HealthStatsWriter(PidHealthStats.CONSTANTS);
             writePid(writer, pids.valueAt(i));
@@ -241,7 +242,8 @@
         addTimer(uidWriter, UidHealthStats.TIMER_CAMERA, uid.getCameraTurnedOnTimer());
 
         // TIMER_FOREGROUND_ACTIVITY
-        addTimer(uidWriter, UidHealthStats.TIMER_FOREGROUND_ACTIVITY, uid.getForegroundActivityTimer());
+        addTimer(uidWriter, UidHealthStats.TIMER_FOREGROUND_ACTIVITY,
+                uid.getForegroundActivityTimer());
 
         // TIMER_BLUETOOTH_SCAN
         addTimer(uidWriter, UidHealthStats.TIMER_BLUETOOTH_SCAN, uid.getBluetoothScanTimer());
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index b4aa4cf..5407d28 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -558,7 +558,7 @@
             }
             EventLog.writeEvent(EventLogTags.AM_KILL, userId, pid, processName, setAdj, reason);
             Process.killProcessQuiet(pid);
-            Process.killProcessGroup(info.uid, pid);
+            Process.killProcessGroup(uid, pid);
             if (!persistent) {
                 killed = true;
                 killedByAm = true;
diff --git a/services/core/java/com/android/server/connectivity/MetricsLoggerService.java b/services/core/java/com/android/server/connectivity/MetricsLoggerService.java
index f6dc9b9..7cac227 100644
--- a/services/core/java/com/android/server/connectivity/MetricsLoggerService.java
+++ b/services/core/java/com/android/server/connectivity/MetricsLoggerService.java
@@ -60,7 +60,7 @@
     private long mLastSentEventTimeMillis = System.currentTimeMillis();
 
     private final void enforceConnectivityInternalPermission() {
-        getContext().enforceCallingPermission(
+        getContext().enforceCallingOrSelfPermission(
                 android.Manifest.permission.CONNECTIVITY_INTERNAL,
                 "MetricsLoggerService");
     }
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index b4c71c1..c5d38cb 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -32,7 +32,6 @@
 import com.android.internal.util.AsyncChannel;
 import com.android.server.ConnectivityService;
 import com.android.server.connectivity.NetworkMonitor;
-import com.android.server.connectivity.ApfFilter;
 
 import java.util.ArrayList;
 import java.util.Comparator;
@@ -164,8 +163,6 @@
     // Used by ConnectivityService to keep track of 464xlat.
     public Nat464Xlat clatd;
 
-    public ApfFilter apfFilter;
-
     public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
             LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler,
             NetworkMisc misc, NetworkRequest defaultRequest, ConnectivityService connService) {
@@ -178,7 +175,6 @@
         currentScore = score;
         networkMonitor = connService.createNetworkMonitor(context, handler, this, defaultRequest);
         networkMisc = misc;
-        apfFilter.maybeInstall(connService, this);
     }
 
     /**
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index bce7733..bbb162e 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -34,6 +34,8 @@
 import android.net.ProxyInfo;
 import android.net.TrafficStats;
 import android.net.Uri;
+import android.net.metrics.CaptivePortalCheckResultEvent;
+import android.net.metrics.CaptivePortalStateChangeEvent;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 import android.os.Handler;
@@ -297,9 +299,13 @@
                     transitionTo(mLingeringState);
                     return HANDLED;
                 case CMD_NETWORK_CONNECTED:
+                    CaptivePortalStateChangeEvent.logEvent(
+                            CaptivePortalStateChangeEvent.NETWORK_MONITOR_CONNECTED);
                     transitionTo(mEvaluatingState);
                     return HANDLED;
                 case CMD_NETWORK_DISCONNECTED:
+                    CaptivePortalStateChangeEvent.logEvent(
+                            CaptivePortalStateChangeEvent.NETWORK_MONITOR_DISCONNECTED);
                     if (mLaunchCaptivePortalAppBroadcastReceiver != null) {
                         mContext.unregisterReceiver(mLaunchCaptivePortalAppBroadcastReceiver);
                         mLaunchCaptivePortalAppBroadcastReceiver = null;
@@ -349,6 +355,8 @@
     private class ValidatedState extends State {
         @Override
         public void enter() {
+            CaptivePortalStateChangeEvent.logEvent(
+                   CaptivePortalStateChangeEvent.NETWORK_MONITOR_VALIDATED);
             mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
                     NETWORK_TEST_RESULT_VALID, 0, mNetworkAgentInfo));
         }
@@ -457,6 +465,8 @@
                     // will be unresponsive. isCaptivePortal() could be executed on another Thread
                     // if this is found to cause problems.
                     int httpResponseCode = isCaptivePortal();
+                    CaptivePortalCheckResultEvent.logEvent(mNetworkAgentInfo.network.netId,
+                            httpResponseCode);
                     if (httpResponseCode == 204) {
                         transitionTo(mValidatedState);
                     } else if (httpResponseCode >= 200 && httpResponseCode <= 399) {
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 4527f1f..715d2d8 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -17,12 +17,12 @@
 package com.android.server.display;
 
 import android.content.res.Resources;
-import android.os.Build;
 import com.android.server.LocalServices;
 import com.android.server.lights.Light;
 import com.android.server.lights.LightsManager;
 
 import android.content.Context;
+import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -31,7 +31,6 @@
 import android.os.Trace;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.SparseBooleanArray;
 import android.view.Display;
 import android.view.DisplayEventReceiver;
 import android.view.Surface;
@@ -388,7 +387,7 @@
                     mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
                             | DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
                     if (res.getBoolean(com.android.internal.R.bool.config_mainBuiltInDisplayIsRound)
-                            || (Build.HARDWARE.contains("goldfish")
+                            || (Build.IS_EMULATOR
                             && SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false))) {
                         mInfo.flags |= DisplayDeviceInfo.FLAG_ROUND;
                     }
diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java
index 5b1cedc..fc412e3 100644
--- a/services/core/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java
@@ -20,6 +20,7 @@
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NONE;
 import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
 import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
+import static android.provider.Settings.ACTION_VPN_SETTINGS;
 
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -66,9 +67,6 @@
 
     private static final String ACTION_LOCKDOWN_RESET = "com.android.server.action.LOCKDOWN_RESET";
 
-    private static final String ACTION_VPN_SETTINGS = "android.net.vpn.SETTINGS";
-    private static final String EXTRA_PICK_LOCKDOWN = "android.net.vpn.PICK_LOCKDOWN";
-
     private static final int ROOT_UID = 0;
 
     private final Context mContext;
@@ -101,7 +99,6 @@
         mProfile = Preconditions.checkNotNull(profile);
 
         final Intent configIntent = new Intent(ACTION_VPN_SETTINGS);
-        configIntent.putExtra(EXTRA_PICK_LOCKDOWN, true);
         mConfigIntent = PendingIntent.getActivity(mContext, 0, configIntent, 0);
 
         final Intent resetIntent = new Intent(ACTION_LOCKDOWN_RESET);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 49ae293..9b92e4f 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -2803,7 +2803,7 @@
 
     private void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) {
         try {
-            mNetworkManager.setUidNetworkRules(uid, rejectOnQuotaInterfaces);
+            mNetworkManager.setUidMeteredNetworkBlacklist(uid, rejectOnQuotaInterfaces);
         } catch (IllegalStateException e) {
             Log.wtf(TAG, "problem setting uid rules", e);
         } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/net/NetworkStatsObservers.java b/services/core/java/com/android/server/net/NetworkStatsObservers.java
index 2f55562..6f781b3 100644
--- a/services/core/java/com/android/server/net/NetworkStatsObservers.java
+++ b/services/core/java/com/android/server/net/NetworkStatsObservers.java
@@ -52,7 +52,7 @@
  */
 class NetworkStatsObservers {
     private static final String TAG = "NetworkStatsObservers";
-    private static final boolean LOGV = true;
+    private static final boolean LOGV = false;
 
     private static final long MIN_THRESHOLD_BYTES = 2 * MB_IN_BYTES;
 
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index d449ce5..eae2eaa 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -16,8 +16,6 @@
 
 package com.android.server.pm;
 
-import static com.android.server.pm.PackageManagerServiceCompilerMapping.REASON_BACKGROUND_DEXOPT;
-
 import android.app.AlarmManager;
 import android.app.job.JobInfo;
 import android.app.job.JobParameters;
@@ -26,7 +24,6 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.ServiceManager;
-import android.os.SystemProperties;
 import android.util.ArraySet;
 import android.util.Log;
 
@@ -94,7 +91,7 @@
                         continue;
                     }
                     if (!pm.performDexOpt(pkg, /* instruction set */ null, /* checkProfiles */ true,
-                            REASON_BACKGROUND_DEXOPT, /* force */ false)) {
+                            PackageManagerService.REASON_BACKGROUND_DEXOPT, /* force */ false)) {
                         // there was a problem running dexopt,
                         // remember this so we do not keep retrying.
                         sFailedPackageNames.add(pkg);
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index a1f937ab..7e25632 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -169,8 +169,12 @@
         mInstaller.execute("rmpackagedir", packageDir);
     }
 
-    public void rmProfiles(String pkgName) throws InstallerException {
-        mInstaller.execute("rmprofiles", pkgName);
+    public void clearAppProfiles(String pkgName) throws InstallerException {
+        mInstaller.execute("clear_app_profiles", pkgName);
+    }
+
+    public void destroyAppProfiles(String pkgName) throws InstallerException {
+        mInstaller.execute("destroy_app_profiles", pkgName);
     }
 
     public void createUserConfig(int userid) throws InstallerException {
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 2f0532a..c303ceb 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -42,6 +42,7 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.IInterface;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteCallbackList;
@@ -54,6 +55,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.content.PackageMonitor;
+import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.Preconditions;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
@@ -102,6 +104,8 @@
 
         private final MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
 
+        private final Handler mCallbackHandler;
+
         public LauncherAppsImpl(Context context) {
             mContext = context;
             mPm = mContext.getPackageManager();
@@ -109,6 +113,7 @@
             mShortcutServiceInternal = Preconditions.checkNotNull(
                     LocalServices.getService(ShortcutServiceInternal.class));
             mShortcutServiceInternal.addListener(mPackageMonitor);
+            mCallbackHandler = BackgroundThread.getHandler();
         }
 
         @VisibleForTesting
@@ -165,7 +170,7 @@
          * Register a receiver to watch for package broadcasts
          */
         private void startWatchingPackageBroadcasts() {
-            mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
+            mPackageMonitor.register(mContext, UserHandle.ALL, true, mCallbackHandler);
         }
 
         /**
@@ -550,8 +555,9 @@
             }
         }
 
+        @VisibleForTesting
         void postToPackageMonitorHandler(Runnable r) {
-            mPackageMonitor.getRegisteredHandler().post(r);
+            mCallbackHandler.post(r);
         }
 
         private class MyPackageMonitor extends PackageMonitor implements ShortcutChangeListener {
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 03e838b..c3a9226 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -20,7 +20,6 @@
 import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
 import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason;
-import static com.android.server.pm.PackageManagerServiceCompilerMapping.REASON_AB_OTA;
 
 import android.content.Context;
 import android.content.pm.IOtaDexopt;
@@ -144,7 +143,7 @@
         }
 
         mPackageDexOptimizer.performDexOpt(nextPackage, null /* ISAs */, false /* useProfiles */,
-                getCompilerFilterForReason(REASON_AB_OTA));
+                getCompilerFilterForReason(PackageManagerService.REASON_AB_OTA));
     }
 
     private void moveAbArtifacts(Installer installer) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b73d8f3..64f8c98 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -94,11 +94,6 @@
 import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet;
 import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason;
 import static com.android.server.pm.PackageManagerServiceCompilerMapping.getFullCompilerFilter;
-import static com.android.server.pm.PackageManagerServiceCompilerMapping.REASON_BOOT;
-import static com.android.server.pm.PackageManagerServiceCompilerMapping.REASON_FORCED_DEXOPT;
-import static com.android.server.pm.PackageManagerServiceCompilerMapping.REASON_INSTALL;
-import static com.android.server.pm.PackageManagerServiceCompilerMapping.REASON_NON_SYSTEM_LIBRARY;
-import static com.android.server.pm.PackageManagerServiceCompilerMapping.REASON_SHARED_APK;
 import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_FAILURE;
 import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS;
 import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
@@ -448,6 +443,17 @@
         sBrowserIntent.setData(Uri.parse("http:"));
     }
 
+    // Compilation reasons.
+    public static final int REASON_BOOT = 0;
+    public static final int REASON_INSTALL = 1;
+    public static final int REASON_BACKGROUND_DEXOPT = 2;
+    public static final int REASON_AB_OTA = 3;
+    public static final int REASON_NON_SYSTEM_LIBRARY = 4;
+    public static final int REASON_SHARED_APK = 5;
+    public static final int REASON_FORCED_DEXOPT = 6;
+
+    public static final int REASON_LAST = REASON_FORCED_DEXOPT;
+
     final ServiceThread mHandlerThread;
 
     final PackageHandler mHandler;
@@ -7197,6 +7203,30 @@
         }
     }
 
+    private void deleteProfilesLI(String packageName, boolean destroy) {
+        final PackageParser.Package pkg;
+        synchronized (mPackages) {
+            pkg = mPackages.get(packageName);
+        }
+        if (pkg == null) {
+            Slog.w(TAG, "Failed to delete profiles. No package: " + packageName);
+            return;
+        }
+        deleteProfilesLI(pkg, destroy);
+    }
+
+    private void deleteProfilesLI(PackageParser.Package pkg, boolean destroy) {
+        try {
+            if (destroy) {
+                mInstaller.clearAppProfiles(pkg.packageName);
+            } else {
+                mInstaller.destroyAppProfiles(pkg.packageName);
+            }
+        } catch (InstallerException ex) {
+            Log.e(TAG, "Could not delete profiles for package " + pkg.packageName);
+        }
+    }
+
     private void deleteCodeCacheDirsLI(String volumeUuid, String packageName) {
         final PackageParser.Package pkg;
         synchronized (mPackages) {
@@ -13229,6 +13259,7 @@
             }
 
             deleteCodeCacheDirsLI(pkg);
+            deleteProfilesLI(pkg, /*destroy*/ false);
 
             try {
                 final PackageParser.Package newPackage = scanPackageTracedLI(pkg, parseFlags,
@@ -13363,6 +13394,7 @@
 
         // Successfully disabled the old package. Now proceed with re-installation
         deleteCodeCacheDirsLI(pkg);
+        deleteProfilesLI(pkg, /*destroy*/ false);
 
         res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
         pkg.setApplicationInfoFlags(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP,
@@ -14435,6 +14467,7 @@
             if (DEBUG_REMOVE) Slog.d(TAG, "deletePackageX: pkg=" + packageName + " user=" + userId);
             res = deletePackageLI(packageName, removeForUser, true, allUsers,
                     flags | REMOVE_CHATTY, info, true, null);
+            deleteProfilesLI(packageName, /*destroy*/ true);
             synchronized (mPackages) {
                 if (res) {
                     mEphemeralApplicationRegistry.onPackageUninstalledLPw(uninstalledPs.pkg);
@@ -15202,7 +15235,7 @@
     public void clearApplicationProfileData(String packageName) {
         enforceSystemOrRoot("Only the system can clear all profile data");
         try {
-            mInstaller.rmProfiles(packageName);
+            mInstaller.clearAppProfiles(packageName);
         } catch (InstallerException ex) {
             Log.e(TAG, "Could not clear profile data of package " + packageName);
         }
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
index b53f8d1..238e410 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
@@ -24,17 +24,6 @@
  * Manage (retrieve) mappings from compilation reason to compilation filter.
  */
 class PackageManagerServiceCompilerMapping {
-    // Compilation reasons.
-    public static final int REASON_BOOT = 0;
-    public static final int REASON_INSTALL = 1;
-    public static final int REASON_BACKGROUND_DEXOPT = 2;
-    public static final int REASON_AB_OTA = 3;
-    public static final int REASON_NON_SYSTEM_LIBRARY = 4;
-    public static final int REASON_SHARED_APK = 5;
-    public static final int REASON_FORCED_DEXOPT = 6;
-
-    private static final int REASON_LAST = REASON_FORCED_DEXOPT;
-
     // Names for compilation reasons.
     static final String REASON_STRINGS[] = {
             "boot", "install", "bg-dexopt", "ab-ota", "nsys-library", "shared-apk", "forced-dexopt"
@@ -42,7 +31,7 @@
 
     // Static block to ensure the strings array is of the right length.
     static {
-        if (REASON_LAST + 1 != REASON_STRINGS.length) {
+        if (PackageManagerService.REASON_LAST + 1 != REASON_STRINGS.length) {
             throw new IllegalStateException("REASON_STRINGS not correct");
         }
     }
@@ -67,8 +56,8 @@
 
         // Ensure that some reasons are not mapped to profile-guided filters.
         switch (reason) {
-            case REASON_SHARED_APK:
-            case REASON_FORCED_DEXOPT:
+            case PackageManagerService.REASON_SHARED_APK:
+            case PackageManagerService.REASON_FORCED_DEXOPT:
                 if (DexFile.isProfileGuidedCompilerFilter(sysPropValue)) {
                     throw new IllegalStateException("\"" + sysPropValue + "\" is profile-guided, "
                             + "but not allowed for " + REASON_STRINGS[reason]);
@@ -86,7 +75,7 @@
         // overview. Store the exceptions here.
         RuntimeException toThrow = null;
 
-        for (int reason = 0; reason <= REASON_LAST; reason++) {
+        for (int reason = 0; reason <= PackageManagerService.REASON_LAST; reason++) {
             try {
                 // Check that the system property name is legal.
                 String sysPropName = getSystemPropertyName(reason);
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 7f626b2..bf44b0f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -44,7 +44,6 @@
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ShellCommand;
-import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.PrintWriterPrinter;
@@ -331,12 +330,12 @@
                 // Use the default mode for background dexopt.
                 targetCompilerFilter =
                         PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
-                                PackageManagerServiceCompilerMapping.REASON_BACKGROUND_DEXOPT);
+                                PackageManagerService.REASON_BACKGROUND_DEXOPT);
             } else if ("reset".equals(compilerFilter)) {
                 // Use the default mode for install.
                 targetCompilerFilter =
                         PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
-                                PackageManagerServiceCompilerMapping.REASON_INSTALL);
+                                PackageManagerService.REASON_INSTALL);
             } else {
                 if (!DexFile.isValidCompilerFilter(compilerFilter)) {
                     pw.println("Error: \"" + compilerFilter +
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index e66ec3c..fa0eb46 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4456,7 +4456,8 @@
             }
         }
 
-        if ((permissionNames != null || dumpAll) && ps.pkg.requestedPermissions != null
+        if ((permissionNames != null || dumpAll) && ps.pkg != null
+                && ps.pkg.requestedPermissions != null
                 && ps.pkg.requestedPermissions.size() > 0) {
             final ArrayList<String> perms = ps.pkg.requestedPermissions;
             pw.print(prefix); pw.println("  requested permissions:");
diff --git a/services/core/java/com/android/server/policy/EnableAccessibilityController.java b/services/core/java/com/android/server/policy/EnableAccessibilityController.java
index da9c001..6b203a9 100644
--- a/services/core/java/com/android/server/policy/EnableAccessibilityController.java
+++ b/services/core/java/com/android/server/policy/EnableAccessibilityController.java
@@ -16,7 +16,9 @@
 
 package com.android.server.policy;
 
+import android.accessibilityservice.AccessibilityService;
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -32,19 +34,25 @@
 import android.os.UserManager;
 import android.provider.Settings;
 import android.speech.tts.TextToSpeech;
+import android.util.Log;
 import android.util.MathUtils;
 import android.view.IWindowManager;
 import android.view.MotionEvent;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
+import android.view.WindowManagerInternal;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.IAccessibilityManager;
 
 import com.android.internal.R;
+import com.android.server.LocalServices;
 
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 
 public class EnableAccessibilityController {
+    private static final String TAG = "EnableAccessibilityController";
 
     private static final int SPEAK_WARNING_DELAY_MILLIS = 2000;
     private static final int ENABLE_ACCESSIBILITY_DELAY_MILLIS = 6000;
@@ -75,9 +83,6 @@
         }
     };
 
-    private final IWindowManager mWindowManager = IWindowManager.Stub.asInterface(
-            ServiceManager.getService("window"));
-
     private final IAccessibilityManager mAccessibilityManager = IAccessibilityManager
             .Stub.asInterface(ServiceManager.getService("accessibility"));
 
@@ -132,7 +137,7 @@
                 && !getInstalledSpeakingAccessibilityServices(context).isEmpty();
     }
 
-    private static List<AccessibilityServiceInfo> getInstalledSpeakingAccessibilityServices(
+    public static List<AccessibilityServiceInfo> getInstalledSpeakingAccessibilityServices(
             Context context) {
         List<AccessibilityServiceInfo> services = new ArrayList<AccessibilityServiceInfo>();
         services.addAll(AccessibilityManager.getInstance(context)
@@ -213,71 +218,74 @@
     }
 
     private void enableAccessibility() {
-        List<AccessibilityServiceInfo> services = getInstalledSpeakingAccessibilityServices(
-                mContext);
-        if (services.isEmpty()) {
+        if (enableAccessibility(mContext)) {
+            mOnAccessibilityEnabledCallback.run();
+        }
+    }
+
+    public static boolean enableAccessibility(Context context) {
+        final IAccessibilityManager accessibilityManager = IAccessibilityManager
+                .Stub.asInterface(ServiceManager.getService("accessibility"));
+        final WindowManagerInternal windowManager = LocalServices.getService(
+                WindowManagerInternal.class);
+        final UserManager userManager = (UserManager) context.getSystemService(
+                Context.USER_SERVICE);
+        ComponentName componentName = getInstalledSpeakingAccessibilityServiceComponent(context);
+        if (componentName == null) {
+            return false;
+        }
+
+        boolean keyguardLocked = windowManager.isKeyguardLocked();
+        final boolean hasMoreThanOneUser = userManager.getUsers().size() > 1;
+        try {
+            if (!keyguardLocked || !hasMoreThanOneUser) {
+                final int userId = ActivityManager.getCurrentUser();
+                accessibilityManager.enableAccessibilityService(componentName, userId);
+            } else if (keyguardLocked) {
+                accessibilityManager.temporaryEnableAccessibilityStateUntilKeyguardRemoved(
+                        componentName, true /* enableTouchExploration */);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "cannot enable accessibilty: " + e);
+        }
+
+        return true;
+    }
+
+    public static void disableAccessibility(Context context) {
+        final IAccessibilityManager accessibilityManager = IAccessibilityManager
+                .Stub.asInterface(ServiceManager.getService("accessibility"));
+        ComponentName componentName = getInstalledSpeakingAccessibilityServiceComponent(context);
+        if (componentName == null) {
             return;
         }
-        boolean keyguardLocked = false;
+
+        final int userId = ActivityManager.getCurrentUser();
         try {
-            keyguardLocked = mWindowManager.isKeyguardLocked();
-        } catch (RemoteException re) {
-            /* ignore */
+            accessibilityManager.disableAccessibilityService(componentName, userId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "cannot disable accessibility " + e);
+        }
+    }
+
+    public static boolean isAccessibilityEnabled(Context context) {
+        final AccessibilityManager accessibilityManager =
+                context.getSystemService(AccessibilityManager.class);
+        List enabledServices = accessibilityManager.getEnabledAccessibilityServiceList(
+                AccessibilityServiceInfo.FEEDBACK_SPOKEN);
+        return enabledServices != null && !enabledServices.isEmpty();
+    }
+
+    @Nullable
+    public static ComponentName getInstalledSpeakingAccessibilityServiceComponent(
+            Context context) {
+        List<AccessibilityServiceInfo> services =
+                getInstalledSpeakingAccessibilityServices(context);
+        if (services.isEmpty()) {
+            return null;
         }
 
-        final boolean hasMoreThanOneUser = mUserManager.getUsers().size() > 1;
-
-        AccessibilityServiceInfo service = services.get(0);
-        boolean enableTouchExploration = (service.flags
-                & AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0;
-        // Try to find a service supporting explore by touch.
-        if (!enableTouchExploration) {
-            final int serviceCount = services.size();
-            for (int i = 1; i < serviceCount; i++) {
-                AccessibilityServiceInfo candidate = services.get(i);
-                if ((candidate.flags & AccessibilityServiceInfo
-                        .FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0) {
-                    enableTouchExploration = true;
-                    service = candidate;
-                    break;
-                }
-            }
-        }
-
-        ServiceInfo serviceInfo = service.getResolveInfo().serviceInfo;
-        ComponentName componentName = new ComponentName(serviceInfo.packageName, serviceInfo.name);
-        if (!keyguardLocked || !hasMoreThanOneUser) {
-            final int userId = ActivityManager.getCurrentUser();
-            String enabledServiceString = componentName.flattenToString();
-            ContentResolver resolver = mContext.getContentResolver();
-            // Enable one speaking accessibility service.
-            Settings.Secure.putStringForUser(resolver,
-                    Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
-                    enabledServiceString, userId);
-            // Allow the services we just enabled to toggle touch exploration.
-            Settings.Secure.putStringForUser(resolver,
-                    Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
-                    enabledServiceString, userId);
-            // Enable touch exploration.
-            if (enableTouchExploration) {
-                Settings.Secure.putIntForUser(resolver, Settings.Secure.TOUCH_EXPLORATION_ENABLED,
-                        1, userId);
-            }
-            // Enable accessibility script injection (AndroidVox) for web content.
-            Settings.Secure.putIntForUser(resolver, Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION,
-                    1, userId);
-            // Turn on accessibility mode last.
-            Settings.Secure.putIntForUser(resolver, Settings.Secure.ACCESSIBILITY_ENABLED,
-                    1, userId);
-        } else if (keyguardLocked) {
-            try {
-                mAccessibilityManager.temporaryEnableAccessibilityStateUntilKeyguardRemoved(
-                        componentName, enableTouchExploration);
-            } catch (RemoteException re) {
-                /* ignore */
-            }
-        }
-
-        mOnAccessibilityEnabledCallback.run();
+        ServiceInfo serviceInfo = services.get(0).getResolveInfo().serviceInfo;
+        return new ComponentName(serviceInfo.packageName, serviceInfo.name);
     }
 }
diff --git a/services/core/java/com/android/server/utils/ManagedApplicationService.java b/services/core/java/com/android/server/utils/ManagedApplicationService.java
index ad8acef0..0f251fd 100644
--- a/services/core/java/com/android/server/utils/ManagedApplicationService.java
+++ b/services/core/java/com/android/server/utils/ManagedApplicationService.java
@@ -62,8 +62,6 @@
     private IInterface mBoundInterface;
     private PendingEvent mPendingEvent;
 
-
-
     private ManagedApplicationService(final Context context, final ComponentName component,
             final int userId, int clientLabel, String settingsAction,
             BinderChecker binderChecker) {
@@ -211,6 +209,7 @@
                         } else {
                             // Service connection wasn't pending, must have been disconnected
                             mContext.unbindService(this);
+                            return;
                         }
 
                         try {
@@ -242,6 +241,8 @@
                 @Override
                 public void onServiceDisconnected(ComponentName componentName) {
                     Slog.w(TAG, "Service disconnected: " + intent);
+                    mConnection = null;
+                    mBoundInterface = null;
                 }
             };
 
diff --git a/services/core/java/com/android/server/vr/VrManagerInternal.java b/services/core/java/com/android/server/vr/VrManagerInternal.java
index 8316efa..93bb9d7 100644
--- a/services/core/java/com/android/server/vr/VrManagerInternal.java
+++ b/services/core/java/com/android/server/vr/VrManagerInternal.java
@@ -38,6 +38,17 @@
     public abstract boolean isInVrMode();
 
     /**
+     * Return {@code true} if the given package is the currently bound VrListenerService for the
+     * given user.
+     *
+     * @param packageName The package name to check.
+     * @param userId the user ID to check the package name for.
+     *
+     * @return {@code true} if the given package is the currently bound VrListenerService.
+     */
+    public abstract boolean isCurrentVrListener(String packageName, int userId);
+
+    /**
      * Set the current VR mode state.
      *
      * @param enabled {@code true} to enable VR mode.
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index 6bf949c..aa6f59e 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -127,6 +127,11 @@
         }
 
         @Override
+        public boolean isCurrentVrListener(String packageName, int userId) {
+            return VrManagerService.this.isCurrentVrListener(packageName, userId);
+        }
+
+        @Override
         public void registerListener(VrStateListener listener) {
             VrManagerService.this.addListener(listener);
         }
@@ -355,6 +360,16 @@
         }
     }
 
+    private boolean isCurrentVrListener(String packageName, int userId) {
+        synchronized (mLock) {
+            if (mCurrentVrService == null) {
+                return false;
+            }
+            return mCurrentVrService.getComponent().getPackageName().equals(packageName) &&
+                    userId == mCurrentVrService.getUserId();
+        }
+    }
+
     private void addListener(VrStateListener listener) {
         synchronized (mLock) {
             mListeners.add(listener);
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 5cb7099..2a57d8e 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -43,9 +43,12 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
+import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
 
 import android.annotation.Nullable;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.graphics.Rect;
 import android.os.Debug;
@@ -943,6 +946,8 @@
         final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
         final int thumbHeightI = mTmpRect.height();
         final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
+        final int thumbStartX = mTmpRect.left - containingFrame.left;
+        final int thumbStartY = mTmpRect.top - containingFrame.top;
 
         // Used for the ENTER_SCALE_UP and EXIT_SCALE_DOWN transitions
         float scale = 1f;
@@ -954,6 +959,9 @@
                     a = createAspectScaledThumbnailEnterFreeformAnimationLocked(
                             containingFrame, surfaceInsets, taskId);
                 } else {
+                    AnimationSet set = new AnimationSet(true);
+
+                    // In portrait, we scale to fit the width
                     mTmpFromClipRect.set(containingFrame);
                     mTmpToClipRect.set(containingFrame);
 
@@ -964,26 +972,40 @@
 
                     // Exclude insets region from the source clip.
                     mTmpFromClipRect.inset(contentInsets);
-
-                    // We scale the width and clip to the top/left square
-                    scale = thumbWidth / (appWidth - contentInsets.left - contentInsets.right);
-                    scaledTopDecor = (int) (scale * contentInsets.top);
-                    int unscaledThumbHeight = (int) (thumbHeight / scale);
-                    mTmpFromClipRect.bottom = mTmpFromClipRect.top + unscaledThumbHeight;
-
                     mNextAppTransitionInsets.set(contentInsets);
 
-                    Animation scaleAnim = new ScaleAnimation(scale, 1, scale, 1,
-                            computePivot(mTmpRect.left - containingFrame.left, scale),
-                            computePivot(mTmpRect.top - containingFrame.top, scale));
-                    Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
-                    Animation translateAnim = new TranslateAnimation(0, 0, -scaledTopDecor, 0);
+                    if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+                        // We scale the width and clip to the top/left square
+                        scale = thumbWidth / (appWidth - contentInsets.left - contentInsets.right);
+                        scaledTopDecor = (int) (scale * contentInsets.top);
+                        int unscaledThumbHeight = (int) (thumbHeight / scale);
+                        mTmpFromClipRect.bottom = mTmpFromClipRect.top + unscaledThumbHeight;
 
-                    AnimationSet set = new AnimationSet(true);
-                    set.addAnimation(clipAnim);
-                    set.addAnimation(scaleAnim);
-                    set.addAnimation(translateAnim);
+                        Animation scaleAnim = new ScaleAnimation(scale, 1, scale, 1,
+                                computePivot(mTmpRect.left - containingFrame.left, scale),
+                                computePivot(mTmpRect.top - containingFrame.top, scale));
+                        Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
+                        Animation translateAnim = new TranslateAnimation(0, 0, -scaledTopDecor, 0);
+
+                        set.addAnimation(clipAnim);
+                        set.addAnimation(scaleAnim);
+                        set.addAnimation(translateAnim);
+
+                    } else {
+                        // In landscape, we don't scale at all and only crop
+                        mTmpFromClipRect.bottom = mTmpFromClipRect.top + thumbHeightI;
+                        mTmpFromClipRect.right = mTmpFromClipRect.left + thumbWidthI;
+
+                        Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
+                        Animation translateAnim = new TranslateAnimation(thumbStartX, 0,
+                                thumbStartY - contentInsets.top, 0);
+
+                        set.addAnimation(clipAnim);
+                        set.addAnimation(translateAnim);
+                    }
+
                     a = set;
+                    a.setZAdjustment(Animation.ZORDER_TOP);
                 }
                 break;
             }
@@ -1015,6 +1037,7 @@
                     a = createAspectScaledThumbnailExitFreeformAnimationLocked(
                             containingFrame, surfaceInsets, taskId);
                 } else {
+                    AnimationSet set = new AnimationSet(true);
                     mTmpFromClipRect.set(containingFrame);
                     mTmpToClipRect.set(containingFrame);
 
@@ -1025,25 +1048,37 @@
 
                     // Exclude insets region from the target clip.
                     mTmpToClipRect.inset(contentInsets);
-
-                    // We scale the width and clip to the top/left square
-                    scale = thumbWidth / (appWidth - contentInsets.left - contentInsets.right);
-                    scaledTopDecor = (int) (scale * contentInsets.top);
-                    int unscaledThumbHeight = (int) (thumbHeight / scale);
-                    mTmpToClipRect.bottom = mTmpToClipRect.top + unscaledThumbHeight;
-
                     mNextAppTransitionInsets.set(contentInsets);
 
-                    Animation scaleAnim = new ScaleAnimation(1, scale, 1, scale,
-                            computePivot(mTmpRect.left - containingFrame.left, scale),
-                            computePivot(mTmpRect.top - containingFrame.top, scale));
-                    Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
-                    Animation translateAnim = new TranslateAnimation(0, 0, 0, -scaledTopDecor);
+                    if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+                        // We scale the width and clip to the top/left square
+                        scale = thumbWidth / (appWidth - contentInsets.left - contentInsets.right);
+                        scaledTopDecor = (int) (scale * contentInsets.top);
+                        int unscaledThumbHeight = (int) (thumbHeight / scale);
+                        mTmpToClipRect.bottom = mTmpToClipRect.top + unscaledThumbHeight;
 
-                    AnimationSet set = new AnimationSet(true);
-                    set.addAnimation(clipAnim);
-                    set.addAnimation(scaleAnim);
-                    set.addAnimation(translateAnim);
+                        Animation scaleAnim = new ScaleAnimation(1, scale, 1, scale,
+                                computePivot(mTmpRect.left - containingFrame.left, scale),
+                                computePivot(mTmpRect.top - containingFrame.top, scale));
+                        Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
+                        Animation translateAnim = new TranslateAnimation(0, 0, 0, -scaledTopDecor);
+
+                        set.addAnimation(clipAnim);
+                        set.addAnimation(scaleAnim);
+                        set.addAnimation(translateAnim);
+
+                    } else {
+                        // In landscape, we don't scale at all and only crop
+                        mTmpToClipRect.bottom = mTmpToClipRect.top + thumbHeightI;
+                        mTmpToClipRect.right = mTmpToClipRect.left + thumbWidthI;
+
+                        Animation clipAnim = new ClipRectAnimation(mTmpFromClipRect, mTmpToClipRect);
+                        Animation translateAnim = new TranslateAnimation(0, thumbStartX, 0,
+                                thumbStartY - contentInsets.top);
+
+                        set.addAnimation(clipAnim);
+                        set.addAnimation(translateAnim);
+                    }
 
                     a = set;
                     a.setZAdjustment(Animation.ZORDER_TOP);
@@ -1467,6 +1502,12 @@
         return a;
     }
 
+    int getAppStackClipMode() {
+        return mNextAppTransition == TRANSIT_ACTIVITY_RELAUNCH
+                ? STACK_CLIP_NONE
+                : STACK_CLIP_AFTER_ANIM;
+    }
+
     void postAnimationCallback() {
         if (mNextAppTransitionCallback != null) {
             mService.mH.sendMessage(mService.mH.obtainMessage(H.DO_ANIMATION_CALLBACK,
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index 3a5dec9..6225fc6 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -23,6 +23,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
+import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
 
 import android.graphics.Matrix;
 import android.util.Slog;
@@ -106,6 +107,7 @@
     boolean usingTransferredAnimation = false;
 
     private boolean mSkipFirstFrame = false;
+    private int mStackClip = STACK_CLIP_BEFORE_ANIM;
 
     static final Animation sDummyAnimation = new DummyAnimation();
 
@@ -115,7 +117,8 @@
         mAnimator = mService.mAnimator;
     }
 
-    public void setAnimation(Animation anim, int width, int height, boolean skipFirstFrame) {
+    public void setAnimation(Animation anim, int width, int height, boolean skipFirstFrame,
+            int stackClip) {
         if (WindowManagerService.localLOGV) Slog.v(TAG, "Setting animation in " + mAppToken
                 + ": " + anim + " wxh=" + width + "x" + height
                 + " isVisible=" + mAppToken.isVisible());
@@ -142,6 +145,7 @@
         transformation.clear();
         transformation.setAlpha(mAppToken.isVisible() ? 1 : 0);
         hasTransformation = true;
+        mStackClip = stackClip;
 
         this.mSkipFirstFrame = skipFirstFrame;
 
@@ -186,6 +190,7 @@
             mAppToken.allDrawn = false;
             mAppToken.deferClearAllDrawn = false;
         }
+        mStackClip = STACK_CLIP_BEFORE_ANIM;
     }
 
     public boolean isAnimating() {
@@ -201,6 +206,10 @@
         deferThumbnailDestruction = false;
     }
 
+    int getStackClip() {
+        return mStackClip;
+    }
+
     void transferCurrentAnimation(
             AppWindowAnimator toAppAnimator, WindowStateAnimator transferWinAnimator) {
 
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index f243761..eae7838 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -36,6 +36,8 @@
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
+import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
 import static com.android.server.wm.WindowSurfacePlacer.SET_FORCE_HIDING_CHANGED;
 import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
 import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION;
@@ -406,7 +408,8 @@
 
                             Animation a = mPolicy.createForceHideEnterAnimation(false,
                                     keyguardGoingAwayToShade);
-                            winAnimator.setAnimation(a, mPostKeyguardExitAnimation.getStartTime());
+                            winAnimator.setAnimation(a, mPostKeyguardExitAnimation.getStartTime(),
+                                    STACK_CLIP_BEFORE_ANIM);
                             winAnimator.mKeyguardGoingAwayAnimation = true;
                             winAnimator.mKeyguardGoingAwayWithWallpaper
                                     = keyguardGoingAwayWithWallpaper;
@@ -445,7 +448,7 @@
             }
 
             final AppWindowToken atoken = win.mAppToken;
-            if (winAnimator.mDrawState == WindowStateAnimator.READY_TO_SHOW) {
+            if (winAnimator.mDrawState == READY_TO_SHOW) {
                 if (atoken == null || atoken.allDrawn) {
                     if (winAnimator.performShowLocked()) {
                         setPendingLayoutChanges(displayId,
@@ -487,7 +490,7 @@
                     if (a != null) {
                         if (DEBUG_KEYGUARD) Slog.v(TAG,
                                 "Starting keyguard exit animation on window " + winAnimator.mWin);
-                        winAnimator.setAnimation(a);
+                        winAnimator.setAnimation(a, STACK_CLIP_BEFORE_ANIM);
                         winAnimator.mKeyguardGoingAwayAnimation = true;
                         winAnimator.mKeyguardGoingAwayWithWallpaper
                                 = keyguardGoingAwayWithWallpaper;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 6da0f62..be1d8067 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -236,6 +236,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_VERBOSE_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
 
 /** {@hide} */
 public class WindowManagerService extends IWindowManager.Stub
@@ -3039,7 +3040,7 @@
                 final int containingWidth = frame.width();
                 final int containingHeight = frame.height();
                 atoken.mAppAnimator.setAnimation(a, containingWidth, containingHeight,
-                        mAppTransition.canSkipFirstFrame());
+                        mAppTransition.canSkipFirstFrame(), mAppTransition.getAppStackClipMode());
             }
         } else {
             atoken.mAppAnimator.clearAnimation();
@@ -5735,7 +5736,7 @@
         if (mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_windowEnableCircularEmulatorDisplayOverlay)
                 && SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false)
-                && Build.HARDWARE.contains("goldfish")) {
+                && Build.IS_EMULATOR) {
             mH.sendMessage(mH.obtainMessage(H.SHOW_EMULATOR_DISPLAY_OVERLAY));
         }
     }
@@ -8973,7 +8974,7 @@
                                 + ", mDrawState=DRAW_PENDING in " + w
                                 + ", surfaceController " + winAnimator.mSurfaceController);
                     }
-                    winAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING;
+                    winAnimator.mDrawState = DRAW_PENDING;
                     if (w.mAppToken != null) {
                         w.mAppToken.allDrawn = false;
                         w.mAppToken.deferClearAllDrawn = false;
@@ -10787,7 +10788,7 @@
                     final boolean isForceHiding = mPolicy.isForceHiding(win.mAttrs);
                     if (win.isVisibleLw()
                             && (win.mAppToken != null || isForceHiding)) {
-                        win.mWinAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING;
+                        win.mWinAnimator.mDrawState = DRAW_PENDING;
                         // Force add to mResizingWindows.
                         win.mLastContentInsets.set(-1, -1, -1, -1);
                         mWaitingForDrawn.add(win);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 04bd7fc..38fda1b 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2528,7 +2528,7 @@
         final int ph = mContainingFrame.height();
         final Task task = getTask();
         final boolean nonFullscreenTask = inMultiWindowMode();
-        final boolean fitToDisplay = task != null && !task.isFloating();
+        final boolean fitToDisplay = task != null && !task.isFloating() && !layoutInParentFrame();
         float x, y;
         int w,h;
 
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 22d3f98..83b6104 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -75,6 +75,25 @@
     static final String TAG = TAG_WITH_CLASS_NAME ? "WindowStateAnimator" : TAG_WM;
     static final int WINDOW_FREEZE_LAYER = TYPE_LAYER_MULTIPLIER * 200;
 
+    /**
+     * Mode how the window gets clipped by the stack bounds during an animation: The clipping should
+     * be applied after applying the animation transformation, i.e. the stack bounds don't move
+     * during the animation.
+     */
+    static final int STACK_CLIP_AFTER_ANIM = 0;
+
+    /**
+     * Mode how the window gets clipped by the stack bounds: The clipping should be applied before
+     * applying the animation transformation, i.e. the stack bounds move with the window.
+     */
+    static final int STACK_CLIP_BEFORE_ANIM = 1;
+
+    /**
+     * Mode how window gets clipped by the stack bounds during an animation: Don't clip the window
+     * by the stack bounds.
+     */
+    static final int STACK_CLIP_NONE = 2;
+
     // Unchanging local convenience fields.
     final WindowManagerService mService;
     final WindowState mWin;
@@ -100,6 +119,7 @@
     int mLastLayer;
     long mAnimationStartTime;
     long mLastAnimationTime;
+    int mStackClip = STACK_CLIP_BEFORE_ANIM;
 
     /**
      * Set when we have changed the size of the surface, to know that
@@ -128,7 +148,9 @@
     boolean mHasClipRect;
     Rect mClipRect = new Rect();
     Rect mTmpClipRect = new Rect();
+    Rect mTmpFinalClipRect = new Rect();
     Rect mLastClipRect = new Rect();
+    Rect mLastFinalClipRect = new Rect();
     Rect mTmpStackBounds = new Rect();
 
     /**
@@ -226,7 +248,7 @@
         mWallpaperControllerLocked = mService.mWallpaperControllerLocked;
     }
 
-    public void setAnimation(Animation anim, long startTime) {
+    public void setAnimation(Animation anim, long startTime, int stackClip) {
         if (localLOGV) Slog.v(TAG, "Setting animation in " + this + ": " + anim);
         mAnimating = false;
         mLocalAnimating = false;
@@ -238,10 +260,15 @@
         mTransformation.setAlpha(mLastHidden ? 0 : 1);
         mHasLocalTransformation = true;
         mAnimationStartTime = startTime;
+        mStackClip = stackClip;
+    }
+
+    public void setAnimation(Animation anim, int stackClip) {
+        setAnimation(anim, -1, stackClip);
     }
 
     public void setAnimation(Animation anim) {
-        setAnimation(anim, -1);
+        setAnimation(anim, -1, STACK_CLIP_AFTER_ANIM);
     }
 
     public void clearAnimation() {
@@ -252,6 +279,7 @@
             mAnimation = null;
             mKeyguardGoingAwayAnimation = false;
             mKeyguardGoingAwayWithWallpaper = false;
+            mStackClip = STACK_CLIP_BEFORE_ANIM;
         }
     }
 
@@ -397,6 +425,7 @@
         if (DEBUG_LAYERS) Slog.v(TAG, "Stepping win " + this + " anim layer: " + mAnimLayer);
         mHasTransformation = false;
         mHasLocalTransformation = false;
+        mStackClip = STACK_CLIP_BEFORE_ANIM;
         mWin.checkPolicyVisibilityChange();
         mTransformation.clear();
         if (mDrawState == HAS_DRAWN
@@ -1130,11 +1159,13 @@
         }
     }
 
-    Rect calculateSurfaceWindowCrop() {
+    void calculateSurfaceWindowCrop(Rect clipRect, Rect finalClipRect) {
         final WindowState w = mWin;
         final DisplayContent displayContent = w.getDisplayContent();
         if (displayContent == null) {
-            return null;
+            clipRect.setEmpty();
+            finalClipRect.setEmpty();
+            return;
         }
         final DisplayInfo displayInfo = displayContent.getDisplayInfo();
         if (DEBUG_WINDOW_CROP) Slog.d(TAG, "Updating crop for window: " + w + ", " + "mLastCrop=" +
@@ -1170,7 +1201,6 @@
         final boolean fullscreen = w.isFrameFullscreen(displayInfo);
         final boolean isFreeformResizing =
                 w.isDragResizing() && w.getResizeMode() == DRAG_RESIZE_MODE_FREEFORM;
-        final Rect clipRect = mTmpClipRect;
 
         // We use the clip rect as provided by the tranformation for non-fullscreen windows to
         // avoid premature clipping with the system decor rect.
@@ -1201,7 +1231,8 @@
         // so we need to translate to match the actual surface coordinates.
         clipRect.offset(attrs.surfaceInsets.left, attrs.surfaceInsets.top);
 
-        adjustCropToStackBounds(w, clipRect, isFreeformResizing);
+        finalClipRect.setEmpty();
+        adjustCropToStackBounds(w, clipRect, finalClipRect, isFreeformResizing);
         if (DEBUG_WINDOW_CROP) Slog.d(TAG, "Clip rect after stack adjustment=" + clipRect);
 
         w.transformFromScreenToSurfaceSpace(clipRect);
@@ -1210,35 +1241,39 @@
         if (w.hasJustMovedInStack() && mLastClipRect.isEmpty() && !clipRect.isEmpty()) {
             clipRect.setEmpty();
         }
-
-        return clipRect;
     }
 
-    void updateSurfaceWindowCrop(Rect clipRect, boolean recoveringMemory) {
+    void updateSurfaceWindowCrop(Rect clipRect, Rect finalClipRect, boolean recoveringMemory) {
         if (!clipRect.equals(mLastClipRect)) {
             mLastClipRect.set(clipRect);
             mSurfaceController.setCropInTransaction(clipRect, recoveringMemory);
         }
+        if (!finalClipRect.equals(mLastFinalClipRect)) {
+            mLastFinalClipRect.set(finalClipRect);
+            mSurfaceController.setFinalCropInTransaction(finalClipRect);
+        }
     }
 
-    private void adjustCropToStackBounds(WindowState w, Rect clipRect, boolean isFreeformResizing) {
+    private int resolveStackClip() {
+
+        // App animation overrides window animation stack clip mode.
+        if (mAppAnimator != null && mAppAnimator.animation != null) {
+            return mAppAnimator.getStackClip();
+        } else {
+            return mStackClip;
+        }
+    }
+    private void adjustCropToStackBounds(WindowState w, Rect clipRect, Rect finalClipRect,
+            boolean isFreeformResizing) {
         final Task task = w.getTask();
         if (task == null || !task.cropWindowsToStackBounds()) {
             return;
         }
 
-        // We don't apply the stack bounds crop if:
-        // 1. The window is currently animating in freeform mode, otherwise the animating window
-        // will be suddenly (docked) or for whole animation (freeform) cut off.
-        // 2. The window that is being replaced during animation, because it was living in a
-        // different stack. If we suddenly crop it to the new stack bounds, it might get cut off.
-        // We don't want it to happen, so we let it ignore the stack bounds until it gets removed.
-        // The window that will replace it will abide them.
-        // TODO: identify animations where we don't want to apply docked stack crop to the docked
-        //       task. For example, if the app is going from freeform to docked mode, we may not
-        //       want to apply the crop during the animation, since it will make the app appear
-        //       cropped prematurely.
-        if (isAnimating() && (w.mWillReplaceWindow || w.inFreeformWorkspace())) {
+        final int stackClip = resolveStackClip();
+
+        // It's animating and we don't want to clip it to stack bounds during animation - abort.
+        if (isAnimating() && stackClip == STACK_CLIP_NONE) {
             return;
         }
 
@@ -1257,16 +1292,24 @@
         final int frameY = isFreeformResizing ? (int) mSurfaceController.getY() :
                 w.mFrame.top + mWin.mYOffset - w.getAttrs().surfaceInsets.top;
 
+        // If we are animating, we either apply the clip before applying all the animation
+        // transformation or after all the transformation.
+        final boolean useFinalClipRect = isAnimating() && stackClip == STACK_CLIP_AFTER_ANIM;
+
         // We need to do some acrobatics with surface position, because their clip region is
         // relative to the inside of the surface, but the stack bounds aren't.
-        clipRect.left = Math.max(0,
-                Math.max(mTmpStackBounds.left, frameX + clipRect.left) - frameX);
-        clipRect.top = Math.max(0,
-                Math.max(mTmpStackBounds.top, frameY + clipRect.top) - frameY);
-        clipRect.right = Math.max(0,
-                Math.min(mTmpStackBounds.right, frameX + clipRect.right) - frameX);
-        clipRect.bottom = Math.max(0,
-                Math.min(mTmpStackBounds.bottom, frameY + clipRect.bottom) - frameY);
+        if (useFinalClipRect) {
+            finalClipRect.set(mTmpStackBounds);
+        } else {
+            clipRect.left = Math.max(0,
+                    Math.max(mTmpStackBounds.left, frameX + clipRect.left) - frameX);
+            clipRect.top = Math.max(0,
+                    Math.max(mTmpStackBounds.top, frameY + clipRect.top) - frameY);
+            clipRect.right = Math.max(0,
+                    Math.min(mTmpStackBounds.right, frameX + clipRect.right) - frameX);
+            clipRect.bottom = Math.max(0,
+                    Math.min(mTmpStackBounds.bottom, frameY + clipRect.bottom) - frameY);
+        }
     }
 
     void setSurfaceBoundariesLocked(final boolean recoveringMemory) {
@@ -1279,10 +1322,10 @@
         float extraHScale = (float) 1.0;
         float extraVScale = (float) 1.0;
 
-        final Rect crop = calculateSurfaceWindowCrop();
+        calculateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect);
         if (task != null && task.mStack.getForceScaleToCrop()) {
-            extraHScale = crop.width() / (float)mTmpSize.width();
-            extraVScale = crop.height() / (float)mTmpSize.height();
+            extraHScale = mTmpClipRect.width() / (float)mTmpSize.width();
+            extraVScale = mTmpClipRect.height() / (float)mTmpSize.height();
 
             // In the case of ForceScaleToCrop we scale entire tasks together,
             // and so we need to scale our offsets relative to the task bounds
@@ -1296,12 +1339,13 @@
             // Since we are scaled to fit in our previously desired crop, we can now
             // expose the whole window in buffer space, and not risk extending
             // past where the system would have cropped us
-            crop.set(0, 0, mTmpSize.width(), mTmpSize.height());
-            updateSurfaceWindowCrop(crop, recoveringMemory);
+            mTmpClipRect.set(0, 0, mTmpSize.width(), mTmpSize.height());
+            mTmpFinalClipRect.setEmpty();
+            updateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect, recoveringMemory);
         } else {
             mSurfaceController.setPositionInTransaction(mTmpSize.left, mTmpSize.top,
                     recoveringMemory);
-            updateSurfaceWindowCrop(crop, recoveringMemory);
+            updateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect, recoveringMemory);
         }
 
 
@@ -1458,7 +1502,8 @@
             SurfaceControl.openTransaction();
             mSurfaceController.setPositionInTransaction(mWin.mFrame.left + left,
                     mWin.mFrame.top + top, false);
-            updateSurfaceWindowCrop(calculateSurfaceWindowCrop(), false);
+            calculateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect);
+            updateSurfaceWindowCrop(mTmpClipRect, mTmpFinalClipRect, false);
         } catch (RuntimeException e) {
             Slog.w(TAG, "Error positioning surface of " + mWin
                     + " pos=(" + left + "," + top + ")", e);
@@ -1721,6 +1766,7 @@
                     pw.print(" mLocalAnimating="); pw.print(mLocalAnimating);
                     pw.print(" mAnimationIsEntrance="); pw.print(mAnimationIsEntrance);
                     pw.print(" mAnimation="); pw.println(mAnimation);
+                    pw.print(" mStackClip="); pw.println(mStackClip);
         }
         if (mHasTransformation || mHasLocalTransformation) {
             pw.print(prefix); pw.print("XForm: has=");
@@ -1740,6 +1786,9 @@
             if (mHasClipRect) {
                 pw.print(" mLastClipRect="); mLastClipRect.printShortString(pw);
             }
+            if (!mLastFinalClipRect.isEmpty()) {
+                pw.print(" mLastFinalClipRect="); mLastFinalClipRect.printShortString(pw);
+            }
             pw.println();
         }
 
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index be3ad3b..8799c61 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -189,6 +189,16 @@
         }
     }
 
+    void setFinalCropInTransaction(Rect clipRect) {
+        if (SHOW_TRANSACTIONS) logSurface(
+                "FINAL CROP " + clipRect.toShortString(), null);
+        try {
+            mSurfaceControl.setFinalCrop(clipRect);
+        } catch (RuntimeException e) {
+            Slog.w(TAG, "Error disconnecting surface in: " + this, e);
+        }
+    }
+
     void setLayer(int layer) {
         if (mSurfaceControl != null) {
             SurfaceControl.openTransaction();
@@ -460,6 +470,7 @@
         private final PointF mPosition = new PointF();
         private final Point mSize = new Point();
         private final Rect mWindowCrop = new Rect();
+        private final Rect mFinalCrop = new Rect();
         private boolean mShown = false;
         private int mLayerStack;
         private boolean mIsOpaque;
@@ -545,6 +556,19 @@
         }
 
         @Override
+        public void setFinalCrop(Rect crop) {
+            if (crop != null) {
+                if (!crop.equals(mFinalCrop)) {
+                    if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setFinalCrop("
+                            + crop.toShortString() + "): OLD:" + this + ". Called by "
+                            + Debug.getCallers(3));
+                    mFinalCrop.set(crop);
+                }
+            }
+            super.setFinalCrop(crop);
+        }
+
+        @Override
         public void setLayerStack(int layerStack) {
             if (layerStack != mLayerStack) {
                 if (LOG_SURFACE_TRACE) Slog.v(SURFACE_TAG, "setLayerStack(" + layerStack + "): OLD:"
@@ -655,6 +679,7 @@
                             pw.print(" mSize="); pw.print(s.mSize.x); pw.print("x");
                             pw.println(s.mSize.y);
                     pw.print("    mCrop="); s.mWindowCrop.printShortString(pw); pw.println();
+                    pw.print("    mFinalCrop="); s.mFinalCrop.printShortString(pw); pw.println();
                     pw.print("    Transform: ("); pw.print(s.mDsdx); pw.print(", ");
                             pw.print(s.mDtdx); pw.print(", "); pw.print(s.mDsdy);
                             pw.print(", "); pw.print(s.mDtdy); pw.println(")");
diff --git a/services/net/java/android/net/apf/ApfCapabilities.java b/services/net/java/android/net/apf/ApfCapabilities.java
new file mode 100644
index 0000000..f169411
--- /dev/null
+++ b/services/net/java/android/net/apf/ApfCapabilities.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.apf;
+
+/**
+ * APF program support capabilities.
+ *
+ * @hide
+ */
+public class ApfCapabilities {
+    /**
+     * Version of APF instruction set supported for packet filtering. 0 indicates no support for
+     * packet filtering using APF programs.
+     */
+    public final int apfVersionSupported;
+
+    /**
+     * Maximum size of APF program allowed.
+     */
+    public final int maximumApfProgramSize;
+
+    /**
+     * Format of packets passed to APF filter. Should be one of ARPHRD_*
+     */
+    public final int apfPacketFormat;
+
+    ApfCapabilities(int apfVersionSupported, int maximumApfProgramSize, int apfPacketFormat) {
+        this.apfVersionSupported = apfVersionSupported;
+        this.maximumApfProgramSize = maximumApfProgramSize;
+        this.apfPacketFormat = apfPacketFormat;
+    }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/connectivity/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
similarity index 65%
rename from services/core/java/com/android/server/connectivity/ApfFilter.java
rename to services/net/java/android/net/apf/ApfFilter.java
index 824db65..ebbf991 100644
--- a/services/core/java/com/android/server/connectivity/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.connectivity;
+package android.net.apf;
 
 import static android.system.OsConstants.*;
 
@@ -22,15 +22,16 @@
 import android.net.apf.ApfGenerator;
 import android.net.apf.ApfGenerator.IllegalInstructionException;
 import android.net.apf.ApfGenerator.Register;
+import android.net.ip.IpManager;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.PacketSocketAddress;
 import android.util.Log;
 import android.util.Pair;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.HexDump;
 import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.ConnectivityService;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
@@ -52,6 +53,17 @@
  * listens for IPv6 ICMPv6 router advertisements (RAs) and generates APF programs to
  * filter out redundant duplicate ones.
  *
+ * Threading model:
+ * A collection of RAs we've received is kept in mRas. Generating APF programs uses mRas to
+ * know what RAs to filter for, thus generating APF programs is dependent on mRas.
+ * mRas can be accessed by multiple threads:
+ * - ReceiveThread, which listens for RAs and adds them to mRas, and generates APF programs.
+ * - callers of:
+ *    - setMulticastFilter(), which can cause an APF program to be generated.
+ *    - dump(), which dumps mRas among other things.
+ *    - shutdown(), which clears mRas.
+ * So access to mRas is synchronized.
+ *
  * @hide
  */
 public class ApfFilter {
@@ -93,23 +105,62 @@
     private static final boolean DBG = true;
     private static final boolean VDBG = false;
 
-    private final ConnectivityService mConnectivityService;
-    private final NetworkAgentInfo mNai;
-    private ReceiveThread mReceiveThread;
-    private String mIfaceName;
-    private long mUniqueCounter;
+    private static final int ETH_HEADER_LEN = 14;
+    private static final int ETH_ETHERTYPE_OFFSET = 12;
+    private static final byte[] ETH_BROADCAST_MAC_ADDRESS = new byte[]{
+            (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
+    // TODO: Make these offsets relative to end of link-layer header; don't include ETH_HEADER_LEN.
+    private static final int IPV4_FRAGMENT_OFFSET_OFFSET = ETH_HEADER_LEN + 6;
+    // Endianness is not an issue for this constant because the APF interpreter always operates in
+    // network byte order.
+    private static final int IPV4_FRAGMENT_OFFSET_MASK = 0x1fff;
+    private static final int IPV4_PROTOCOL_OFFSET = ETH_HEADER_LEN + 9;
+    private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16;
 
-    private ApfFilter(ConnectivityService connectivityService, NetworkAgentInfo nai) {
-        mConnectivityService = connectivityService;
-        mNai = nai;
+    private static final int IPV6_NEXT_HEADER_OFFSET = ETH_HEADER_LEN + 6;
+    private static final int IPV6_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 8;
+    private static final int IPV6_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 24;
+    private static final int IPV6_HEADER_LEN = 40;
+    // The IPv6 all nodes address ff02::1
+    private static final byte[] IPV6_ALL_NODES_ADDRESS =
+            new byte[]{ (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
+
+    private static final int ICMP6_TYPE_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN;
+    private static final int ICMP6_NEIGHBOR_ANNOUNCEMENT = 136;
+
+    // NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT
+    private static final int UDP_DESTINATION_PORT_OFFSET = ETH_HEADER_LEN + 2;
+    private static final int UDP_HEADER_LEN = 8;
+
+    private static final int DHCP_CLIENT_PORT = 68;
+    // NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT
+    private static final int DHCP_CLIENT_MAC_OFFSET = ETH_HEADER_LEN + UDP_HEADER_LEN + 28;
+
+    private final ApfCapabilities mApfCapabilities;
+    private final IpManager.Callback mIpManagerCallback;
+    private final NetworkInterface mNetworkInterface;
+    private byte[] mHardwareAddress;
+    private ReceiveThread mReceiveThread;
+    @GuardedBy("this")
+    private long mUniqueCounter;
+    @GuardedBy("this")
+    private boolean mMulticastFilter;
+
+    private ApfFilter(ApfCapabilities apfCapabilities, NetworkInterface networkInterface,
+            IpManager.Callback ipManagerCallback) {
+        mApfCapabilities = apfCapabilities;
+        mIpManagerCallback = ipManagerCallback;
+        mNetworkInterface = networkInterface;
+
         maybeStartFilter();
     }
 
     private void log(String s) {
-        Log.d(TAG, "(" + mNai.network.netId + "): " + s);
+        Log.d(TAG, "(" + mNetworkInterface.getName() + "): " + s);
     }
 
-    private long getUniqueNumber() {
+    @GuardedBy("this")
+    private long getUniqueNumberLocked() {
         return mUniqueCounter++;
     }
 
@@ -118,37 +169,26 @@
      * filters to ignore useless RAs.
      */
     private void maybeStartFilter() {
-        mIfaceName = mNai.linkProperties.getInterfaceName();
-        if (mIfaceName == null) return;
         FileDescriptor socket;
         try {
+            mHardwareAddress = mNetworkInterface.getHardwareAddress();
+            synchronized(this) {
+                // Install basic filters
+                installNewProgramLocked();
+            }
             socket = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IPV6);
             PacketSocketAddress addr = new PacketSocketAddress((short) ETH_P_IPV6,
-                    NetworkInterface.getByName(mIfaceName).getIndex());
+                    mNetworkInterface.getIndex());
             Os.bind(socket, addr);
-            NetworkUtils.attachRaFilter(socket, mNai.networkMisc.apfPacketFormat);
+            NetworkUtils.attachRaFilter(socket, mApfCapabilities.apfPacketFormat);
         } catch(SocketException|ErrnoException e) {
-            Log.e(TAG, "Error filtering raw socket", e);
+            Log.e(TAG, "Error starting filter", e);
             return;
         }
         mReceiveThread = new ReceiveThread(socket);
         mReceiveThread.start();
     }
 
-    /**
-     * mNai's LinkProperties may have changed, take appropriate action.
-     */
-    public void updateFilter() {
-        // If we're not listening for RAs, try starting.
-        if (mReceiveThread == null) {
-            maybeStartFilter();
-        // If interface name has changed, restart.
-        } else if (!mIfaceName.equals(mNai.linkProperties.getInterfaceName())) {
-            shutdown();
-            maybeStartFilter();
-        }
-    }
-
     // Returns seconds since Unix Epoch.
     private static long curTime() {
         return System.currentTimeMillis() / 1000L;
@@ -156,12 +196,6 @@
 
     // A class to hold information about an RA.
     private class Ra {
-        private static final int ETH_HEADER_LEN = 14;
-
-        private static final int IPV6_HEADER_LEN = 40;
-        private static final int IPV6_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 8;
-        private static final int IPV6_DST_ADDR_OFFSET = ETH_HEADER_LEN + 24;
-
         // From RFC4861:
         private static final int ICMP6_RA_HEADER_LEN = 16;
         private static final int ICMP6_RA_CHECKSUM_OFFSET =
@@ -250,7 +284,7 @@
                 StringBuffer sb = new StringBuffer();
                 sb.append(String.format("RA %s -> %s %d ",
                         IPv6AddresstoString(IPV6_SRC_ADDR_OFFSET),
-                        IPv6AddresstoString(IPV6_DST_ADDR_OFFSET),
+                        IPv6AddresstoString(IPV6_DEST_ADDR_OFFSET),
                         uint16(mPacket.getShort(ICMP6_RA_ROUTER_LIFETIME_OFFSET))));
                 for (int i: mPrefixOptionOffsets) {
                     String prefix = IPv6AddresstoString(i + 16);
@@ -302,7 +336,7 @@
                     ICMP6_RA_ROUTER_LIFETIME_OFFSET,
                     ICMP6_RA_ROUTER_LIFETIME_LEN);
 
-            // Parse ICMP6 options
+            // Parse ICMPv6 options
             mPrefixOptionOffsets = new ArrayList<>();
             mPacket.position(ICMP6_RA_OPTION_OFFSET);
             while (mPacket.hasRemaining()) {
@@ -394,13 +428,16 @@
         }
 
         boolean isExpired() {
-            return currentLifetime() < 0;
+            // TODO: We may want to handle 0 lifetime RAs differently, if they are common. We'll
+            // have to calculte the filter lifetime specially as a fraction of 0 is still 0.
+            return currentLifetime() <= 0;
         }
 
         // Append a filter for this RA to {@code gen}. Jump to DROP_LABEL if it should be dropped.
         // Jump to the next filter if packet doesn't match this RA.
-        long generateFilter(ApfGenerator gen) throws IllegalInstructionException {
-            String nextFilterLabel = "Ra" + getUniqueNumber();
+        @GuardedBy("ApfFilter.this")
+        long generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
+            String nextFilterLabel = "Ra" + getUniqueNumberLocked();
             // Skip if packet is not the right size
             gen.addLoadFromMemory(Register.R0, gen.PACKET_SIZE_MEMORY_SLOT);
             gen.addJumpIfR0NotEquals(mPacket.limit(), nextFilterLabel);
@@ -447,6 +484,8 @@
 
     // Maximum number of RAs to filter for.
     private static final int MAX_RAS = 10;
+
+    @GuardedBy("this")
     private ArrayList<Ra> mRas = new ArrayList<Ra>();
 
     // There is always some marginal benefit to updating the installed APF program when an RA is
@@ -460,42 +499,167 @@
     private static final int FRACTION_OF_LIFETIME_TO_FILTER = 6;
 
     // When did we last install a filter program? In seconds since Unix Epoch.
+    @GuardedBy("this")
     private long mLastTimeInstalledProgram;
     // How long should the last installed filter program live for? In seconds.
+    @GuardedBy("this")
     private long mLastInstalledProgramMinLifetime;
 
-    // For debugging only. The length in bytes of the last program.
+    // For debugging only. The last program installed.
+    @GuardedBy("this")
     private byte[] mLastInstalledProgram;
 
-    private void installNewProgram() {
-        if (mRas.size() == 0) return;
+    /**
+     * Generate filter code to process IPv4 packets. Execution of this code ends in either the
+     * DROP_LABEL or PASS_LABEL and does not fall off the end.
+     * Preconditions:
+     *  - Packet being filtered is IPv4
+     *  - R1 is initialized to 0
+     */
+    @GuardedBy("this")
+    private void generateIPv4FilterLocked(ApfGenerator gen) throws IllegalInstructionException {
+        // Here's a basic summary of what the IPv4 filter program does:
+        //
+        // if it's multicast and we're dropping multicast:
+        //   drop
+        // if it's not broadcast:
+        //   pass
+        // if it's not DHCP destined to our MAC:
+        //   drop
+        // pass
+
+        if (mMulticastFilter) {
+            // Check for multicast destination address range
+            gen.addLoad8(Register.R0, IPV4_DEST_ADDR_OFFSET);
+            gen.addAnd(0xf0);
+            gen.addJumpIfR0Equals(0xe0, gen.DROP_LABEL);
+        }
+
+        // Drop all broadcasts besides DHCP addressed to us
+        // If not a broadcast packet, pass
+        // NOTE: Relies on R1 being initialized to 0 which is the offset of the ethernet
+        //       destination MAC address
+        gen.addJumpIfBytesNotEqual(Register.R1, ETH_BROADCAST_MAC_ADDRESS, gen.PASS_LABEL);
+        // If not UDP, drop
+        gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET);
+        gen.addJumpIfR0NotEquals(IPPROTO_UDP, gen.DROP_LABEL);
+        // If fragment, drop. This matches the BPF filter installed by the DHCP client.
+        gen.addLoad16(Register.R0, IPV4_FRAGMENT_OFFSET_OFFSET);
+        gen.addJumpIfR0AnyBitsSet(IPV4_FRAGMENT_OFFSET_MASK, gen.DROP_LABEL);
+        // If not to DHCP client port, drop
+        gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
+        gen.addLoad16Indexed(Register.R0, UDP_DESTINATION_PORT_OFFSET);
+        gen.addJumpIfR0NotEquals(DHCP_CLIENT_PORT, gen.DROP_LABEL);
+        // If not DHCP to our MAC address, drop
+        gen.addLoadImmediate(Register.R0, DHCP_CLIENT_MAC_OFFSET);
+        // NOTE: Relies on R1 containing IPv4 header offset.
+        gen.addAddR1();
+        gen.addJumpIfBytesNotEqual(Register.R0, mHardwareAddress, gen.DROP_LABEL);
+
+        // Otherwise, pass
+        gen.addJump(gen.PASS_LABEL);
+    }
+
+
+    /**
+     * Generate filter code to process IPv6 packets. Execution of this code ends in either the
+     * DROP_LABEL or PASS_LABEL, or falls off the end for ICMPv6 packets.
+     * Preconditions:
+     *  - Packet being filtered is IPv6
+     *  - R1 is initialized to 0
+     */
+    @GuardedBy("this")
+    private void generateIPv6FilterLocked(ApfGenerator gen) throws IllegalInstructionException {
+        // Here's a basic summary of what the IPv6 filter program does:
+        //
+        // if it's not ICMPv6:
+        //   pass
+        // if it's ICMPv6 NA to ff02::1:
+        //   drop
+
+        // If not ICMPv6, pass
+        gen.addLoad8(Register.R0, IPV6_NEXT_HEADER_OFFSET);
+        // TODO: Drop multicast if the multicast filter is enabled.
+        gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, gen.PASS_LABEL);
+        // Add unsolicited multicast neighbor announcements filter
+        String skipUnsolicitedMulticastNALabel = "skipUnsolicitedMulticastNA";
+        // If not neighbor announcements, skip unsolicited multicast NA filter
+        gen.addLoad8(Register.R0, ICMP6_TYPE_OFFSET);
+        gen.addJumpIfR0NotEquals(ICMP6_NEIGHBOR_ANNOUNCEMENT, skipUnsolicitedMulticastNALabel);
+        // If to ff02::1, drop
+        // TODO: Drop only if they don't contain the address of on-link neighbours.
+        gen.addLoadImmediate(Register.R0, IPV6_DEST_ADDR_OFFSET);
+        gen.addJumpIfBytesNotEqual(Register.R0, IPV6_ALL_NODES_ADDRESS,
+                skipUnsolicitedMulticastNALabel);
+        gen.addJump(gen.DROP_LABEL);
+        gen.defineLabel(skipUnsolicitedMulticastNALabel);
+    }
+
+    /**
+     * Begin generating an APF program to:
+     * <ul>
+     * <li>Drop IPv4 broadcast packets, except DHCP destined to our MAC,
+     * <li>Drop IPv4 multicast packets, if mMulticastFilter,
+     * <li>Pass all other IPv4 packets,
+     * <li>Pass all non-ICMPv6 IPv6 packets,
+     * <li>Pass all non-IPv4 and non-IPv6 packets,
+     * <li>Drop IPv6 ICMPv6 NAs to ff02::1.
+     * <li>Let execution continue off the end of the program for IPv6 ICMPv6 packets. This allows
+     *     insertion of RA filters here, or if there aren't any, just passes the packets.
+     * </ul>
+     */
+    @GuardedBy("this")
+    private ApfGenerator beginProgramLocked() throws IllegalInstructionException {
+        ApfGenerator gen = new ApfGenerator();
+        // This is guaranteed to return true because of the check in maybeCreate.
+        gen.setApfVersion(mApfCapabilities.apfVersionSupported);
+
+        // Here's a basic summary of what the initial program does:
+        //
+        // if it's IPv4:
+        //   insert IPv4 filter to drop or pass these appropriately
+        // if it's not IPv6:
+        //   pass
+        // insert IPv6 filter to drop, pass, or fall off the end for ICMPv6 packets
+
+        // Add IPv4 filters:
+        String skipIPv4FiltersLabel = "skipIPv4Filters";
+        // If not IPv4, skip IPv4 filters
+        gen.addLoad16(Register.R0, ETH_ETHERTYPE_OFFSET);
+        gen.addJumpIfR0NotEquals(ETH_P_IP, skipIPv4FiltersLabel);
+        // NOTE: Relies on R1 being initialized to 0.
+        generateIPv4FilterLocked(gen);
+        gen.defineLabel(skipIPv4FiltersLabel);
+
+        // Add IPv6 filters:
+        // If not IPv6, pass
+        // NOTE: Relies on R0 containing ethertype. This is safe because if we got here, we did not
+        // execute the IPv4 filter, since that filter does not fall through, but either drops or
+        // passes.
+        gen.addJumpIfR0NotEquals(ETH_P_IPV6, gen.PASS_LABEL);
+        generateIPv6FilterLocked(gen);
+        return gen;
+    }
+
+    @GuardedBy("this")
+    private void installNewProgramLocked() {
+        purgeExpiredRasLocked();
         final byte[] program;
         long programMinLifetime = Long.MAX_VALUE;
         try {
-            ApfGenerator gen = new ApfGenerator();
-            // This is guaranteed to return true because of the check in maybeInstall.
-            gen.setApfVersion(mNai.networkMisc.apfVersionSupported);
             // Step 1: Determine how many RA filters we can fit in the program.
-            int ras = 0;
+            ApfGenerator gen = beginProgramLocked();
+            ArrayList<Ra> rasToFilter = new ArrayList<Ra>();
             for (Ra ra : mRas) {
-                if (ra.isExpired()) continue;
-                ra.generateFilter(gen);
-                if (gen.programLengthOverEstimate() > mNai.networkMisc.maximumApfProgramSize) {
-                    // We went too far.  Use prior number of RAs in "ras".
-                    break;
-                } else {
-                    // Yay! this RA filter fits, increment "ras".
-                    ras++;
-                }
+                ra.generateFilterLocked(gen);
+                // Stop if we get too big.
+                if (gen.programLengthOverEstimate() > mApfCapabilities.maximumApfProgramSize) break;
+                rasToFilter.add(ra);
             }
-            // Step 2: Generate RA filters
-            gen = new ApfGenerator();
-            // This is guaranteed to return true because of the check in maybeInstall.
-            gen.setApfVersion(mNai.networkMisc.apfVersionSupported);
-            for (Ra ra : mRas) {
-                if (ras-- == 0) break;
-                if (ra.isExpired()) continue;
-                programMinLifetime = Math.min(programMinLifetime, ra.generateFilter(gen));
+            // Step 2: Actually generate the program
+            gen = beginProgramLocked();
+            for (Ra ra : rasToFilter) {
+                programMinLifetime = Math.min(programMinLifetime, ra.generateFilterLocked(gen));
             }
             // Execution will reach the end of the program if no filters match, which will pass the
             // packet to the AP.
@@ -509,19 +673,18 @@
         mLastInstalledProgram = program;
         if (VDBG) {
             hexDump("Installing filter: ", program, program.length);
-        } else {
-            Log.d(TAG, "Installing filter length=" + program.length);
         }
-        mConnectivityService.pushApfProgramToNetwork(mNai, program);
+        mIpManagerCallback.installPacketFilter(program);
     }
 
     // Install a new filter program if the last installed one will die soon.
-    private void maybeInstallNewProgram() {
+    @GuardedBy("this")
+    private void maybeInstallNewProgramLocked() {
         if (mRas.size() == 0) return;
         // If the current program doesn't expire for a while, don't bother updating.
         long expiry = mLastTimeInstalledProgram + mLastInstalledProgramMinLifetime;
         if (expiry < curTime() + MAX_PROGRAM_LIFETIME_WORTH_REFRESHING) {
-            installNewProgram();
+            installNewProgramLocked();
         }
     }
 
@@ -529,7 +692,19 @@
         log(msg + HexDump.toHexString(packet, 0, length, false /* lowercase */));
     }
 
-    private void processRa(byte[] packet, int length) {
+    @GuardedBy("this")
+    private void purgeExpiredRasLocked() {
+        for (int i = 0; i < mRas.size();) {
+            if (mRas.get(i).isExpired()) {
+                log("Expiring " + mRas.get(i));
+                mRas.remove(i);
+            } else {
+                i++;
+            }
+        }
+    }
+
+    private synchronized void processRa(byte[] packet, int length) {
         if (VDBG) hexDump("Read packet = ", packet, length);
 
         // Have we seen this RA before?
@@ -551,66 +726,70 @@
                 // Swap to front of array.
                 mRas.add(0, mRas.remove(i));
 
-                maybeInstallNewProgram();
+                maybeInstallNewProgramLocked();
                 return;
             }
         }
-        // Purge expired RAs.
-        for (int i = 0; i < mRas.size();) {
-            if (mRas.get(i).isExpired()) {
-                log("Expired RA " + mRas.get(i));
-                mRas.remove(i);
-            } else {
-                i++;
-            }
-        }
+        purgeExpiredRasLocked();
         // TODO: figure out how to proceed when we've received more then MAX_RAS RAs.
         if (mRas.size() >= MAX_RAS) return;
+        final Ra ra;
         try {
-            Ra ra = new Ra(packet, length);
-            log("Adding " + ra);
-            mRas.add(ra);
+            ra = new Ra(packet, length);
         } catch (Exception e) {
             Log.e(TAG, "Error parsing RA: " + e);
             return;
         }
-        installNewProgram();
+        // Ignore 0 lifetime RAs.
+        if (ra.isExpired()) return;
+        log("Adding " + ra);
+        mRas.add(ra);
+        installNewProgramLocked();
     }
 
     /**
-     * Install an {@link ApfFilter} on {@code nai} if {@code nai} supports packet
+     * Create an {@link ApfFilter} if {@code apfCapabilities} indicates support for packet
      * filtering using APF programs.
      */
-    public static void maybeInstall(ConnectivityService connectivityService, NetworkAgentInfo nai) {
-        if (nai.networkMisc == null) return;
-        if (nai.networkMisc.apfVersionSupported == 0) return;
-        if (nai.networkMisc.maximumApfProgramSize < 512) {
-            Log.e(TAG, "Unacceptably small APF limit: " + nai.networkMisc.maximumApfProgramSize);
-            return;
+    public static ApfFilter maybeCreate(ApfCapabilities apfCapabilities,
+            NetworkInterface networkInterface, IpManager.Callback ipManagerCallback) {
+        if (apfCapabilities == null || networkInterface == null) return null;
+        if (apfCapabilities.apfVersionSupported == 0) return null;
+        if (apfCapabilities.maximumApfProgramSize < 512) {
+            Log.e(TAG, "Unacceptably small APF limit: " + apfCapabilities.maximumApfProgramSize);
+            return null;
         }
         // For now only support generating programs for Ethernet frames. If this restriction is
         // lifted:
         //   1. the program generator will need its offsets adjusted.
         //   2. the packet filter attached to our packet socket will need its offset adjusted.
-        if (nai.networkMisc.apfPacketFormat != ARPHRD_ETHER) return;
-        if (!new ApfGenerator().setApfVersion(nai.networkMisc.apfVersionSupported)) {
-            Log.e(TAG, "Unsupported APF version: " + nai.networkMisc.apfVersionSupported);
-            return;
+        if (apfCapabilities.apfPacketFormat != ARPHRD_ETHER) return null;
+        if (!new ApfGenerator().setApfVersion(apfCapabilities.apfVersionSupported)) {
+            Log.e(TAG, "Unsupported APF version: " + apfCapabilities.apfVersionSupported);
+            return null;
         }
-        nai.apfFilter = new ApfFilter(connectivityService, nai);
+        return new ApfFilter(apfCapabilities, networkInterface, ipManagerCallback);
     }
 
-    public void shutdown() {
+    public synchronized void shutdown() {
         if (mReceiveThread != null) {
             log("shutting down");
             mReceiveThread.halt();  // Also closes socket.
             mReceiveThread = null;
         }
+        mRas.clear();
     }
 
-    public void dump(IndentingPrintWriter pw) {
-        pw.println("APF version: " + mNai.networkMisc.apfVersionSupported);
-        pw.println("Max program size: " + mNai.networkMisc.maximumApfProgramSize);
+    public synchronized void setMulticastFilter(boolean enabled) {
+        if (mMulticastFilter != enabled) {
+            mMulticastFilter = enabled;
+            installNewProgramLocked();
+        }
+    }
+
+    public synchronized void dump(IndentingPrintWriter pw) {
+        pw.println("APF version: " + mApfCapabilities.apfVersionSupported);
+        pw.println("Max program size: " + mApfCapabilities.maximumApfProgramSize);
         pw.println("Receive thread: " + (mReceiveThread != null ? "RUNNING" : "STOPPED"));
         if (mLastTimeInstalledProgram == 0) {
             pw.println("No program installed.");
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index e2562cd..406dd56 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -30,6 +30,8 @@
 import android.net.InterfaceConfiguration;
 import android.net.LinkAddress;
 import android.net.NetworkUtils;
+import android.net.metrics.IpConnectivityEvent;
+import android.net.metrics.DhcpClientEvent;
 import android.os.IBinder;
 import android.os.INetworkManagementService;
 import android.os.Message;
@@ -136,7 +138,7 @@
             MessageUtils.findMessageNames(sMessageClasses);
 
     // DHCP parameters that we request.
-    private static final byte[] REQUESTED_PARAMS = new byte[] {
+    /* package */ static final byte[] REQUESTED_PARAMS = new byte[] {
         DHCP_SUBNET_MASK,
         DHCP_ROUTER,
         DHCP_DNS_SERVER,
@@ -146,6 +148,7 @@
         DHCP_LEASE_TIME,
         DHCP_RENEWAL_TIME,
         DHCP_REBINDING_TIME,
+        DHCP_VENDOR_INFO,
     };
 
     // DHCP flag that means "yes, we support unicast."
@@ -355,11 +358,15 @@
                     if (!stopped) {
                         Log.e(TAG, "Read error", e);
                     }
+                    DhcpClientEvent.logEvent(IpConnectivityEvent.IPCE_DHCP_RECV_ERROR,
+                            mIfaceName, e.getMessage());
                 } catch (DhcpPacket.ParseException e) {
                     Log.e(TAG, "Can't parse packet: " + e.getMessage());
                     if (PACKET_DBG) {
                         Log.d(TAG, HexDump.dumpHexString(mPacket, 0, length));
                     }
+                    DhcpClientEvent.logEvent(IpConnectivityEvent.IPCE_DHCP_PARSE_ERROR, mIfaceName,
+                            e.getMessage());
                 }
             }
             if (DBG) Log.d(TAG, "Receive thread stopped");
@@ -456,7 +463,9 @@
 
     abstract class LoggingState extends State {
         public void enter() {
-            if (STATE_DBG) Log.d(TAG, "Entering state " + getName());
+            String msg = "Entering state " + getName();
+            if (STATE_DBG) Log.d(TAG, msg);
+            DhcpClientEvent.logEvent(IpConnectivityEvent.IPCE_DHCP_STATE_CHANGE, mIfaceName, msg);
         }
 
         private String messageName(int what) {
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index c7c5015..66c7909 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -19,6 +19,8 @@
 import com.android.internal.util.MessageUtils;
 
 import android.content.Context;
+import android.net.apf.ApfCapabilities;
+import android.net.apf.ApfFilter;
 import android.net.DhcpResults;
 import android.net.InterfaceConfiguration;
 import android.net.LinkAddress;
@@ -38,10 +40,12 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
 import com.android.server.net.NetlinkTracker;
 
+import java.io.PrintWriter;
 import java.net.InetAddress;
 import java.net.NetworkInterface;
 import java.net.SocketException;
@@ -108,6 +112,9 @@
 
         // Called when the IpManager state machine terminates.
         public void onQuit() {}
+
+        // Install an APF program to filter incoming packets.
+        public void installPacketFilter(byte[] filter) {}
     }
 
     public static class WaitForProvisioningCallback extends Callback {
@@ -179,6 +186,11 @@
                 return this;
             }
 
+            public Builder withApfCapabilities(ApfCapabilities apfCapabilities) {
+                mConfig.mApfCapabilities = apfCapabilities;
+                return this;
+            }
+
             public ProvisioningConfiguration build() {
                 return new ProvisioningConfiguration(mConfig);
             }
@@ -187,6 +199,7 @@
         /* package */ boolean mUsingIpReachabilityMonitor = true;
         /* package */ boolean mRequestedPreDhcpAction;
         /* package */ StaticIpConfiguration mStaticIpConfig;
+        /* package */ ApfCapabilities mApfCapabilities;
 
         public ProvisioningConfiguration() {}
 
@@ -194,6 +207,7 @@
             mUsingIpReachabilityMonitor = other.mUsingIpReachabilityMonitor;
             mRequestedPreDhcpAction = other.mRequestedPreDhcpAction;
             mStaticIpConfig = other.mStaticIpConfig;
+            mApfCapabilities = other.mApfCapabilities;
         }
     }
 
@@ -229,7 +243,7 @@
     private final INetworkManagementService mNwService;
     private final NetlinkTracker mNetlinkTracker;
 
-    private int mInterfaceIndex;
+    private NetworkInterface mNetworkInterface;
 
     /**
      * Non-final member variables accessed only from within our StateMachine.
@@ -240,6 +254,7 @@
     private DhcpResults mDhcpResults;
     private String mTcpBufferSizes;
     private ProxyInfo mHttpProxy;
+    private ApfFilter mApfFilter;
 
     /**
      * Member variables accessed both from within the StateMachine thread
@@ -325,7 +340,7 @@
     }
 
     public void startProvisioning(ProvisioningConfiguration req) {
-        getInterfaceIndex();
+        getNetworkInterface();
         sendMessage(CMD_START, new ProvisioningConfiguration(req));
     }
 
@@ -378,6 +393,19 @@
         }
     }
 
+    public void dumpApf(PrintWriter writer) {
+        writer.println("--------------------------------------------------------------------");
+        writer.println("APF dump:");
+        // Thread-unsafe access to mApfFilter but just used for debugging.
+        ApfFilter apfFilter = mApfFilter;
+        if (apfFilter != null) {
+            apfFilter.dump(new IndentingPrintWriter(writer, "  "));
+        } else {
+            writer.println("No apf support");
+        }
+        writer.println("--------------------------------------------------------------------");
+    }
+
 
     /**
      * Internals.
@@ -392,7 +420,7 @@
     protected String getLogRecString(Message msg) {
         final String logLine = String.format(
                 "iface{%s/%d} arg1{%d} arg2{%d} obj{%s}",
-                mInterfaceName, mInterfaceIndex,
+                mInterfaceName, mNetworkInterface == null ? -1 : mNetworkInterface.getIndex(),
                 msg.arg1, msg.arg2, Objects.toString(msg.obj));
         if (VDBG) {
             Log.d(mTag, getWhatToString(msg.what) + " " + logLine);
@@ -400,12 +428,12 @@
         return logLine;
     }
 
-    private void getInterfaceIndex() {
+    private void getNetworkInterface() {
         try {
-            mInterfaceIndex = NetworkInterface.getByName(mInterfaceName).getIndex();
+            mNetworkInterface = NetworkInterface.getByName(mInterfaceName);
         } catch (SocketException | NullPointerException e) {
             // TODO: throw new IllegalStateException.
-            Log.e(mTag, "ALERT: Failed to get interface index: ", e);
+            Log.e(mTag, "ALERT: Failed to get interface object: ", e);
         }
     }
 
@@ -732,6 +760,8 @@
     class StartedState extends State {
         @Override
         public void enter() {
+            mApfFilter = ApfFilter.maybeCreate(mConfiguration.mApfCapabilities, mNetworkInterface,
+                    mCallback);
             // Set privacy extensions.
             try {
                 mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
@@ -788,6 +818,11 @@
                 mDhcpClient.doQuit();
             }
 
+            if (mApfFilter != null) {
+                mApfFilter.shutdown();
+                mApfFilter = null;
+            }
+
             resetLinkProperties();
         }
 
diff --git a/services/net/java/android/net/ip/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java
index 5b4fd50..af3175a 100644
--- a/services/net/java/android/net/ip/IpReachabilityMonitor.java
+++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java
@@ -24,6 +24,8 @@
 import android.net.LinkProperties.ProvisioningChange;
 import android.net.ProxyInfo;
 import android.net.RouteInfo;
+import android.net.metrics.IpReachabilityMonitorMessageEvent;
+import android.net.metrics.IpReachabilityMonitorProbeEvent;
 import android.net.netlink.NetlinkConstants;
 import android.net.netlink.NetlinkErrorMessage;
 import android.net.netlink.NetlinkMessage;
@@ -162,7 +164,7 @@
     private boolean mRunning;
 
     /**
-     * Make the kernel to perform neighbor reachability detection (IPv4 ARP or IPv6 ND)
+     * Make the kernel perform neighbor reachability detection (IPv4 ARP or IPv6 ND)
      * for the given IP address on the specified interface index.
      *
      * @return true, if the request was successfully passed to the kernel; false otherwise.
@@ -203,7 +205,8 @@
         } catch (ErrnoException | InterruptedIOException | SocketException e) {
             Log.d(TAG, "Error " + msgSnippet, e);
         }
-
+        IpReachabilityMonitorProbeEvent.logEvent("ifindex-" + ifIndex, ip.getHostAddress(),
+                returnValue);
         return returnValue;
     }
 
@@ -400,8 +403,7 @@
         return (numUnicastProbes * retransTimeMs) + gracePeriodMs;
     }
 
-
-    // TODO: simply the number of objects by making this extend Thread.
+    // TODO: simplify the number of objects by making this extend Thread.
     private final class NetlinkSocketObserver implements Runnable {
         private NetlinkSocket mSocket;
 
@@ -519,6 +521,8 @@
 
             final short msgType = neighMsg.getHeader().nlmsg_type;
             final short nudState = ndMsg.ndm_state;
+            IpReachabilityMonitorMessageEvent.logEvent(maybeGetInterfaceName(mInterfaceIndex),
+                    destination.getHostAddress(), msgType, nudState);
             final String eventMsg = "NeighborEvent{"
                     + "elapsedMs=" + whenMs + ", "
                     + destination.getHostAddress() + ", "
@@ -549,4 +553,11 @@
             }
         }
     }
+
+    private String maybeGetInterfaceName(int index) {
+        if (index == mInterfaceIndex) {
+            return mInterfaceName;
+        }
+        return "ifindex-" + index;
+    }
 }
diff --git a/services/net/java/android/net/metrics/CaptivePortalCheckResultEvent.java b/services/net/java/android/net/metrics/CaptivePortalCheckResultEvent.java
new file mode 100644
index 0000000..163f7e40
--- /dev/null
+++ b/services/net/java/android/net/metrics/CaptivePortalCheckResultEvent.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.metrics;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class CaptivePortalCheckResultEvent extends IpConnectivityEvent implements Parcelable {
+    public static final String TAG = "CaptivePortalCheckResultEvent";
+
+    private int mNetId;
+    private int mResult;
+
+    public CaptivePortalCheckResultEvent(int netId, int result) {
+        mNetId = netId;
+        mResult = result;
+    }
+
+    public CaptivePortalCheckResultEvent(Parcel in) {
+        mNetId = in.readInt();
+        mResult = in.readInt();
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mNetId);
+        out.writeInt(mResult);
+    }
+
+    public static final Parcelable.Creator<CaptivePortalCheckResultEvent> CREATOR
+        = new Parcelable.Creator<CaptivePortalCheckResultEvent>() {
+            public CaptivePortalCheckResultEvent createFromParcel(Parcel in) {
+                return new CaptivePortalCheckResultEvent(in);
+            }
+
+            public CaptivePortalCheckResultEvent[] newArray(int size) {
+                return new CaptivePortalCheckResultEvent[size];
+            }
+        };
+
+    public static void logEvent(int netId, int result) {
+        IpConnectivityEvent.logEvent(IpConnectivityEvent.IPCE_NETMON_CHECK_RESULT,
+                new CaptivePortalCheckResultEvent(netId, result));
+    }
+};
diff --git a/services/net/java/android/net/metrics/CaptivePortalStateChangeEvent.java b/services/net/java/android/net/metrics/CaptivePortalStateChangeEvent.java
new file mode 100644
index 0000000..d0cc120
--- /dev/null
+++ b/services/net/java/android/net/metrics/CaptivePortalStateChangeEvent.java
@@ -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.
+ */
+
+package android.net.metrics;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class CaptivePortalStateChangeEvent extends IpConnectivityEvent implements Parcelable {
+    public static final String TAG = "CaptivePortalStateChangeEvent";
+
+    public static final int NETWORK_MONITOR_CONNECTED = 0;
+    public static final int NETWORK_MONITOR_DISCONNECTED = 1;
+    public static final int NETWORK_MONITOR_VALIDATED = 2;
+    private int mState;
+
+    public CaptivePortalStateChangeEvent(int state) {
+        mState = state;
+    }
+
+    public CaptivePortalStateChangeEvent(Parcel in) {
+        mState = in.readInt();
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mState);
+    }
+
+    public static final Parcelable.Creator<CaptivePortalStateChangeEvent> CREATOR
+        = new Parcelable.Creator<CaptivePortalStateChangeEvent>() {
+        public CaptivePortalStateChangeEvent createFromParcel(Parcel in) {
+            return new CaptivePortalStateChangeEvent(in);
+        }
+
+        public CaptivePortalStateChangeEvent[] newArray(int size) {
+            return new CaptivePortalStateChangeEvent[size];
+        }
+    };
+
+    public static void logEvent(int state) {
+        IpConnectivityEvent.logEvent(IpConnectivityEvent.IPCE_NETMON_STATE_CHANGE,
+                new CaptivePortalStateChangeEvent(state));
+    }
+};
diff --git a/services/net/java/android/net/metrics/ConnectivityServiceChangeEvent.java b/services/net/java/android/net/metrics/ConnectivityServiceChangeEvent.java
new file mode 100644
index 0000000..92b376c
--- /dev/null
+++ b/services/net/java/android/net/metrics/ConnectivityServiceChangeEvent.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.metrics;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class ConnectivityServiceChangeEvent extends IpConnectivityEvent implements Parcelable {
+    public static final String TAG = "ConnectivityServiceChangeEvent";
+
+    private int mNetId;
+
+    public ConnectivityServiceChangeEvent(int netId) {
+        mNetId = netId;
+    }
+
+    public ConnectivityServiceChangeEvent(Parcel in) {
+        mNetId = in.readInt();
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mNetId);
+    }
+
+    public static final Parcelable.Creator<ConnectivityServiceChangeEvent> CREATOR
+        = new Parcelable.Creator<ConnectivityServiceChangeEvent>() {
+        public ConnectivityServiceChangeEvent createFromParcel(Parcel in) {
+            return new ConnectivityServiceChangeEvent(in);
+        }
+
+        public ConnectivityServiceChangeEvent[] newArray(int size) {
+            return new ConnectivityServiceChangeEvent[size];
+        }
+    };
+
+    public static void logEvent(int netId) {
+        IpConnectivityEvent.logEvent(IpConnectivityEvent.IPCE_CONSRV_DEFAULT_NET_CHANGE,
+                new ConnectivityServiceChangeEvent(netId));
+    }
+};
diff --git a/services/net/java/android/net/metrics/DhcpClientEvent.java b/services/net/java/android/net/metrics/DhcpClientEvent.java
new file mode 100644
index 0000000..2c24034
--- /dev/null
+++ b/services/net/java/android/net/metrics/DhcpClientEvent.java
@@ -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.
+ */
+
+package android.net.metrics;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class DhcpClientEvent extends IpConnectivityEvent implements Parcelable {
+    public static final String TAG = "DhcpClientEvent";
+
+    private String mIfName;
+    private String mMsg;
+
+    public DhcpClientEvent(String ifName, String msg) {
+        mIfName = ifName;
+        mMsg = msg;
+    }
+
+    public DhcpClientEvent(Parcel in) {
+        mIfName = in.readString();
+        mMsg = in.readString();
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(mIfName);
+        out.writeString(mMsg);
+    }
+
+    public static final Parcelable.Creator<DhcpClientEvent> CREATOR
+        = new Parcelable.Creator<DhcpClientEvent>() {
+        public DhcpClientEvent createFromParcel(Parcel in) {
+            return new DhcpClientEvent(in);
+        }
+
+        public DhcpClientEvent[] newArray(int size) {
+            return new DhcpClientEvent[size];
+        }
+    };
+
+    public static void logEvent(int eventType, String ifName, String msg) {
+        IpConnectivityEvent.logEvent(eventType, new DhcpClientEvent(ifName, msg));
+    }
+};
diff --git a/services/net/java/android/net/metrics/IpConnectivityEvent.java b/services/net/java/android/net/metrics/IpConnectivityEvent.java
new file mode 100644
index 0000000..f277bd0
--- /dev/null
+++ b/services/net/java/android/net/metrics/IpConnectivityEvent.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.metrics;
+
+import android.net.ConnectivityMetricsLogger;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class IpConnectivityEvent implements Parcelable {
+    // IPRM = IpReachabilityMonitor
+    // DHCP = DhcpClient
+    // NETMON = NetworkMonitorEvent
+    // CONSRV = ConnectivityServiceEvent
+    public static final String TAG = "IpConnectivityEvent";
+    public static final int IPCE_IPRM_BASE = 0*1024;
+    public static final int IPCE_DHCP_BASE = 1*1024;
+    public static final int IPCE_NETMON_BASE = 2*1024;
+    public static final int IPCE_CONSRV_BASE = 3*1024;
+
+    public static final int IPCE_IPRM_PROBE_RESULT = IPCE_IPRM_BASE + 0;
+    public static final int IPCE_IPRM_MESSAGE_RECEIVED = IPCE_IPRM_BASE + 1;
+    public static final int IPCE_DHCP_RECV_ERROR = IPCE_DHCP_BASE + 0;
+    public static final int IPCE_DHCP_PARSE_ERROR = IPCE_DHCP_BASE + 1;
+    public static final int IPCE_DHCP_TIMEOUT = IPCE_DHCP_BASE + 2;
+    public static final int IPCE_DHCP_STATE_CHANGE = IPCE_DHCP_BASE + 3;
+    public static final int IPCE_NETMON_STATE_CHANGE = IPCE_NETMON_BASE + 0;
+    public static final int IPCE_NETMON_CHECK_RESULT = IPCE_NETMON_BASE + 1;
+    public static final int IPCE_CONSRV_DEFAULT_NET_CHANGE = IPCE_CONSRV_BASE + 0;
+
+    private static ConnectivityMetricsLogger mMetricsLogger = new ConnectivityMetricsLogger();
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+    }
+
+    public static void logEvent(int tag, IpConnectivityEvent event) {
+        long timestamp = System.currentTimeMillis();
+        mMetricsLogger.logEvent(timestamp, ConnectivityMetricsLogger.COMPONENT_TAG_CONNECTIVITY,
+                tag, event);
+    }
+};
diff --git a/services/net/java/android/net/metrics/IpReachabilityMonitorMessageEvent.java b/services/net/java/android/net/metrics/IpReachabilityMonitorMessageEvent.java
new file mode 100644
index 0000000..a8c18d6
--- /dev/null
+++ b/services/net/java/android/net/metrics/IpReachabilityMonitorMessageEvent.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.metrics;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class IpReachabilityMonitorMessageEvent extends IpConnectivityEvent
+    implements Parcelable {
+    public static final String TAG = "IpReachabilityMonitorMessageEvent";
+
+    private String mIfName;
+    private String mDestination;
+    private int mMsgType;
+    private int mNudState;
+
+    public IpReachabilityMonitorMessageEvent(String ifName, String destination, int msgType,
+            int nudState) {
+        mIfName = ifName;
+        mDestination = destination;
+        mMsgType = msgType;
+        mNudState = nudState;
+    }
+
+    public IpReachabilityMonitorMessageEvent(Parcel in) {
+        mIfName = in.readString();
+        mDestination = in.readString();
+        mMsgType = in.readInt();
+        mNudState = in.readInt();
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(mIfName);
+        out.writeString(mDestination);
+        out.writeInt(mMsgType);
+        out.writeInt(mNudState);
+    }
+
+    public static final Parcelable.Creator<IpReachabilityMonitorMessageEvent> CREATOR
+        = new Parcelable.Creator<IpReachabilityMonitorMessageEvent>() {
+        public IpReachabilityMonitorMessageEvent createFromParcel(Parcel in) {
+            return new IpReachabilityMonitorMessageEvent(in);
+        }
+
+        public IpReachabilityMonitorMessageEvent[] newArray(int size) {
+            return new IpReachabilityMonitorMessageEvent[size];
+        }
+    };
+
+    public static void logEvent(String ifName, String destination, int msgType, int nudState) {
+        IpConnectivityEvent.logEvent(IpConnectivityEvent.IPCE_IPRM_MESSAGE_RECEIVED,
+                new IpReachabilityMonitorMessageEvent(ifName, destination, msgType, nudState));
+    }
+};
diff --git a/services/net/java/android/net/metrics/IpReachabilityMonitorProbeEvent.java b/services/net/java/android/net/metrics/IpReachabilityMonitorProbeEvent.java
new file mode 100644
index 0000000..172cbf8
--- /dev/null
+++ b/services/net/java/android/net/metrics/IpReachabilityMonitorProbeEvent.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.metrics;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class IpReachabilityMonitorProbeEvent extends IpConnectivityEvent
+    implements Parcelable {
+    public static final String TAG = "IpReachabilityMonitorProbeEvent";
+
+    private String mIfName;
+    private String mDestination;
+    private boolean mSuccess;
+
+    public IpReachabilityMonitorProbeEvent(String ifName, String destination, boolean success) {
+        mIfName = ifName;
+        mDestination = destination;
+        mSuccess = success;
+    }
+
+    public IpReachabilityMonitorProbeEvent(Parcel in) {
+        mIfName = in.readString();
+        mDestination = in.readString();
+        mSuccess = in.readByte() > 0 ? true : false;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(mIfName);
+        out.writeString(mDestination);
+        out.writeByte((byte)(mSuccess ? 1 : 0));
+    }
+
+    public static final Parcelable.Creator<IpReachabilityMonitorProbeEvent> CREATOR
+        = new Parcelable.Creator<IpReachabilityMonitorProbeEvent>() {
+        public IpReachabilityMonitorProbeEvent createFromParcel(Parcel in) {
+            return new IpReachabilityMonitorProbeEvent(in);
+        }
+
+        public IpReachabilityMonitorProbeEvent[] newArray(int size) {
+            return new IpReachabilityMonitorProbeEvent[size];
+        }
+    };
+
+    public static void logEvent(String ifName, String destination, boolean success) {
+        IpConnectivityEvent.logEvent(IpConnectivityEvent.IPCE_IPRM_PROBE_RESULT,
+                new IpReachabilityMonitorProbeEvent(ifName, destination, success));
+    }
+};
diff --git a/services/print/java/com/android/server/print/RemotePrintService.java b/services/print/java/com/android/server/print/RemotePrintService.java
index 9b99c67..9c3a852 100644
--- a/services/print/java/com/android/server/print/RemotePrintService.java
+++ b/services/print/java/com/android/server/print/RemotePrintService.java
@@ -19,6 +19,7 @@
 import android.annotation.FloatRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.StringRes;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -813,6 +814,20 @@
         }
 
         @Override
+        public void setStatusRes(@NonNull PrintJobId printJobId, @StringRes int status,
+                @NonNull CharSequence appPackageName) {
+            RemotePrintService service = mWeakService.get();
+            if (service != null) {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    service.mSpooler.setStatus(printJobId, status, appPackageName);
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+        }
+
+        @Override
         @SuppressWarnings({"rawtypes", "unchecked"})
         public void onPrintersAdded(ParceledListSlice printers) {
             RemotePrintService service = mWeakService.get();
diff --git a/services/print/java/com/android/server/print/RemotePrintSpooler.java b/services/print/java/com/android/server/print/RemotePrintSpooler.java
index e1d8c6c..be822c8 100644
--- a/services/print/java/com/android/server/print/RemotePrintSpooler.java
+++ b/services/print/java/com/android/server/print/RemotePrintSpooler.java
@@ -19,6 +19,7 @@
 import android.annotation.FloatRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.StringRes;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -301,6 +302,35 @@
     }
 
     /**
+     * Set status of a print job.
+     *
+     * @param printJobId The print job to update
+     * @param status The new status as a string resource
+     * @param appPackageName The app package name the string res belongs to
+     */
+    public final void setStatus(@NonNull PrintJobId printJobId, @StringRes int status,
+            @NonNull CharSequence appPackageName) {
+        throwIfCalledOnMainThread();
+        synchronized (mLock) {
+            throwIfDestroyedLocked();
+            mCanUnbind = false;
+        }
+        try {
+            getRemoteInstanceLazy().setStatusRes(printJobId, status, appPackageName);
+        } catch (RemoteException|TimeoutException re) {
+            Slog.e(LOG_TAG, "Error setting status.", re);
+        } finally {
+            if (DEBUG) {
+                Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] setStatus()");
+            }
+            synchronized (mLock) {
+                mCanUnbind = true;
+                mLock.notifyAll();
+            }
+        }
+    }
+
+    /**
      * Handle that a custom icon for a printer was loaded.
      *
      * @param printerId the id of the printer the icon belongs to
diff --git a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
index c322ab8..876d95b 100644
--- a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
+++ b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
@@ -631,18 +631,10 @@
         byte[] hwaddr = {
                 (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a
         };
-        byte[] params = new byte[] {
-            DHCP_SUBNET_MASK,
-            DHCP_ROUTER,
-            DHCP_DNS_SERVER,
-            DHCP_DOMAIN_NAME,
-            DHCP_MTU,
-            DHCP_LEASE_TIME,
-        };
 
         ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
                 DhcpPacket.ENCAP_L2, transactionId, secs, hwaddr,
-                false /* do unicast */, params);
+                false /* do unicast */, DhcpClient.REQUESTED_PARAMS);
 
         byte[] headers = new byte[] {
             // Ethernet header.
@@ -650,14 +642,14 @@
             (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a,
             (byte) 0x08, (byte) 0x00,
             // IP header.
-            (byte) 0x45, (byte) 0x10, (byte) 0x01, (byte) 0x52,
+            (byte) 0x45, (byte) 0x10, (byte) 0x01, (byte) 0x56,
             (byte) 0x00, (byte) 0x00, (byte) 0x40, (byte) 0x00,
-            (byte) 0x40, (byte) 0x11, (byte) 0x39, (byte) 0x8c,
+            (byte) 0x40, (byte) 0x11, (byte) 0x39, (byte) 0x88,
             (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
             (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
             // UDP header.
             (byte) 0x00, (byte) 0x44, (byte) 0x00, (byte) 0x43,
-            (byte) 0x01, (byte) 0x3e, (byte) 0xd8, (byte) 0xa4,
+            (byte) 0x01, (byte) 0x42, (byte) 0x6a, (byte) 0x4a,
             // BOOTP.
             (byte) 0x01, (byte) 0x01, (byte) 0x06, (byte) 0x00,
             (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef,
@@ -688,13 +680,17 @@
                     'a', 'n', 'd', 'r', 'o', 'i', 'd', '-',
                     '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e',
             // Requested parameter list.
-            (byte) 0x37, (byte) 0x06,
+            (byte) 0x37, (byte) 0x0a,
                 DHCP_SUBNET_MASK,
                 DHCP_ROUTER,
                 DHCP_DNS_SERVER,
                 DHCP_DOMAIN_NAME,
                 DHCP_MTU,
+                DHCP_BROADCAST_ADDRESS,
                 DHCP_LEASE_TIME,
+                DHCP_RENEWAL_TIME,
+                DHCP_REBINDING_TIME,
+                DHCP_VENDOR_INFO,
             // End options.
             (byte) 0xff,
             // Our packets are always of even length. TODO: find out why and possibly fix it.
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 72a458b..622e46e 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -313,7 +313,7 @@
     public void testScreenChangesRules() throws Exception {
         Future<Void> future;
 
-        expectSetUidNetworkRules(UID_A, false);
+        expectSetUidMeteredNetworkBlacklist(UID_A, false);
         expectSetUidForeground(UID_A, true);
         future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
         replay();
@@ -322,7 +322,7 @@
         verifyAndReset();
 
         // push strict policy for foreground uid, verify ALLOW rule
-        expectSetUidNetworkRules(UID_A, false);
+        expectSetUidMeteredNetworkBlacklist(UID_A, false);
         expectSetUidForeground(UID_A, true);
         future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
         replay();
@@ -332,7 +332,7 @@
 
         // now turn screen off and verify REJECT rule
         expect(mPowerManager.isInteractive()).andReturn(false).atLeastOnce();
-        expectSetUidNetworkRules(UID_A, true);
+        expectSetUidMeteredNetworkBlacklist(UID_A, true);
         expectSetUidForeground(UID_A, false);
         future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
         replay();
@@ -342,7 +342,7 @@
 
         // and turn screen back on, verify ALLOW rule restored
         expect(mPowerManager.isInteractive()).andReturn(true).atLeastOnce();
-        expectSetUidNetworkRules(UID_A, false);
+        expectSetUidMeteredNetworkBlacklist(UID_A, false);
         expectSetUidForeground(UID_A, true);
         future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
         replay();
@@ -354,7 +354,7 @@
     public void testPolicyNone() throws Exception {
         Future<Void> future;
 
-        expectSetUidNetworkRules(UID_A, false);
+        expectSetUidMeteredNetworkBlacklist(UID_A, false);
         expectSetUidForeground(UID_A, true);
         future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
         replay();
@@ -363,7 +363,7 @@
         verifyAndReset();
 
         // POLICY_NONE should RULE_ALLOW in foreground
-        expectSetUidNetworkRules(UID_A, false);
+        expectSetUidMeteredNetworkBlacklist(UID_A, false);
         expectSetUidForeground(UID_A, true);
         future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
         replay();
@@ -372,7 +372,7 @@
         verifyAndReset();
 
         // POLICY_NONE should RULE_ALLOW in background
-        expectSetUidNetworkRules(UID_A, false);
+        expectSetUidMeteredNetworkBlacklist(UID_A, false);
         expectSetUidForeground(UID_A, false);
         future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
         replay();
@@ -385,7 +385,7 @@
         Future<Void> future;
 
         // POLICY_REJECT should RULE_ALLOW in background
-        expectSetUidNetworkRules(UID_A, true);
+        expectSetUidMeteredNetworkBlacklist(UID_A, true);
         expectSetUidForeground(UID_A, false);
         future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
         replay();
@@ -394,7 +394,7 @@
         verifyAndReset();
 
         // POLICY_REJECT should RULE_ALLOW in foreground
-        expectSetUidNetworkRules(UID_A, false);
+        expectSetUidMeteredNetworkBlacklist(UID_A, false);
         expectSetUidForeground(UID_A, true);
         future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
         replay();
@@ -403,7 +403,7 @@
         verifyAndReset();
 
         // POLICY_REJECT should RULE_REJECT in background
-        expectSetUidNetworkRules(UID_A, true);
+        expectSetUidMeteredNetworkBlacklist(UID_A, true);
         expectSetUidForeground(UID_A, false);
         future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
         replay();
@@ -416,7 +416,7 @@
         Future<Void> future;
 
         // POLICY_NONE should have RULE_ALLOW in background
-        expectSetUidNetworkRules(UID_A, false);
+        expectSetUidMeteredNetworkBlacklist(UID_A, false);
         expectSetUidForeground(UID_A, false);
         future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
         replay();
@@ -426,7 +426,7 @@
         verifyAndReset();
 
         // adding POLICY_REJECT should cause RULE_REJECT
-        expectSetUidNetworkRules(UID_A, true);
+        expectSetUidMeteredNetworkBlacklist(UID_A, true);
         expectSetUidForeground(UID_A, false);
         future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
         replay();
@@ -435,7 +435,7 @@
         verifyAndReset();
 
         // removing POLICY_REJECT should return us to RULE_ALLOW
-        expectSetUidNetworkRules(UID_A, false);
+        expectSetUidMeteredNetworkBlacklist(UID_A, false);
         expectSetUidForeground(UID_A, false);
         future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
         replay();
@@ -632,7 +632,7 @@
         Future<Void> future;
 
         // POLICY_REJECT should RULE_REJECT in background
-        expectSetUidNetworkRules(UID_A, true);
+        expectSetUidMeteredNetworkBlacklist(UID_A, true);
         expectSetUidForeground(UID_A, false);
         future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
         replay();
@@ -641,7 +641,7 @@
         verifyAndReset();
 
         // uninstall should clear RULE_REJECT
-        expectSetUidNetworkRules(UID_A, false);
+        expectSetUidMeteredNetworkBlacklist(UID_A, false);
         expectSetUidForeground(UID_A, false);
         future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
         replay();
@@ -890,9 +890,9 @@
         expectLastCall().atLeastOnce();
     }
 
-    private void expectSetUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces)
+    private void expectSetUidMeteredNetworkBlacklist(int uid, boolean rejectOnQuotaInterfaces)
             throws Exception {
-        mNetworkManager.setUidNetworkRules(uid, rejectOnQuotaInterfaces);
+        mNetworkManager.setUidMeteredNetworkBlacklist(uid, rejectOnQuotaInterfaces);
         expectLastCall().atLeastOnce();
     }
 
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index e26e54b..b4c6e6a 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -250,7 +250,7 @@
          * in its manifest.
          * <p>
          * See {@link Connection#CAPABILITY_CAN_PULL_CALL} and
-         * {@link Connection#CAPABILITY_IS_EXTERNAL_CALL}.
+         * {@link Connection#PROPERTY_IS_EXTERNAL_CALL}.
          */
         public static final int CAPABILITY_CAN_PULL_CALL = 0x00800000;
 
@@ -296,13 +296,13 @@
          * Consider, for example, a scenario where a user has two phones with the same phone number.
          * When a user places a call on one device, the telephony stack can represent that call on
          * the other device by adding it to the {@link ConnectionService} with the
-         * {@link Connection#CAPABILITY_IS_EXTERNAL_CALL} capability set.
+         * {@link Connection#PROPERTY_IS_EXTERNAL_CALL} property set.
          * <p>
          * An {@link InCallService} will only see calls with this property if it has the
          * {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true}
          * in its manifest.
          * <p>
-         * See {@link Connection#CAPABILITY_IS_EXTERNAL_CALL}.
+         * See {@link Connection#PROPERTY_IS_EXTERNAL_CALL}.
          */
         public static final int PROPERTY_IS_EXTERNAL_CALL = 0x00000040;
 
@@ -686,7 +686,7 @@
             sb.append(", caps: ");
             sb.append(capabilitiesToString(mCallCapabilities));
             sb.append(", props: ");
-            sb.append(mCallProperties);
+            sb.append(propertiesToString(mCallProperties));
             sb.append("]");
             return sb.toString();
         }
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 00e07af..06851ee 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -53,6 +53,8 @@
         public void onDestroyed(Conference conference) {}
         public void onConnectionCapabilitiesChanged(
                 Conference conference, int connectionCapabilities) {}
+        public void onConnectionPropertiesChanged(
+                Conference conference, int connectionProperties) {}
         public void onVideoStateChanged(Conference c, int videoState) { }
         public void onVideoProviderChanged(Conference c, Connection.VideoProvider videoProvider) {}
         public void onStatusHintsChanged(Conference conference, StatusHints statusHints) {}
@@ -74,6 +76,7 @@
     private int mState = Connection.STATE_NEW;
     private DisconnectCause mDisconnectCause;
     private int mConnectionCapabilities;
+    private int mConnectionProperties;
     private String mDisconnectMessage;
     private long mConnectTimeMillis = CONNECT_TIME_NOT_SPECIFIED;
     private StatusHints mStatusHints;
@@ -156,6 +159,16 @@
     }
 
     /**
+     * Returns the properties of the conference. See {@code PROPERTY_*} constants in class
+     * {@link Connection} for valid values.
+     *
+     * @return A bitmask of the properties of the conference call.
+     */
+    public final int getConnectionProperties() {
+        return mConnectionProperties;
+    }
+
+    /**
      * Whether the given capabilities support the specified capability.
      *
      * @param capabilities A capability bit field.
@@ -364,7 +377,7 @@
      * Sets the capabilities of a conference. See {@code CAPABILITY_*} constants of class
      * {@link Connection} for valid values.
      *
-     * @param connectionCapabilities A bitmask of the {@code PhoneCapabilities} of the conference call.
+     * @param connectionCapabilities A bitmask of the {@code Capabilities} of the conference call.
      */
     public final void setConnectionCapabilities(int connectionCapabilities) {
         if (connectionCapabilities != mConnectionCapabilities) {
@@ -377,6 +390,22 @@
     }
 
     /**
+     * Sets the properties of a conference. See {@code PROPERTY_*} constants of class
+     * {@link Connection} for valid values.
+     *
+     * @param connectionProperties A bitmask of the {@code Properties} of the conference call.
+     */
+    public final void setConnectionProperties(int connectionProperties) {
+        if (connectionProperties != mConnectionProperties) {
+            mConnectionProperties = connectionProperties;
+
+            for (Listener l : mListeners) {
+                l.onConnectionPropertiesChanged(this, mConnectionProperties);
+            }
+        }
+    }
+
+    /**
      * Adds the specified connection as a child of this conference.
      *
      * @param connection The connection to add.
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 3ea1c6a..310c957 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -98,7 +98,7 @@
      * The state of an external connection which is in the process of being pulled from a remote
      * device to the local device.
      * <p>
-     * A connection can only be in this state if the {@link #CAPABILITY_IS_EXTERNAL_CALL} and
+     * A connection can only be in this state if the {@link #PROPERTY_IS_EXTERNAL_CALL} property and
      * {@link #CAPABILITY_CAN_PULL_CALL} capability bits are set on the connection.
      */
     public static final int STATE_PULLING_CALL = 7;
@@ -194,31 +194,28 @@
     public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 0x00002000;
 
     /**
-     * Whether the call is a generic conference, where we do not know the precise state of
-     * participants in the conference (eg. on CDMA).
-     *
+     * Un-used.
      * @hide
      */
-    public static final int CAPABILITY_GENERIC_CONFERENCE = 0x00004000;
+    public static final int CAPABILITY_UNUSED_2 = 0x00004000;
 
     /**
-     * Connection is using high definition audio.
+     * Un-used.
      * @hide
      */
-    public static final int CAPABILITY_HIGH_DEF_AUDIO = 0x00008000;
+    public static final int CAPABILITY_UNUSED_3 = 0x00008000;
 
     /**
-     * Connection is using WIFI.
+     * Un-used.
      * @hide
      */
-    public static final int CAPABILITY_WIFI = 0x00010000;
+    public static final int CAPABILITY_UNUSED_4 = 0x00010000;
 
     /**
-     * Indicates that the current device callback number should be shown.
-     *
+     * Un-used.
      * @hide
      */
-    public static final int CAPABILITY_SHOW_CALLBACK_NUMBER = 0x00020000;
+    public static final int CAPABILITY_UNUSED_5 = 0x00020000;
 
     /**
      * Speed up audio setup for MT call.
@@ -281,32 +278,64 @@
     public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 0x00800000;
 
     /**
+     * When set for an external connection, indicates that this {@code Connection} can be pulled
+     * from a remote device to the current device.
+     * <p>
+     * Should only be set on a {@code Connection} where {@link #PROPERTY_IS_EXTERNAL_CALL}
+     * is set.
+     */
+    public static final int CAPABILITY_CAN_PULL_CALL = 0x01000000;
+
+    //**********************************************************************************************
+    // Next CAPABILITY value: 0x02000000
+    //**********************************************************************************************
+
+    /**
+     * Indicates that the current device callback number should be shown.
+     *
+     * @hide
+     */
+    public static final int PROPERTY_SHOW_CALLBACK_NUMBER = 1<<0;
+
+    /**
+     * Whether the call is a generic conference, where we do not know the precise state of
+     * participants in the conference (eg. on CDMA).
+     *
+     * @hide
+     */
+    public static final int PROPERTY_GENERIC_CONFERENCE = 1<<1;
+
+    /**
+     * Connection is using high definition audio.
+     * @hide
+     */
+    public static final int PROPERTY_HIGH_DEF_AUDIO = 1<<2;
+
+    /**
+     * Connection is using WIFI.
+     * @hide
+     */
+    public static final int PROPERTY_WIFI = 1<<3;
+
+    /**
      * When set, indicates that the {@code Connection} does not actually exist locally for the
      * {@link ConnectionService}.
      * <p>
      * Consider, for example, a scenario where a user has two devices with the same phone number.
      * When a user places a call on one devices, the telephony stack can represent that call on the
      * other device by adding is to the {@link ConnectionService} with the
-     * {@code CAPABILITY_IS_EXTERNAL_CALL} capability set.
+     * {@link #PROPERTY_IS_EXTERNAL_CALL} capability set.
      * <p>
      * An {@link ConnectionService} should not assume that all {@link InCallService}s will handle
      * external connections.  Only those {@link InCallService}s which have the
      * {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true} in its
      * manifest will see external connections.
      */
-    public static final int CAPABILITY_IS_EXTERNAL_CALL = 0x01000000;
+    public static final int PROPERTY_IS_EXTERNAL_CALL = 1<<4;
 
-    /**
-     * When set for an external connection, indicates that this {@code Connection} can be pulled
-     * from a remote device to the current device.
-     * <p>
-     * Should only be set on a {@code Connection} where {@link #CAPABILITY_IS_EXTERNAL_CALL}
-     * is set.
-     */
-    public static final int CAPABILITY_CAN_PULL_CALL = 0x02000000;
 
     //**********************************************************************************************
-    // Next CAPABILITY value: 0x04000000
+    // Next PROPERTY value: 1<<5
     //**********************************************************************************************
 
     /**
@@ -454,18 +483,6 @@
         if (can(capabilities, CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO)) {
             builder.append(" CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO");
         }
-        if (can(capabilities, CAPABILITY_HIGH_DEF_AUDIO)) {
-            builder.append(" CAPABILITY_HIGH_DEF_AUDIO");
-        }
-        if (can(capabilities, CAPABILITY_WIFI)) {
-            builder.append(" CAPABILITY_WIFI");
-        }
-        if (can(capabilities, CAPABILITY_GENERIC_CONFERENCE)) {
-            builder.append(" CAPABILITY_GENERIC_CONFERENCE");
-        }
-        if (can(capabilities, CAPABILITY_SHOW_CALLBACK_NUMBER)) {
-            builder.append(" CAPABILITY_SHOW_CALLBACK_NUMBER");
-        }
         if (can(capabilities, CAPABILITY_SPEED_UP_MT_AUDIO)) {
             builder.append(" CAPABILITY_SPEED_UP_MT_AUDIO");
         }
@@ -481,9 +498,6 @@
         if (can(capabilities, CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION)) {
             builder.append(" CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION");
         }
-        if (can(capabilities, CAPABILITY_IS_EXTERNAL_CALL)) {
-            builder.append(" CAPABILITY_IS_EXTERNAL_CALL");
-        }
         if (can(capabilities, CAPABILITY_CAN_PULL_CALL)) {
             builder.append(" CAPABILITY_CAN_PULL_CALL");
         }
@@ -492,6 +506,34 @@
         return builder.toString();
     }
 
+    public static String propertiesToString(int properties) {
+        StringBuilder builder = new StringBuilder();
+        builder.append("[Properties:");
+
+        if (can(properties, PROPERTY_SHOW_CALLBACK_NUMBER)) {
+            builder.append(" PROPERTY_SHOW_CALLBACK_NUMBER");
+        }
+
+        if (can(properties, PROPERTY_HIGH_DEF_AUDIO)) {
+            builder.append(" PROPERTY_HIGH_DEF_AUDIO");
+        }
+
+        if (can(properties, PROPERTY_WIFI)) {
+            builder.append(" PROPERTY_WIFI");
+        }
+
+        if (can(properties, PROPERTY_GENERIC_CONFERENCE)) {
+            builder.append(" PROPERTY_GENERIC_CONFERENCE");
+        }
+
+        if (can(properties, PROPERTY_IS_EXTERNAL_CALL)) {
+            builder.append(" PROPERTY_IS_EXTERNAL_CALL");
+        }
+
+        builder.append("]");
+        return builder.toString();
+    }
+
     /** @hide */
     public abstract static class Listener {
         public void onStateChanged(Connection c, int state) {}
@@ -505,6 +547,7 @@
         public void onRingbackRequested(Connection c, boolean ringback) {}
         public void onDestroyed(Connection c) {}
         public void onConnectionCapabilitiesChanged(Connection c, int capabilities) {}
+        public void onConnectionPropertiesChanged(Connection c, int properties) {}
         public void onVideoProviderChanged(
                 Connection c, VideoProvider videoProvider) {}
         public void onAudioModeIsVoipChanged(Connection c, boolean isVoip) {}
@@ -1175,6 +1218,7 @@
     private int mCallerDisplayNamePresentation;
     private boolean mRingbackRequested = false;
     private int mConnectionCapabilities;
+    private int mConnectionProperties;
     private VideoProvider mVideoProvider;
     private boolean mAudioModeIsVoip;
     private long mConnectTimeMillis = Conference.CONNECT_TIME_NOT_SPECIFIED;
@@ -1439,6 +1483,13 @@
     }
 
     /**
+     * Returns the connection's properties, as a bit mask of the {@code PROPERTY_*} constants.
+     */
+    public final int getConnectionProperties() {
+        return mConnectionProperties;
+    }
+
+    /**
      * Sets the value of the {@link #getAddress()} property.
      *
      * @param address The new address.
@@ -1635,6 +1686,21 @@
     }
 
     /**
+     * Sets the connection's properties as a bit mask of the {@code PROPERTY_*} constants.
+     *
+     * @param connectionProperties The new connection properties.
+     */
+    public final void setConnectionProperties(int connectionProperties) {
+        checkImmutable();
+        if (mConnectionProperties != connectionProperties) {
+            mConnectionProperties = connectionProperties;
+            for (Listener l : mListeners) {
+                l.onConnectionPropertiesChanged(this, mConnectionProperties);
+            }
+        }
+    }
+
+    /**
      * Tears down the Connection object.
      */
     public final void destroy() {
@@ -2042,10 +2108,10 @@
      * The {@link InCallService} issues a request to pull an external call to the local device via
      * {@link Call#pullExternalCall()}.
      * <p>
-     * For a Connection to be pulled, both the {@link Connection#CAPABILITY_CAN_PULL_CALL} and
-     * {@link Connection#CAPABILITY_IS_EXTERNAL_CALL} capability bits must be set.
+     * For a Connection to be pulled, both the {@link Connection#CAPABILITY_CAN_PULL_CALL}
+     * capability and {@link Connection#PROPERTY_IS_EXTERNAL_CALL} property bits must be set.
      * <p>
-     * For more information on external calls, see {@link Connection#CAPABILITY_IS_EXTERNAL_CALL}.
+     * For more information on external calls, see {@link Connection#PROPERTY_IS_EXTERNAL_CALL}.
      */
     public void onPullExternalCall() {}
 
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index e092095..4cab7f0 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -495,6 +495,16 @@
         }
 
         @Override
+        public void onConnectionPropertiesChanged(
+                Conference conference,
+                int connectionProperties) {
+            String id = mIdByConference.get(conference);
+            Log.d(this, "call capabilities: conference: %s",
+                    Connection.propertiesToString(connectionProperties));
+            mAdapter.setConnectionProperties(id, connectionProperties);
+        }
+
+        @Override
         public void onVideoStateChanged(Conference c, int videoState) {
             String id = mIdByConference.get(c);
             Log.d(this, "onVideoStateChanged set video state %d", videoState);
@@ -623,6 +633,14 @@
         }
 
         @Override
+        public void onConnectionPropertiesChanged(Connection c, int properties) {
+            String id = mIdByConnection.get(c);
+            Log.d(this, "properties: parcelableconnection: %s",
+                    Connection.propertiesToString(properties));
+            mAdapter.setConnectionProperties(id, properties);
+        }
+
+        @Override
         public void onVideoProviderChanged(Connection c, Connection.VideoProvider videoProvider) {
             String id = mIdByConnection.get(c);
             Log.d(this, "onVideoProviderChanged: Connection: %s, VideoProvider: %s", c,
@@ -740,10 +758,11 @@
 
         Uri address = connection.getAddress();
         String number = address == null ? "null" : address.getSchemeSpecificPart();
-        Log.v(this, "createConnection, number: %s, state: %s, capabilities: %s",
+        Log.v(this, "createConnection, number: %s, state: %s, capabilities: %s, properties: %s",
                 Connection.toLogSafePhoneNumber(number),
                 Connection.stateToString(connection.getState()),
-                Connection.capabilitiesToString(connection.getConnectionCapabilities()));
+                Connection.capabilitiesToString(connection.getConnectionCapabilities()),
+                Connection.propertiesToString(connection.getConnectionProperties()));
 
         Log.d(this, "createConnection, calling handleCreateConnectionSuccessful %s", callId);
         mAdapter.handleCreateConnectionComplete(
@@ -753,6 +772,7 @@
                         request.getAccountHandle(),
                         connection.getState(),
                         connection.getConnectionCapabilities(),
+                        connection.getConnectionProperties(),
                         connection.getAddress(),
                         connection.getAddressPresentation(),
                         connection.getCallerDisplayName(),
@@ -1110,6 +1130,7 @@
                     conference.getPhoneAccountHandle(),
                     conference.getState(),
                     conference.getConnectionCapabilities(),
+                    conference.getConnectionProperties(),
                     connectionIds,
                     conference.getVideoProvider() == null ?
                             null : conference.getVideoProvider().getInterface(),
@@ -1150,6 +1171,7 @@
                     phoneAccountHandle,
                     connection.getState(),
                     connection.getConnectionCapabilities(),
+                    connection.getConnectionProperties(),
                     connection.getAddress(),
                     connection.getAddressPresentation(),
                     connection.getCallerDisplayName(),
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index 81e4c22..c8cd3c0 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -196,6 +196,15 @@
         }
     }
 
+    void setConnectionProperties(String callId, int properties) {
+        for (IConnectionServiceAdapter adapter : mAdapters) {
+            try {
+                adapter.setConnectionProperties(callId, properties);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+
     /**
      * Indicates whether or not the specified call is currently conferenced into the specified
      * conference call.
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index 3e46557..bf28feb 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -64,6 +64,7 @@
     private static final int MSG_PUT_EXTRAS = 24;
     private static final int MSG_REMOVE_EXTRAS = 25;
     private static final int MSG_ON_CONNECTION_EVENT = 26;
+    private static final int MSG_SET_CONNECTION_PROPERTIES = 27;
 
     private final IConnectionServiceAdapter mDelegate;
 
@@ -118,6 +119,9 @@
                 case MSG_SET_CONNECTION_CAPABILITIES:
                     mDelegate.setConnectionCapabilities((String) msg.obj, msg.arg1);
                     break;
+                case MSG_SET_CONNECTION_PROPERTIES:
+                    mDelegate.setConnectionProperties((String) msg.obj, msg.arg1);
+                    break;
                 case MSG_SET_IS_CONFERENCED: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
@@ -322,6 +326,13 @@
         }
 
         @Override
+        public void setConnectionProperties(String connectionId, int connectionProperties) {
+            mHandler.obtainMessage(
+                    MSG_SET_CONNECTION_PROPERTIES, connectionProperties, 0, connectionId)
+                    .sendToTarget();
+        }
+
+        @Override
         public void setConferenceMergeFailed(String callId) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = callId;
diff --git a/telecomm/java/android/telecom/ParcelableConference.java b/telecomm/java/android/telecom/ParcelableConference.java
index 870f5ee..f5689d8 100644
--- a/telecomm/java/android/telecom/ParcelableConference.java
+++ b/telecomm/java/android/telecom/ParcelableConference.java
@@ -34,6 +34,7 @@
     private PhoneAccountHandle mPhoneAccount;
     private int mState;
     private int mConnectionCapabilities;
+    private int mConnectionProperties;
     private List<String> mConnectionIds;
     private long mConnectTimeMillis = Conference.CONNECT_TIME_NOT_SPECIFIED;
     private final IVideoProvider mVideoProvider;
@@ -45,6 +46,7 @@
             PhoneAccountHandle phoneAccount,
             int state,
             int connectionCapabilities,
+            int connectionProperties,
             List<String> connectionIds,
             IVideoProvider videoProvider,
             int videoState,
@@ -54,6 +56,7 @@
         mPhoneAccount = phoneAccount;
         mState = state;
         mConnectionCapabilities = connectionCapabilities;
+        mConnectionProperties = connectionProperties;
         mConnectionIds = connectionIds;
         mConnectTimeMillis = Conference.CONNECT_TIME_NOT_SPECIFIED;
         mVideoProvider = videoProvider;
@@ -72,6 +75,8 @@
                 .append(Connection.stateToString(mState))
                 .append(", capabilities: ")
                 .append(Connection.capabilitiesToString(mConnectionCapabilities))
+                .append(", properties: ")
+                .append(Connection.propertiesToString(mConnectionProperties))
                 .append(", connectTime: ")
                 .append(mConnectTimeMillis)
                 .append(", children: ")
@@ -95,6 +100,10 @@
         return mConnectionCapabilities;
     }
 
+    public int getConnectionProperties() {
+        return mConnectionProperties;
+    }
+
     public List<String> getConnectionIds() {
         return mConnectionIds;
     }
@@ -134,9 +143,11 @@
             int videoState = source.readInt();
             StatusHints statusHints = source.readParcelable(classLoader);
             Bundle extras = source.readBundle(classLoader);
+            int properties = source.readInt();
 
-            return new ParcelableConference(phoneAccount, state, capabilities, connectionIds,
-                    videoCallProvider, videoState, connectTimeMillis, statusHints, extras);
+            return new ParcelableConference(phoneAccount, state, capabilities, properties,
+                    connectionIds, videoCallProvider, videoState, connectTimeMillis, statusHints,
+                    extras);
         }
 
         @Override
@@ -164,5 +175,6 @@
         destination.writeInt(mVideoState);
         destination.writeParcelable(mStatusHints, 0);
         destination.writeBundle(mExtras);
+        destination.writeInt(mConnectionProperties);
     }
 }
diff --git a/telecomm/java/android/telecom/ParcelableConnection.java b/telecomm/java/android/telecom/ParcelableConnection.java
index ce51c96..540f388 100644
--- a/telecomm/java/android/telecom/ParcelableConnection.java
+++ b/telecomm/java/android/telecom/ParcelableConnection.java
@@ -36,6 +36,7 @@
     private final PhoneAccountHandle mPhoneAccount;
     private final int mState;
     private final int mConnectionCapabilities;
+    private final int mConnectionProperties;
     private final Uri mAddress;
     private final int mAddressPresentation;
     private final String mCallerDisplayName;
@@ -55,6 +56,7 @@
             PhoneAccountHandle phoneAccount,
             int state,
             int capabilities,
+            int properties,
             Uri address,
             int addressPresentation,
             String callerDisplayName,
@@ -71,6 +73,7 @@
         mPhoneAccount = phoneAccount;
         mState = state;
         mConnectionCapabilities = capabilities;
+        mConnectionProperties = properties;
         mAddress = address;
         mAddressPresentation = addressPresentation;
         mCallerDisplayName = callerDisplayName;
@@ -94,11 +97,26 @@
         return mState;
     }
 
-    // Bit mask of actions a call supports, values are defined in {@link CallCapabilities}.
+    /**
+     * Returns the current connection capabilities bit-mask.  Connection capabilities are defined as
+     * {@code CAPABILITY_*} constants in {@link Connection}.
+     *
+     * @return Bit-mask containing capabilities of the connection.
+     */
     public int getConnectionCapabilities() {
         return mConnectionCapabilities;
     }
 
+    /**
+     * Returns the current connection properties bit-mask.  Connection properties are defined as
+     * {@code PROPERTY_*} constants in {@link Connection}.
+     *
+     * @return Bit-mask containing properties of the connection.
+     */
+    public int getConnectionProperties() {
+        return mConnectionProperties;
+    }
+
     public Uri getHandle() {
         return mAddress;
     }
@@ -160,6 +178,8 @@
                 .append(mState)
                 .append(", capabilities:")
                 .append(Connection.capabilitiesToString(mConnectionCapabilities))
+                .append(", properties:")
+                .append(Connection.propertiesToString(mConnectionProperties))
                 .append(", extras:")
                 .append(mExtras)
                 .toString();
@@ -189,11 +209,13 @@
             List<String> conferenceableConnectionIds = new ArrayList<>();
             source.readStringList(conferenceableConnectionIds);
             Bundle extras = Bundle.setDefusable(source.readBundle(classLoader), true);
+            int properties = source.readInt();
 
             return new ParcelableConnection(
                     phoneAccount,
                     state,
                     capabilities,
+                    properties,
                     address,
                     addressPresentation,
                     callerDisplayName,
@@ -241,5 +263,6 @@
         destination.writeParcelable(mDisconnectCause, 0);
         destination.writeStringList(mConferenceableConnectionIds);
         destination.writeBundle(mExtras);
+        destination.writeInt(mConnectionProperties);
     }
 }
diff --git a/telecomm/java/android/telecom/RemoteConference.java b/telecomm/java/android/telecom/RemoteConference.java
index b03cb51..943da6d 100644
--- a/telecomm/java/android/telecom/RemoteConference.java
+++ b/telecomm/java/android/telecom/RemoteConference.java
@@ -92,6 +92,18 @@
                 int connectionCapabilities) {}
 
         /**
+         * Indicates that the call properties of this {@code RemoteConference} have changed.
+         * See {@link #getConnectionProperties()}.
+         *
+         * @param conference The {@code RemoteConference} invoking this method.
+         * @param connectionProperties The new properties of the {@code RemoteConference}.
+         */
+        public void onConnectionPropertiesChanged(
+                RemoteConference conference,
+                int connectionProperties) {}
+
+
+        /**
          * Invoked when the set of {@link RemoteConnection}s which can be added to this conference
          * call have changed.
          *
@@ -133,6 +145,7 @@
     private int mState = Connection.STATE_NEW;
     private DisconnectCause mDisconnectCause;
     private int mConnectionCapabilities;
+    private int mConnectionProperties;
     private Bundle mExtras;
 
     /** @hide */
@@ -244,6 +257,24 @@
     }
 
     /** @hide */
+    void setConnectionProperties(final int connectionProperties) {
+        if (mConnectionProperties != connectionProperties) {
+            mConnectionProperties = connectionProperties;
+            for (CallbackRecord<Callback> record : mCallbackRecords) {
+                final RemoteConference conference = this;
+                final Callback callback = record.getCallback();
+                record.getHandler().post(new Runnable() {
+                    @Override
+                    public void run() {
+                        callback.onConnectionPropertiesChanged(
+                                conference, mConnectionProperties);
+                    }
+                });
+            }
+        }
+    }
+
+    /** @hide */
     void setConferenceableConnections(List<RemoteConnection> conferenceableConnections) {
         mConferenceableConnections.clear();
         mConferenceableConnections.addAll(conferenceableConnections);
@@ -342,6 +373,16 @@
     }
 
     /**
+     * Returns the properties of the conference. See {@code PROPERTY_*} constants in class
+     * {@link Connection} for valid values.
+     *
+     * @return A bitmask of the properties of the conference call.
+     */
+    public final int getConnectionProperties() {
+        return mConnectionProperties;
+    }
+
+    /**
      * Obtain the extras associated with this {@code RemoteConnection}.
      *
      * @return The extras for this connection.
diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java
index 7df6678..dc8eaf6 100644
--- a/telecomm/java/android/telecom/RemoteConnection.java
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -90,6 +90,17 @@
                 int connectionCapabilities) {}
 
         /**
+         * Indicates that the call properties of this {@code RemoteConnection} have changed.
+         * See {@link #getConnectionProperties()}.
+         *
+         * @param connection The {@code RemoteConnection} invoking this method.
+         * @param connectionProperties The new properties of the {@code RemoteConnection}.
+         */
+        public void onConnectionPropertiesChanged(
+                RemoteConnection connection,
+                int connectionProperties) {}
+
+        /**
          * Invoked when the post-dial sequence in the outgoing {@code Connection} has reached a
          * pause character. This causes the post-dial signals to stop pending user confirmation. An
          * implementation should present this choice to the user and invoke
@@ -588,6 +599,7 @@
     private boolean mRingbackRequested;
     private boolean mConnected;
     private int mConnectionCapabilities;
+    private int mConnectionProperties;
     private int mVideoState;
     private VideoProvider mVideoProvider;
     private boolean mIsVoipAudioMode;
@@ -624,6 +636,7 @@
         mDisconnectCause = connection.getDisconnectCause();
         mRingbackRequested = connection.isRingbackRequested();
         mConnectionCapabilities = connection.getConnectionCapabilities();
+        mConnectionProperties = connection.getConnectionProperties();
         mVideoState = connection.getVideoState();
         mVideoProvider = new RemoteConnection.VideoProvider(connection.getVideoProvider());
         mIsVoipAudioMode = connection.getIsVoipAudioMode();
@@ -719,6 +732,16 @@
     }
 
     /**
+     * Obtains the properties of this {@code RemoteConnection}.
+     *
+     * @return A bitmask of the properties of the {@code RemoteConnection}, as defined in the
+     *         {@code PROPERTY_*} constants in class {@link Connection}.
+     */
+    public int getConnectionProperties() {
+        return mConnectionProperties;
+    }
+
+    /**
      * Determines if the audio mode of this {@code RemoteConnection} is VOIP.
      *
      * @return {@code true} if the {@code RemoteConnection}'s current audio mode is VOIP.
@@ -1114,6 +1137,23 @@
     /**
      * @hide
      */
+    void setConnectionProperties(final int connectionProperties) {
+        mConnectionProperties = connectionProperties;
+        for (CallbackRecord record : mCallbackRecords) {
+            final RemoteConnection connection = this;
+            final Callback callback = record.getCallback();
+            record.getHandler().post(new Runnable() {
+                @Override
+                public void run() {
+                    callback.onConnectionPropertiesChanged(connection, connectionProperties);
+                }
+            });
+        }
+    }
+
+    /**
+     * @hide
+     */
     void setDestroyed() {
         if (!mCallbackRecords.isEmpty()) {
             // Make sure that the callbacks are notified that the call is destroyed first.
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index d88d007..21a7706 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -61,6 +61,7 @@
                 mPendingConnections.remove(connection);
                 // Unconditionally initialize the connection ...
                 connection.setConnectionCapabilities(parcel.getConnectionCapabilities());
+                connection.setConnectionProperties(parcel.getConnectionProperties());
                 if (parcel.getHandle() != null
                     || parcel.getState() != Connection.STATE_DISCONNECTED) {
                     connection.setAddress(parcel.getHandle(), parcel.getHandlePresentation());
@@ -156,6 +157,17 @@
         }
 
         @Override
+        public void setConnectionProperties(String callId, int connectionProperties) {
+            if (mConnectionById.containsKey(callId)) {
+                findConnectionForAction(callId, "setConnectionProperties")
+                        .setConnectionProperties(connectionProperties);
+            } else {
+                findConferenceForAction(callId, "setConnectionProperties")
+                        .setConnectionProperties(connectionProperties);
+            }
+        }
+
+        @Override
         public void setIsConferenced(String callId, String conferenceCallId) {
             // Note: callId should not be null; conferenceCallId may be null
             RemoteConnection connection =
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
index 6804805..9bc8ffe 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -55,6 +55,8 @@
 
     void setConnectionCapabilities(String callId, int connectionCapabilities);
 
+    void setConnectionProperties(String callId, int connectionProperties);
+
     void setIsConferenced(String callId, String conferenceCallId);
 
     void setConferenceMergeFailed(String callId);
diff --git a/telephony/java/com/android/ims/ImsCallProfile.java b/telephony/java/com/android/ims/ImsCallProfile.java
index 96c6243..303746c 100644
--- a/telephony/java/com/android/ims/ImsCallProfile.java
+++ b/telephony/java/com/android/ims/ImsCallProfile.java
@@ -201,7 +201,7 @@
      * "14" vs (int) 14).
      * Note: This is used by {@link com.android.internal.telephony.imsphone.ImsPhoneConnection#
      *      updateWifiStateFromExtras(Bundle)} to determine whether to set the
-     * {@link android.telecom.Connection#CAPABILITY_WIFI} capability on a connection.
+     * {@link android.telecom.Connection#PROPERTY_WIFI} property on a connection.
      */
     public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
 
diff --git a/tools/fonts/fontchain_lint.py b/tools/fonts/fontchain_lint.py
index a848346..75e837b 100755
--- a/tools/fonts/fontchain_lint.py
+++ b/tools/fonts/fontchain_lint.py
@@ -9,17 +9,37 @@
 from fontTools import ttLib
 
 LANG_TO_SCRIPT = {
+    'as': 'Beng',
+    'bn': 'Beng',
+    'cy': 'Latn',
+    'da': 'Latn',
     'de': 'Latn',
     'en': 'Latn',
     'es': 'Latn',
+    'et': 'Latn',
     'eu': 'Latn',
-    'ja': 'Jpan',
-    'ko': 'Kore',
+    'fr': 'Latn',
+    'ga': 'Latn',
+    'gu': 'Gujr',
+    'hi': 'Deva',
+    'hr': 'Latn',
     'hu': 'Latn',
     'hy': 'Armn',
+    'ja': 'Jpan',
+    'kn': 'Knda',
+    'ko': 'Kore',
+    'ml': 'Mlym',
+    'mn': 'Cyrl',
+    'mr': 'Deva',
     'nb': 'Latn',
     'nn': 'Latn',
+    'or': 'Orya',
+    'pa': 'Guru',
     'pt': 'Latn',
+    'sl': 'Latn',
+    'ta': 'Taml',
+    'te': 'Telu',
+    'tk': 'Latn',
 }
 
 def lang_to_script(lang_code):
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index a9259fa..67cf107 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -56,6 +56,11 @@
      */
     public int anqpDomainId;
 
+    /*
+     * This field is equivalent to the |flags|, rather than the |capabilities| field
+     * of the per-BSS scan results returned by WPA supplicant. See the definition of
+     * |struct wpa_bss| in wpa_supplicant/bss.h for more details.
+     */
     /**
      * Describes the authentication, key management, and encryption schemes
      * supported by the access point.
@@ -211,6 +216,10 @@
     /** {@hide} */
     public static final long FLAG_80211mc_RESPONDER               = 0x0000000000000002;
 
+    /*
+     * These flags are specific to the ScanResult class, and are not related to the |flags|
+     * field of the per-BSS scan results from WPA supplicant.
+     */
     /**
      * Defines flags; such as {@link #FLAG_PASSPOINT_NETWORK}.
      * {@hide}
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 7dc8049..06dea07 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -684,6 +684,13 @@
 
     /**
      * @hide
+     * A hint about whether or not the network represented by this WifiConfiguration
+     * is metered.
+     */
+    public boolean meteredHint;
+
+    /**
+     * @hide
      * Number of time the scorer overrode a the priority based choice, when comparing two
      * WifiConfigurations, note that since comparing WifiConfiguration happens very often
      * potentially at every scan, this number might become very large, even on an idle
@@ -1302,6 +1309,7 @@
         selfAdded = false;
         didSelfAdd = false;
         ephemeral = false;
+        meteredHint = false;
         validatedInternetAccess = false;
         mIpConfiguration = new IpConfiguration();
         lastUpdateUid = -1;
@@ -1399,7 +1407,9 @@
         if (this.selfAdded) sbuf.append(" selfAdded");
         if (this.validatedInternetAccess) sbuf.append(" validatedInternetAccess");
         if (this.ephemeral) sbuf.append(" ephemeral");
-        if (this.didSelfAdd || this.selfAdded || this.validatedInternetAccess || this.ephemeral) {
+        if (this.meteredHint) sbuf.append(" meteredHint");
+        if (this.didSelfAdd || this.selfAdded || this.validatedInternetAccess
+            || this.ephemeral || this.meteredHint) {
             sbuf.append("\n");
         }
         sbuf.append(" KeyMgmt:");
@@ -1546,18 +1556,6 @@
         return sbuf.toString();
     }
 
-    /**
-     * Construct a WifiConfiguration from a scanned network
-     * @param scannedAP the scan result used to construct the config entry
-     * TODO: figure out whether this is a useful way to construct a new entry.
-     *
-    public WifiConfiguration(ScanResult scannedAP) {
-        networkId = -1;
-        SSID = scannedAP.SSID;
-        BSSID = scannedAP.BSSID;
-    }
-    */
-
     /** {@hide} */
     public String getPrintableSsid() {
         if (SSID == null) return "";
@@ -1832,6 +1830,7 @@
             selfAdded = source.selfAdded;
             validatedInternetAccess = source.validatedInternetAccess;
             ephemeral = source.ephemeral;
+            meteredHint = source.meteredHint;
             if (source.visibility != null) {
                 visibility = new Visibility(source.visibility);
             }
@@ -1870,11 +1869,6 @@
         }
     }
 
-    /** {@hide} */
-    //public static final int NOTHING_TAG = 0;
-    /** {@hide} */
-    //public static final int SCAN_CACHE_TAG = 1;
-
     /** Implement the Parcelable interface {@hide} */
     @Override
     public void writeToParcel(Parcel dest, int flags) {
@@ -1916,6 +1910,7 @@
         dest.writeInt(didSelfAdd ? 1 : 0);
         dest.writeInt(validatedInternetAccess ? 1 : 0);
         dest.writeInt(ephemeral ? 1 : 0);
+        dest.writeInt(meteredHint ? 1 : 0);
         dest.writeInt(creatorUid);
         dest.writeInt(lastConnectUid);
         dest.writeInt(lastUpdateUid);
@@ -1985,6 +1980,7 @@
                 config.didSelfAdd = in.readInt() != 0;
                 config.validatedInternetAccess = in.readInt() != 0;
                 config.ephemeral = in.readInt() != 0;
+                config.meteredHint = in.readInt() != 0;
                 config.creatorUid = in.readInt();
                 config.lastConnectUid = in.readInt();
                 config.lastUpdateUid = in.readInt();
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index c5e7bff..ecf54474f 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -277,6 +277,12 @@
          * non-zero => scan was truncated, so results may not be complete
          */
         private int mFlags;
+        /**
+         * Indicates the buckets that were scanned to generate these results.
+         * This is not relevant to WifiScanner API users and is used internally.
+         * {@hide}
+         */
+        private int mBucketsScanned;
         /** all scan results discovered in this scan, sorted by timestamp in ascending order */
         private ScanResult mResults[];
 
@@ -288,9 +294,18 @@
             mResults = results;
         }
 
+        /** {@hide} */
+        public ScanData(int id, int flags, int bucketsScanned, ScanResult[] results) {
+            mId = id;
+            mFlags = flags;
+            mBucketsScanned = bucketsScanned;
+            mResults = results;
+        }
+
         public ScanData(ScanData s) {
             mId = s.mId;
             mFlags = s.mFlags;
+            mBucketsScanned = s.mBucketsScanned;
             mResults = new ScanResult[s.mResults.length];
             for (int i = 0; i < s.mResults.length; i++) {
                 ScanResult result = s.mResults[i];
@@ -321,6 +336,7 @@
             if (mResults != null) {
                 dest.writeInt(mId);
                 dest.writeInt(mFlags);
+                dest.writeInt(mBucketsScanned);
                 dest.writeInt(mResults.length);
                 for (int i = 0; i < mResults.length; i++) {
                     ScanResult result = mResults[i];
@@ -337,12 +353,13 @@
                     public ScanData createFromParcel(Parcel in) {
                         int id = in.readInt();
                         int flags = in.readInt();
+                        int bucketsScanned = in.readInt();
                         int n = in.readInt();
                         ScanResult results[] = new ScanResult[n];
                         for (int i = 0; i < n; i++) {
                             results[i] = ScanResult.CREATOR.createFromParcel(in);
                         }
-                        return new ScanData(id, flags, results);
+                        return new ScanData(id, flags, bucketsScanned, results);
                     }
 
                     public ScanData[] newArray(int size) {