Merge "Fix issue #37306208: CTS: ServiceTest#testForegroundService..." into oc-dev
diff --git a/api/current.txt b/api/current.txt
index d3184d6..e4169b2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1710,6 +1710,7 @@
     field public static final int ic_notification_clear_all = 17301594; // 0x108005a
     field public static final int ic_notification_overlay = 17301595; // 0x108005b
     field public static final int ic_partial_secure = 17301596; // 0x108005c
+    field public static final int ic_picture_in_picture = 17301685; // 0x10800b5
     field public static final int ic_popup_disk_full = 17301597; // 0x108005d
     field public static final int ic_popup_reminder = 17301598; // 0x108005e
     field public static final int ic_popup_sync = 17301599; // 0x108005f
@@ -3848,7 +3849,7 @@
     method public deprecated java.util.List<android.app.ActivityManager.RecentTaskInfo> getRecentTasks(int, int) throws java.lang.SecurityException;
     method public java.util.List<android.app.ActivityManager.RunningAppProcessInfo> getRunningAppProcesses();
     method public android.app.PendingIntent getRunningServiceControlPanel(android.content.ComponentName) throws java.lang.SecurityException;
-    method public java.util.List<android.app.ActivityManager.RunningServiceInfo> getRunningServices(int) throws java.lang.SecurityException;
+    method public deprecated java.util.List<android.app.ActivityManager.RunningServiceInfo> getRunningServices(int) throws java.lang.SecurityException;
     method public deprecated java.util.List<android.app.ActivityManager.RunningTaskInfo> getRunningTasks(int) throws java.lang.SecurityException;
     method public deprecated boolean isInLockTaskMode();
     method public boolean isLowRamDevice();
@@ -4577,6 +4578,7 @@
     method public final android.app.FragmentManager getFragmentManager();
     method public final java.lang.Object getHost();
     method public final int getId();
+    method public final android.view.LayoutInflater getLayoutInflater();
     method public android.app.LoaderManager getLoaderManager();
     method public final android.app.Fragment getParentFragment();
     method public android.transition.Transition getReenterTransition();
@@ -5604,7 +5606,6 @@
     method public boolean removeAutomaticZenRule(java.lang.String);
     method public final void setInterruptionFilter(int);
     method public void setNotificationPolicy(android.app.NotificationManager.Policy);
-    method public deprecated android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification);
     method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule);
     field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED";
     field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED";
@@ -6625,7 +6626,7 @@
 
   public static class AssistStructure.ViewNode {
     method public float getAlpha();
-    method public java.lang.String[] getAutoFillHints();
+    method public java.lang.String[] getAutofillHints();
     method public android.view.autofill.AutofillId getAutofillId();
     method public java.lang.String[] getAutofillOptions();
     method public int getAutofillType();
@@ -7064,6 +7065,7 @@
     method public static void deleteAllHosts();
     method public void deleteAppWidgetId(int);
     method public void deleteHost();
+    method public int[] getAppWidgetIds();
     method protected android.appwidget.AppWidgetHostView onCreateView(android.content.Context, int, android.appwidget.AppWidgetProviderInfo);
     method protected void onProviderChanged(int, android.appwidget.AppWidgetProviderInfo);
     method protected void onProvidersChanged();
@@ -10941,12 +10943,13 @@
     method public android.content.pm.VersionedPackage getDeclaringPackage();
     method public java.util.List<android.content.pm.VersionedPackage> getDependentPackages();
     method public java.lang.String getName();
-    method public int getVersion();
-    method public boolean isBuiltin();
-    method public boolean isDynamic();
-    method public boolean isStatic();
+    method public int getType();
+    method public long getVersion();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.pm.SharedLibraryInfo> CREATOR;
+    field public static final int TYPE_BUILTIN = 0; // 0x0
+    field public static final int TYPE_DYNAMIC = 1; // 0x1
+    field public static final int TYPE_STATIC = 2; // 0x2
     field public static final int VERSION_UNDEFINED = -1; // 0xffffffff
   }
 
@@ -12600,7 +12603,6 @@
 
   public class BitmapShader extends android.graphics.Shader {
     ctor public BitmapShader(android.graphics.Bitmap, android.graphics.Shader.TileMode, android.graphics.Shader.TileMode);
-    method public void set(android.graphics.Bitmap, android.graphics.Shader.TileMode, android.graphics.Shader.TileMode);
   }
 
   public class BlurMaskFilter extends android.graphics.MaskFilter {
@@ -12853,8 +12855,6 @@
     ctor public ColorMatrixColorFilter(android.graphics.ColorMatrix);
     ctor public ColorMatrixColorFilter(float[]);
     method public void getColorMatrix(android.graphics.ColorMatrix);
-    method public void setColorMatrix(android.graphics.ColorMatrix);
-    method public void setColorMatrixArray(float[]);
   }
 
   public abstract class ColorSpace {
@@ -12995,8 +12995,6 @@
   public class ComposeShader extends android.graphics.Shader {
     ctor public ComposeShader(android.graphics.Shader, android.graphics.Shader, android.graphics.Xfermode);
     ctor public ComposeShader(android.graphics.Shader, android.graphics.Shader, android.graphics.PorterDuff.Mode);
-    method public void set(android.graphics.Shader, android.graphics.Shader, android.graphics.Xfermode);
-    method public void set(android.graphics.Shader, android.graphics.Shader, android.graphics.PorterDuff.Mode);
   }
 
   public class CornerPathEffect extends android.graphics.PathEffect {
@@ -13069,15 +13067,11 @@
     ctor public LightingColorFilter(int, int);
     method public int getColorAdd();
     method public int getColorMultiply();
-    method public void setColorAdd(int);
-    method public void setColorMultiply(int);
   }
 
   public class LinearGradient extends android.graphics.Shader {
     ctor public LinearGradient(float, float, float, float, int[], float[], android.graphics.Shader.TileMode);
     ctor public LinearGradient(float, float, float, float, int, int, android.graphics.Shader.TileMode);
-    method public void set(float, float, float, float, int[], float[], android.graphics.Shader.TileMode);
-    method public void set(float, float, float, float, int, int, android.graphics.Shader.TileMode);
   }
 
   public class MaskFilter {
@@ -13587,10 +13581,6 @@
 
   public class PorterDuffColorFilter extends android.graphics.ColorFilter {
     ctor public PorterDuffColorFilter(int, android.graphics.PorterDuff.Mode);
-    method public int getColor();
-    method public android.graphics.PorterDuff.Mode getMode();
-    method public void setColor(int);
-    method public void setMode(android.graphics.PorterDuff.Mode);
   }
 
   public class PorterDuffXfermode extends android.graphics.Xfermode {
@@ -13600,8 +13590,6 @@
   public class RadialGradient extends android.graphics.Shader {
     ctor public RadialGradient(float, float, float, int[], float[], android.graphics.Shader.TileMode);
     ctor public RadialGradient(float, float, float, int, int, android.graphics.Shader.TileMode);
-    method public void set(float, float, float, int[], float[], android.graphics.Shader.TileMode);
-    method public void set(float, float, float, int, int, android.graphics.Shader.TileMode);
   }
 
   public final class Rect implements android.os.Parcelable {
@@ -13786,8 +13774,6 @@
   public class SweepGradient extends android.graphics.Shader {
     ctor public SweepGradient(float, float, int[], float[]);
     ctor public SweepGradient(float, float, int, int);
-    method public void set(float, float, int[], float[]);
-    method public void set(float, float, int, int);
   }
 
   public class Typeface {
@@ -20801,13 +20787,10 @@
     method public boolean hasSpeedAccuracy();
     method public boolean hasVerticalAccuracy();
     method public boolean isFromMockProvider();
-    method public void removeAccuracy();
-    method public void removeAltitude();
-    method public void removeBearing();
-    method public void removeBearingAccuracy();
-    method public void removeSpeed();
-    method public void removeSpeedAccuracy();
-    method public void removeVerticalAccuracy();
+    method public deprecated void removeAccuracy();
+    method public deprecated void removeAltitude();
+    method public deprecated void removeBearing();
+    method public deprecated void removeSpeed();
     method public void reset();
     method public void set(android.location.Location);
     method public void setAccuracy(float);
@@ -22775,7 +22758,7 @@
     method public android.media.MediaPlayer.DrmInfo getDrmInfo();
     method public java.lang.String getDrmPropertyString(java.lang.String) throws android.media.MediaPlayer.NoDrmSchemeException;
     method public int getDuration();
-    method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer.NoDrmSchemeException;
+    method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer.NoDrmSchemeException;
     method public android.os.PersistableBundle getMetrics();
     method public android.media.PlaybackParams getPlaybackParams();
     method public int getSelectedTrack(int) throws java.lang.IllegalStateException;
@@ -22789,7 +22772,7 @@
     method public void pause() throws java.lang.IllegalStateException;
     method public void prepare() throws java.io.IOException, java.lang.IllegalStateException;
     method public void prepareAsync() throws java.lang.IllegalStateException;
-    method public void prepareDrm(java.util.UUID) throws android.media.MediaPlayer.ProvisioningErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException;
+    method public void prepareDrm(java.util.UUID) throws android.media.MediaPlayer.ProvisioningNetworkErrorException, android.media.MediaPlayer.ProvisioningServerErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException;
     method public byte[] provideKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.MediaPlayer.NoDrmSchemeException;
     method public void release();
     method public void releaseDrm() throws android.media.MediaPlayer.NoDrmSchemeException;
@@ -22816,7 +22799,7 @@
     method public void setNextMediaPlayer(android.media.MediaPlayer);
     method public void setOnBufferingUpdateListener(android.media.MediaPlayer.OnBufferingUpdateListener);
     method public void setOnCompletionListener(android.media.MediaPlayer.OnCompletionListener);
-    method public void setOnDrmConfigListener(android.media.MediaPlayer.OnDrmConfigListener);
+    method public void setOnDrmConfigHelper(android.media.MediaPlayer.OnDrmConfigHelper);
     method public void setOnDrmInfoListener(android.media.MediaPlayer.OnDrmInfoListener);
     method public void setOnDrmInfoListener(android.media.MediaPlayer.OnDrmInfoListener, android.os.Handler);
     method public void setOnDrmPreparedListener(android.media.MediaPlayer.OnDrmPreparedListener);
@@ -22857,6 +22840,10 @@
     field public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; // 0x3
     field public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; // 0x2bc
     field public static final java.lang.String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
+    field public static final int PREPARE_DRM_STATUS_PREPARATION_ERROR = 3; // 0x3
+    field public static final int PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR = 1; // 0x1
+    field public static final int PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR = 2; // 0x2
+    field public static final int PREPARE_DRM_STATUS_SUCCESS = 0; // 0x0
     field public static final int SEEK_CLOSEST = 3; // 0x3
     field public static final int SEEK_CLOSEST_SYNC = 2; // 0x2
     field public static final int SEEK_NEXT_SYNC = 1; // 0x1
@@ -22866,7 +22853,6 @@
   }
 
   public static final class MediaPlayer.DrmInfo {
-    method public java.lang.String[] getMimes();
     method public java.util.Map<java.util.UUID, byte[]> getPssh();
     method public java.util.UUID[] getSupportedSchemes();
   }
@@ -22898,7 +22884,7 @@
     method public abstract void onCompletion(android.media.MediaPlayer);
   }
 
-  public static abstract interface MediaPlayer.OnDrmConfigListener {
+  public static abstract interface MediaPlayer.OnDrmConfigHelper {
     method public abstract void onDrmConfig(android.media.MediaPlayer);
   }
 
@@ -22907,7 +22893,7 @@
   }
 
   public static abstract interface MediaPlayer.OnDrmPreparedListener {
-    method public abstract void onDrmPrepared(android.media.MediaPlayer, boolean);
+    method public abstract void onDrmPrepared(android.media.MediaPlayer, int);
   }
 
   public static abstract interface MediaPlayer.OnErrorListener {
@@ -22938,8 +22924,12 @@
     method public abstract void onVideoSizeChanged(android.media.MediaPlayer, int, int);
   }
 
-  public static final class MediaPlayer.ProvisioningErrorException extends android.media.MediaDrmException {
-    ctor public MediaPlayer.ProvisioningErrorException(java.lang.String);
+  public static final class MediaPlayer.ProvisioningNetworkErrorException extends android.media.MediaDrmException {
+    ctor public MediaPlayer.ProvisioningNetworkErrorException(java.lang.String);
+  }
+
+  public static final class MediaPlayer.ProvisioningServerErrorException extends android.media.MediaDrmException {
+    ctor public MediaPlayer.ProvisioningServerErrorException(java.lang.String);
   }
 
   public static class MediaPlayer.TrackInfo implements android.os.Parcelable {
@@ -25528,14 +25518,10 @@
   }
 
   public final class IpSecManager {
-    method public void applyTransportModeTransform(java.net.Socket, android.net.IpSecTransform) throws java.io.IOException;
-    method public void applyTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform) throws java.io.IOException;
     method public void applyTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException;
     method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
     method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
-    method public void removeTransportModeTransform(java.net.Socket, android.net.IpSecTransform);
-    method public void removeTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform);
-    method public void removeTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform);
+    method public void removeTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException;
     method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress) throws android.net.IpSecManager.ResourceUnavailableException;
     method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
     field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0
@@ -25554,7 +25540,7 @@
   }
 
   public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable {
-    method public void close();
+    method public void close() throws java.io.IOException;
     method public int getPort();
     method public java.io.FileDescriptor getSocket();
   }
@@ -26752,8 +26738,8 @@
   }
 
   public class DiscoverySession {
-    method public java.lang.String createNetworkSpecifierOpen(android.net.wifi.aware.PeerHandle);
-    method public java.lang.String createNetworkSpecifierPassphrase(android.net.wifi.aware.PeerHandle, java.lang.String);
+    method public android.net.NetworkSpecifier createNetworkSpecifierOpen(android.net.wifi.aware.PeerHandle);
+    method public android.net.NetworkSpecifier createNetworkSpecifierPassphrase(android.net.wifi.aware.PeerHandle, java.lang.String);
     method public void destroy();
     method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[]);
   }
@@ -26839,8 +26825,8 @@
   }
 
   public class WifiAwareSession {
-    method public java.lang.String createNetworkSpecifierOpen(int, byte[]);
-    method public java.lang.String createNetworkSpecifierPassphrase(int, byte[], java.lang.String);
+    method public android.net.NetworkSpecifier createNetworkSpecifierOpen(int, byte[]);
+    method public android.net.NetworkSpecifier createNetworkSpecifierPassphrase(int, byte[], java.lang.String);
     method public void destroy();
     method public void publish(android.net.wifi.aware.PublishConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler);
     method public void subscribe(android.net.wifi.aware.SubscribeConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler);
@@ -37048,6 +37034,7 @@
   public abstract class AutofillService extends android.app.Service {
     ctor public AutofillService();
     method public final deprecated void disableSelf();
+    method public final android.service.autofill.FillEventHistory getFillEventHistory();
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onConnected();
     method public void onDisconnected();
@@ -37070,6 +37057,7 @@
     ctor public Dataset.Builder();
     method public android.service.autofill.Dataset build();
     method public android.service.autofill.Dataset.Builder setAuthentication(android.content.IntentSender);
+    method public android.service.autofill.Dataset.Builder setId(java.lang.String);
     method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue);
     method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, android.widget.RemoteViews);
   }
@@ -37087,6 +37075,23 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.FillContext> CREATOR;
   }
 
+  public final class FillEventHistory implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.os.Bundle getClientState();
+    method public java.util.List<android.service.autofill.FillEventHistory.Event> getEvents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.FillEventHistory> CREATOR;
+  }
+
+  public static final class FillEventHistory.Event {
+    method public java.lang.String getDatasetId();
+    method public int getType();
+    field public static final int TYPE_AUTHENTICATION_SELECTED = 2; // 0x2
+    field public static final int TYPE_DATASET_AUTHENTICATION_SELECTED = 1; // 0x1
+    field public static final int TYPE_DATASET_SELECTED = 0; // 0x0
+    field public static final int TYPE_SAVE_SHOWN = 3; // 0x3
+  }
+
   public final class FillRequest implements android.os.Parcelable {
     method public int describeContents();
     method public android.os.Bundle getClientState();
@@ -37111,6 +37116,7 @@
     method public android.service.autofill.FillResponse.Builder setAuthentication(android.view.autofill.AutofillId[], android.content.IntentSender, android.widget.RemoteViews);
     method public android.service.autofill.FillResponse.Builder setClientState(android.os.Bundle);
     method public deprecated android.service.autofill.FillResponse.Builder setExtras(android.os.Bundle);
+    method public android.service.autofill.FillResponse.Builder setIgnoredIds(android.view.autofill.AutofillId...);
     method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo);
   }
 
@@ -37123,6 +37129,7 @@
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.autofill.SaveInfo> CREATOR;
+    field public static final int FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE = 1; // 0x1
     field public static final int SAVE_DATA_TYPE_ADDRESS = 2; // 0x2
     field public static final int SAVE_DATA_TYPE_CREDIT_CARD = 4; // 0x4
     field public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 16; // 0x10
@@ -37135,9 +37142,9 @@
     ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]);
     method public android.service.autofill.SaveInfo build();
     method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence);
+    method public android.service.autofill.SaveInfo.Builder setFlags(int);
     method public android.service.autofill.SaveInfo.Builder setNegativeAction(java.lang.CharSequence, android.content.IntentSender);
     method public android.service.autofill.SaveInfo.Builder setOptionalIds(android.view.autofill.AutofillId[]);
-    method public android.service.autofill.SaveInfo.Builder setSaveOnAllViewsInvisible(boolean);
   }
 
   public final class SaveRequest implements android.os.Parcelable {
@@ -40906,7 +40913,6 @@
     method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException;
     method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
     method public android.content.ComponentName startService(android.content.Intent);
-    method public android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification);
     method public boolean stopService(android.content.Intent);
     method public void unbindService(android.content.ServiceConnection);
     method public void unregisterReceiver(android.content.BroadcastReceiver);
@@ -45940,7 +45946,9 @@
     field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1
     field public static final int IMPORTANT_FOR_AUTOFILL_AUTO = 0; // 0x0
     field public static final int IMPORTANT_FOR_AUTOFILL_NO = 2; // 0x2
+    field public static final int IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS = 8; // 0x8
     field public static final int IMPORTANT_FOR_AUTOFILL_YES = 1; // 0x1
+    field public static final int IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS = 4; // 0x4
     field public static final int INVISIBLE = 4; // 0x4
     field public static final int KEEP_SCREEN_ON = 67108864; // 0x4000000
     field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2
@@ -55390,6 +55398,7 @@
     method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.util.List<java.lang.Class<?>>);
     method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.lang.Class<?>...);
     method public static java.lang.invoke.MethodHandle exactInvoker(java.lang.invoke.MethodType);
+    method public static java.lang.invoke.MethodHandle explicitCastArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType);
     method public static java.lang.invoke.MethodHandle filterArguments(java.lang.invoke.MethodHandle, int, java.lang.invoke.MethodHandle...);
     method public static java.lang.invoke.MethodHandle filterReturnValue(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
     method public static java.lang.invoke.MethodHandle foldArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
diff --git a/api/removed.txt b/api/removed.txt
index 0f5b81a..b6c2a98 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -63,6 +63,12 @@
     field public static final int REQUESTED_PERMISSION_REQUIRED = 1; // 0x1
   }
 
+  public final class SharedLibraryInfo implements android.os.Parcelable {
+    method public boolean isBuiltin();
+    method public boolean isDynamic();
+    method public boolean isStatic();
+  }
+
 }
 
 package android.database {
@@ -126,6 +132,16 @@
 
 }
 
+package android.location {
+
+  public class Location implements android.os.Parcelable {
+    method public deprecated void removeBearingAccuracy();
+    method public deprecated void removeSpeedAccuracy();
+    method public deprecated void removeVerticalAccuracy();
+  }
+
+}
+
 package android.media {
 
   public final class AudioFormat implements android.os.Parcelable {
diff --git a/api/system-current.txt b/api/system-current.txt
index b216832..5dd3b88 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1834,6 +1834,7 @@
     field public static final int ic_notification_clear_all = 17301594; // 0x108005a
     field public static final int ic_notification_overlay = 17301595; // 0x108005b
     field public static final int ic_partial_secure = 17301596; // 0x108005c
+    field public static final int ic_picture_in_picture = 17301685; // 0x10800b5
     field public static final int ic_popup_disk_full = 17301597; // 0x108005d
     field public static final int ic_popup_reminder = 17301598; // 0x108005e
     field public static final int ic_popup_sync = 17301599; // 0x108005f
@@ -3989,7 +3990,7 @@
     method public deprecated java.util.List<android.app.ActivityManager.RecentTaskInfo> getRecentTasks(int, int) throws java.lang.SecurityException;
     method public java.util.List<android.app.ActivityManager.RunningAppProcessInfo> getRunningAppProcesses();
     method public android.app.PendingIntent getRunningServiceControlPanel(android.content.ComponentName) throws java.lang.SecurityException;
-    method public java.util.List<android.app.ActivityManager.RunningServiceInfo> getRunningServices(int) throws java.lang.SecurityException;
+    method public deprecated java.util.List<android.app.ActivityManager.RunningServiceInfo> getRunningServices(int) throws java.lang.SecurityException;
     method public deprecated java.util.List<android.app.ActivityManager.RunningTaskInfo> getRunningTasks(int) throws java.lang.SecurityException;
     method public int getUidImportance(int);
     method public deprecated boolean isInLockTaskMode();
@@ -4744,6 +4745,7 @@
     method public final android.app.FragmentManager getFragmentManager();
     method public final java.lang.Object getHost();
     method public final int getId();
+    method public final android.view.LayoutInflater getLayoutInflater();
     method public android.app.LoaderManager getLoaderManager();
     method public final android.app.Fragment getParentFragment();
     method public android.transition.Transition getReenterTransition();
@@ -5806,7 +5808,6 @@
     method public boolean removeAutomaticZenRule(java.lang.String);
     method public final void setInterruptionFilter(int);
     method public void setNotificationPolicy(android.app.NotificationManager.Policy);
-    method public deprecated android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification);
     method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule);
     field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED";
     field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED";
@@ -6870,7 +6871,7 @@
 
   public static class AssistStructure.ViewNode {
     method public float getAlpha();
-    method public java.lang.String[] getAutoFillHints();
+    method public java.lang.String[] getAutofillHints();
     method public android.view.autofill.AutofillId getAutofillId();
     method public java.lang.String[] getAutofillOptions();
     method public int getAutofillType();
@@ -7530,6 +7531,7 @@
     method public static void deleteAllHosts();
     method public void deleteAppWidgetId(int);
     method public void deleteHost();
+    method public int[] getAppWidgetIds();
     method protected android.appwidget.AppWidgetHostView onCreateView(android.content.Context, int, android.appwidget.AppWidgetProviderInfo);
     method protected void onProviderChanged(int, android.appwidget.AppWidgetProviderInfo);
     method protected void onProvidersChanged();
@@ -11701,12 +11703,13 @@
     method public android.content.pm.VersionedPackage getDeclaringPackage();
     method public java.util.List<android.content.pm.VersionedPackage> getDependentPackages();
     method public java.lang.String getName();
-    method public int getVersion();
-    method public boolean isBuiltin();
-    method public boolean isDynamic();
-    method public boolean isStatic();
+    method public int getType();
+    method public long getVersion();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.pm.SharedLibraryInfo> CREATOR;
+    field public static final int TYPE_BUILTIN = 0; // 0x0
+    field public static final int TYPE_DYNAMIC = 1; // 0x1
+    field public static final int TYPE_STATIC = 2; // 0x2
     field public static final int VERSION_UNDEFINED = -1; // 0xffffffff
   }
 
@@ -13374,7 +13377,6 @@
 
   public class BitmapShader extends android.graphics.Shader {
     ctor public BitmapShader(android.graphics.Bitmap, android.graphics.Shader.TileMode, android.graphics.Shader.TileMode);
-    method public void set(android.graphics.Bitmap, android.graphics.Shader.TileMode, android.graphics.Shader.TileMode);
   }
 
   public class BlurMaskFilter extends android.graphics.MaskFilter {
@@ -13627,8 +13629,6 @@
     ctor public ColorMatrixColorFilter(android.graphics.ColorMatrix);
     ctor public ColorMatrixColorFilter(float[]);
     method public void getColorMatrix(android.graphics.ColorMatrix);
-    method public void setColorMatrix(android.graphics.ColorMatrix);
-    method public void setColorMatrixArray(float[]);
   }
 
   public abstract class ColorSpace {
@@ -13769,8 +13769,6 @@
   public class ComposeShader extends android.graphics.Shader {
     ctor public ComposeShader(android.graphics.Shader, android.graphics.Shader, android.graphics.Xfermode);
     ctor public ComposeShader(android.graphics.Shader, android.graphics.Shader, android.graphics.PorterDuff.Mode);
-    method public void set(android.graphics.Shader, android.graphics.Shader, android.graphics.Xfermode);
-    method public void set(android.graphics.Shader, android.graphics.Shader, android.graphics.PorterDuff.Mode);
   }
 
   public class CornerPathEffect extends android.graphics.PathEffect {
@@ -13843,15 +13841,11 @@
     ctor public LightingColorFilter(int, int);
     method public int getColorAdd();
     method public int getColorMultiply();
-    method public void setColorAdd(int);
-    method public void setColorMultiply(int);
   }
 
   public class LinearGradient extends android.graphics.Shader {
     ctor public LinearGradient(float, float, float, float, int[], float[], android.graphics.Shader.TileMode);
     ctor public LinearGradient(float, float, float, float, int, int, android.graphics.Shader.TileMode);
-    method public void set(float, float, float, float, int[], float[], android.graphics.Shader.TileMode);
-    method public void set(float, float, float, float, int, int, android.graphics.Shader.TileMode);
   }
 
   public class MaskFilter {
@@ -14361,10 +14355,6 @@
 
   public class PorterDuffColorFilter extends android.graphics.ColorFilter {
     ctor public PorterDuffColorFilter(int, android.graphics.PorterDuff.Mode);
-    method public int getColor();
-    method public android.graphics.PorterDuff.Mode getMode();
-    method public void setColor(int);
-    method public void setMode(android.graphics.PorterDuff.Mode);
   }
 
   public class PorterDuffXfermode extends android.graphics.Xfermode {
@@ -14374,8 +14364,6 @@
   public class RadialGradient extends android.graphics.Shader {
     ctor public RadialGradient(float, float, float, int[], float[], android.graphics.Shader.TileMode);
     ctor public RadialGradient(float, float, float, int, int, android.graphics.Shader.TileMode);
-    method public void set(float, float, float, int[], float[], android.graphics.Shader.TileMode);
-    method public void set(float, float, float, int, int, android.graphics.Shader.TileMode);
   }
 
   public final class Rect implements android.os.Parcelable {
@@ -14560,8 +14548,6 @@
   public class SweepGradient extends android.graphics.Shader {
     ctor public SweepGradient(float, float, int[], float[]);
     ctor public SweepGradient(float, float, int, int);
-    method public void set(float, float, int[], float[]);
-    method public void set(float, float, int, int);
   }
 
   public class Typeface {
@@ -22535,13 +22521,10 @@
     method public boolean isComplete();
     method public boolean isFromMockProvider();
     method public void makeComplete();
-    method public void removeAccuracy();
-    method public void removeAltitude();
-    method public void removeBearing();
-    method public void removeBearingAccuracy();
-    method public void removeSpeed();
-    method public void removeSpeedAccuracy();
-    method public void removeVerticalAccuracy();
+    method public deprecated void removeAccuracy();
+    method public deprecated void removeAltitude();
+    method public deprecated void removeBearing();
+    method public deprecated void removeSpeed();
     method public void reset();
     method public void set(android.location.Location);
     method public void setAccuracy(float);
@@ -24611,7 +24594,7 @@
     method public android.media.MediaPlayer.DrmInfo getDrmInfo();
     method public java.lang.String getDrmPropertyString(java.lang.String) throws android.media.MediaPlayer.NoDrmSchemeException;
     method public int getDuration();
-    method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer.NoDrmSchemeException;
+    method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer.NoDrmSchemeException;
     method public android.os.PersistableBundle getMetrics();
     method public android.media.PlaybackParams getPlaybackParams();
     method public int getSelectedTrack(int) throws java.lang.IllegalStateException;
@@ -24625,7 +24608,7 @@
     method public void pause() throws java.lang.IllegalStateException;
     method public void prepare() throws java.io.IOException, java.lang.IllegalStateException;
     method public void prepareAsync() throws java.lang.IllegalStateException;
-    method public void prepareDrm(java.util.UUID) throws android.media.MediaPlayer.ProvisioningErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException;
+    method public void prepareDrm(java.util.UUID) throws android.media.MediaPlayer.ProvisioningNetworkErrorException, android.media.MediaPlayer.ProvisioningServerErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException;
     method public byte[] provideKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.MediaPlayer.NoDrmSchemeException;
     method public void release();
     method public void releaseDrm() throws android.media.MediaPlayer.NoDrmSchemeException;
@@ -24652,7 +24635,7 @@
     method public void setNextMediaPlayer(android.media.MediaPlayer);
     method public void setOnBufferingUpdateListener(android.media.MediaPlayer.OnBufferingUpdateListener);
     method public void setOnCompletionListener(android.media.MediaPlayer.OnCompletionListener);
-    method public void setOnDrmConfigListener(android.media.MediaPlayer.OnDrmConfigListener);
+    method public void setOnDrmConfigHelper(android.media.MediaPlayer.OnDrmConfigHelper);
     method public void setOnDrmInfoListener(android.media.MediaPlayer.OnDrmInfoListener);
     method public void setOnDrmInfoListener(android.media.MediaPlayer.OnDrmInfoListener, android.os.Handler);
     method public void setOnDrmPreparedListener(android.media.MediaPlayer.OnDrmPreparedListener);
@@ -24693,6 +24676,10 @@
     field public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; // 0x3
     field public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; // 0x2bc
     field public static final java.lang.String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
+    field public static final int PREPARE_DRM_STATUS_PREPARATION_ERROR = 3; // 0x3
+    field public static final int PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR = 1; // 0x1
+    field public static final int PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR = 2; // 0x2
+    field public static final int PREPARE_DRM_STATUS_SUCCESS = 0; // 0x0
     field public static final int SEEK_CLOSEST = 3; // 0x3
     field public static final int SEEK_CLOSEST_SYNC = 2; // 0x2
     field public static final int SEEK_NEXT_SYNC = 1; // 0x1
@@ -24702,7 +24689,6 @@
   }
 
   public static final class MediaPlayer.DrmInfo {
-    method public java.lang.String[] getMimes();
     method public java.util.Map<java.util.UUID, byte[]> getPssh();
     method public java.util.UUID[] getSupportedSchemes();
   }
@@ -24734,7 +24720,7 @@
     method public abstract void onCompletion(android.media.MediaPlayer);
   }
 
-  public static abstract interface MediaPlayer.OnDrmConfigListener {
+  public static abstract interface MediaPlayer.OnDrmConfigHelper {
     method public abstract void onDrmConfig(android.media.MediaPlayer);
   }
 
@@ -24743,7 +24729,7 @@
   }
 
   public static abstract interface MediaPlayer.OnDrmPreparedListener {
-    method public abstract void onDrmPrepared(android.media.MediaPlayer, boolean);
+    method public abstract void onDrmPrepared(android.media.MediaPlayer, int);
   }
 
   public static abstract interface MediaPlayer.OnErrorListener {
@@ -24774,8 +24760,12 @@
     method public abstract void onVideoSizeChanged(android.media.MediaPlayer, int, int);
   }
 
-  public static final class MediaPlayer.ProvisioningErrorException extends android.media.MediaDrmException {
-    ctor public MediaPlayer.ProvisioningErrorException(java.lang.String);
+  public static final class MediaPlayer.ProvisioningNetworkErrorException extends android.media.MediaDrmException {
+    ctor public MediaPlayer.ProvisioningNetworkErrorException(java.lang.String);
+  }
+
+  public static final class MediaPlayer.ProvisioningServerErrorException extends android.media.MediaDrmException {
+    ctor public MediaPlayer.ProvisioningServerErrorException(java.lang.String);
   }
 
   public static class MediaPlayer.TrackInfo implements android.os.Parcelable {
@@ -27717,14 +27707,10 @@
   }
 
   public final class IpSecManager {
-    method public void applyTransportModeTransform(java.net.Socket, android.net.IpSecTransform) throws java.io.IOException;
-    method public void applyTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform) throws java.io.IOException;
     method public void applyTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException;
     method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
     method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
-    method public void removeTransportModeTransform(java.net.Socket, android.net.IpSecTransform);
-    method public void removeTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform);
-    method public void removeTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform);
+    method public void removeTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException;
     method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress) throws android.net.IpSecManager.ResourceUnavailableException;
     method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
     field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0
@@ -27743,7 +27729,7 @@
   }
 
   public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable {
-    method public void close();
+    method public void close() throws java.io.IOException;
     method public int getPort();
     method public java.io.FileDescriptor getSocket();
   }
@@ -29495,9 +29481,9 @@
   }
 
   public class DiscoverySession {
-    method public java.lang.String createNetworkSpecifierOpen(android.net.wifi.aware.PeerHandle);
-    method public java.lang.String createNetworkSpecifierPassphrase(android.net.wifi.aware.PeerHandle, java.lang.String);
-    method public java.lang.String createNetworkSpecifierPmk(android.net.wifi.aware.PeerHandle, byte[]);
+    method public android.net.NetworkSpecifier createNetworkSpecifierOpen(android.net.wifi.aware.PeerHandle);
+    method public android.net.NetworkSpecifier createNetworkSpecifierPassphrase(android.net.wifi.aware.PeerHandle, java.lang.String);
+    method public android.net.NetworkSpecifier createNetworkSpecifierPmk(android.net.wifi.aware.PeerHandle, byte[]);
     method public void destroy();
     method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[]);
   }
@@ -29583,9 +29569,9 @@
   }
 
   public class WifiAwareSession {
-    method public java.lang.String createNetworkSpecifierOpen(int, byte[]);
-    method public java.lang.String createNetworkSpecifierPassphrase(int, byte[], java.lang.String);
-    method public java.lang.String createNetworkSpecifierPmk(int, byte[], byte[]);
+    method public android.net.NetworkSpecifier createNetworkSpecifierOpen(int, byte[]);
+    method public android.net.NetworkSpecifier createNetworkSpecifierPassphrase(int, byte[], java.lang.String);
+    method public android.net.NetworkSpecifier createNetworkSpecifierPmk(int, byte[], byte[]);
     method public void destroy();
     method public void publish(android.net.wifi.aware.PublishConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler);
     method public void subscribe(android.net.wifi.aware.SubscribeConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler);
@@ -34377,6 +34363,7 @@
     method public static void rebootWipeUserData(android.content.Context) throws java.io.IOException;
     method public static void scheduleUpdateOnBoot(android.content.Context, java.io.File) throws java.io.IOException;
     method public static void verifyPackage(java.io.File, android.os.RecoverySystem.ProgressListener, java.io.File) throws java.security.GeneralSecurityException, java.io.IOException;
+    method public static boolean verifyPackageCompatibility(java.io.File) throws java.io.IOException;
   }
 
   public static abstract interface RecoverySystem.ProgressListener {
@@ -40160,6 +40147,7 @@
   public abstract class AutofillService extends android.app.Service {
     ctor public AutofillService();
     method public final deprecated void disableSelf();
+    method public final android.service.autofill.FillEventHistory getFillEventHistory();
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onConnected();
     method public void onDisconnected();
@@ -40182,6 +40170,7 @@
     ctor public Dataset.Builder();
     method public android.service.autofill.Dataset build();
     method public android.service.autofill.Dataset.Builder setAuthentication(android.content.IntentSender);
+    method public android.service.autofill.Dataset.Builder setId(java.lang.String);
     method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue);
     method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, android.widget.RemoteViews);
   }
@@ -40199,6 +40188,23 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.FillContext> CREATOR;
   }
 
+  public final class FillEventHistory implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.os.Bundle getClientState();
+    method public java.util.List<android.service.autofill.FillEventHistory.Event> getEvents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.FillEventHistory> CREATOR;
+  }
+
+  public static final class FillEventHistory.Event {
+    method public java.lang.String getDatasetId();
+    method public int getType();
+    field public static final int TYPE_AUTHENTICATION_SELECTED = 2; // 0x2
+    field public static final int TYPE_DATASET_AUTHENTICATION_SELECTED = 1; // 0x1
+    field public static final int TYPE_DATASET_SELECTED = 0; // 0x0
+    field public static final int TYPE_SAVE_SHOWN = 3; // 0x3
+  }
+
   public final class FillRequest implements android.os.Parcelable {
     method public int describeContents();
     method public android.os.Bundle getClientState();
@@ -40223,6 +40229,7 @@
     method public android.service.autofill.FillResponse.Builder setAuthentication(android.view.autofill.AutofillId[], android.content.IntentSender, android.widget.RemoteViews);
     method public android.service.autofill.FillResponse.Builder setClientState(android.os.Bundle);
     method public deprecated android.service.autofill.FillResponse.Builder setExtras(android.os.Bundle);
+    method public android.service.autofill.FillResponse.Builder setIgnoredIds(android.view.autofill.AutofillId...);
     method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo);
   }
 
@@ -40235,6 +40242,7 @@
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.autofill.SaveInfo> CREATOR;
+    field public static final int FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE = 1; // 0x1
     field public static final int SAVE_DATA_TYPE_ADDRESS = 2; // 0x2
     field public static final int SAVE_DATA_TYPE_CREDIT_CARD = 4; // 0x4
     field public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 16; // 0x10
@@ -40247,9 +40255,9 @@
     ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]);
     method public android.service.autofill.SaveInfo build();
     method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence);
+    method public android.service.autofill.SaveInfo.Builder setFlags(int);
     method public android.service.autofill.SaveInfo.Builder setNegativeAction(java.lang.CharSequence, android.content.IntentSender);
     method public android.service.autofill.SaveInfo.Builder setOptionalIds(android.view.autofill.AutofillId[]);
-    method public android.service.autofill.SaveInfo.Builder setSaveOnAllViewsInvisible(boolean);
   }
 
   public final class SaveRequest implements android.os.Parcelable {
@@ -44462,7 +44470,6 @@
     method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException;
     method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
     method public android.content.ComponentName startService(android.content.Intent);
-    method public android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification);
     method public boolean stopService(android.content.Intent);
     method public void unbindService(android.content.ServiceConnection);
     method public void unregisterReceiver(android.content.BroadcastReceiver);
@@ -49515,7 +49522,9 @@
     field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1
     field public static final int IMPORTANT_FOR_AUTOFILL_AUTO = 0; // 0x0
     field public static final int IMPORTANT_FOR_AUTOFILL_NO = 2; // 0x2
+    field public static final int IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS = 8; // 0x8
     field public static final int IMPORTANT_FOR_AUTOFILL_YES = 1; // 0x1
+    field public static final int IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS = 4; // 0x4
     field public static final int INVISIBLE = 4; // 0x4
     field public static final int KEEP_SCREEN_ON = 67108864; // 0x4000000
     field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2
@@ -59333,6 +59342,7 @@
     method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.util.List<java.lang.Class<?>>);
     method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.lang.Class<?>...);
     method public static java.lang.invoke.MethodHandle exactInvoker(java.lang.invoke.MethodType);
+    method public static java.lang.invoke.MethodHandle explicitCastArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType);
     method public static java.lang.invoke.MethodHandle filterArguments(java.lang.invoke.MethodHandle, int, java.lang.invoke.MethodHandle...);
     method public static java.lang.invoke.MethodHandle filterReturnValue(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
     method public static java.lang.invoke.MethodHandle foldArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 823d88f..6d82a49 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -61,6 +61,12 @@
     field public static final int REQUESTED_PERMISSION_REQUIRED = 1; // 0x1
   }
 
+  public final class SharedLibraryInfo implements android.os.Parcelable {
+    method public boolean isBuiltin();
+    method public boolean isDynamic();
+    method public boolean isStatic();
+  }
+
 }
 
 package android.database {
@@ -124,6 +130,16 @@
 
 }
 
+package android.location {
+
+  public class Location implements android.os.Parcelable {
+    method public deprecated void removeBearingAccuracy();
+    method public deprecated void removeSpeedAccuracy();
+    method public deprecated void removeVerticalAccuracy();
+  }
+
+}
+
 package android.media {
 
   public final class AudioFormat implements android.os.Parcelable {
diff --git a/api/test-current.txt b/api/test-current.txt
index e3292f9..47a0fd8 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1710,6 +1710,7 @@
     field public static final int ic_notification_clear_all = 17301594; // 0x108005a
     field public static final int ic_notification_overlay = 17301595; // 0x108005b
     field public static final int ic_partial_secure = 17301596; // 0x108005c
+    field public static final int ic_picture_in_picture = 17301685; // 0x10800b5
     field public static final int ic_popup_disk_full = 17301597; // 0x108005d
     field public static final int ic_popup_reminder = 17301598; // 0x108005e
     field public static final int ic_popup_sync = 17301599; // 0x108005f
@@ -3852,7 +3853,7 @@
     method public deprecated java.util.List<android.app.ActivityManager.RecentTaskInfo> getRecentTasks(int, int) throws java.lang.SecurityException;
     method public java.util.List<android.app.ActivityManager.RunningAppProcessInfo> getRunningAppProcesses();
     method public android.app.PendingIntent getRunningServiceControlPanel(android.content.ComponentName) throws java.lang.SecurityException;
-    method public java.util.List<android.app.ActivityManager.RunningServiceInfo> getRunningServices(int) throws java.lang.SecurityException;
+    method public deprecated java.util.List<android.app.ActivityManager.RunningServiceInfo> getRunningServices(int) throws java.lang.SecurityException;
     method public deprecated java.util.List<android.app.ActivityManager.RunningTaskInfo> getRunningTasks(int) throws java.lang.SecurityException;
     method public int getUidImportance(int);
     method public deprecated boolean isInLockTaskMode();
@@ -4590,6 +4591,7 @@
     method public final android.app.FragmentManager getFragmentManager();
     method public final java.lang.Object getHost();
     method public final int getId();
+    method public final android.view.LayoutInflater getLayoutInflater();
     method public android.app.LoaderManager getLoaderManager();
     method public final android.app.Fragment getParentFragment();
     method public android.transition.Transition getReenterTransition();
@@ -5618,7 +5620,6 @@
     method public boolean removeAutomaticZenRule(java.lang.String);
     method public final void setInterruptionFilter(int);
     method public void setNotificationPolicy(android.app.NotificationManager.Policy);
-    method public deprecated android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification);
     method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule);
     field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED";
     field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED";
@@ -6655,7 +6656,7 @@
 
   public static class AssistStructure.ViewNode {
     method public float getAlpha();
-    method public java.lang.String[] getAutoFillHints();
+    method public java.lang.String[] getAutofillHints();
     method public android.view.autofill.AutofillId getAutofillId();
     method public java.lang.String[] getAutofillOptions();
     method public int getAutofillType();
@@ -7095,6 +7096,7 @@
     method public static void deleteAllHosts();
     method public void deleteAppWidgetId(int);
     method public void deleteHost();
+    method public int[] getAppWidgetIds();
     method protected android.appwidget.AppWidgetHostView onCreateView(android.content.Context, int, android.appwidget.AppWidgetProviderInfo);
     method protected void onProviderChanged(int, android.appwidget.AppWidgetProviderInfo);
     method protected void onProvidersChanged();
@@ -10982,12 +10984,13 @@
     method public android.content.pm.VersionedPackage getDeclaringPackage();
     method public java.util.List<android.content.pm.VersionedPackage> getDependentPackages();
     method public java.lang.String getName();
-    method public int getVersion();
-    method public boolean isBuiltin();
-    method public boolean isDynamic();
-    method public boolean isStatic();
+    method public int getType();
+    method public long getVersion();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.pm.SharedLibraryInfo> CREATOR;
+    field public static final int TYPE_BUILTIN = 0; // 0x0
+    field public static final int TYPE_DYNAMIC = 1; // 0x1
+    field public static final int TYPE_STATIC = 2; // 0x2
     field public static final int VERSION_UNDEFINED = -1; // 0xffffffff
   }
 
@@ -12642,7 +12645,6 @@
 
   public class BitmapShader extends android.graphics.Shader {
     ctor public BitmapShader(android.graphics.Bitmap, android.graphics.Shader.TileMode, android.graphics.Shader.TileMode);
-    method public void set(android.graphics.Bitmap, android.graphics.Shader.TileMode, android.graphics.Shader.TileMode);
   }
 
   public class BlurMaskFilter extends android.graphics.MaskFilter {
@@ -12895,8 +12897,6 @@
     ctor public ColorMatrixColorFilter(android.graphics.ColorMatrix);
     ctor public ColorMatrixColorFilter(float[]);
     method public void getColorMatrix(android.graphics.ColorMatrix);
-    method public void setColorMatrix(android.graphics.ColorMatrix);
-    method public void setColorMatrixArray(float[]);
   }
 
   public abstract class ColorSpace {
@@ -13037,8 +13037,6 @@
   public class ComposeShader extends android.graphics.Shader {
     ctor public ComposeShader(android.graphics.Shader, android.graphics.Shader, android.graphics.Xfermode);
     ctor public ComposeShader(android.graphics.Shader, android.graphics.Shader, android.graphics.PorterDuff.Mode);
-    method public void set(android.graphics.Shader, android.graphics.Shader, android.graphics.Xfermode);
-    method public void set(android.graphics.Shader, android.graphics.Shader, android.graphics.PorterDuff.Mode);
   }
 
   public class CornerPathEffect extends android.graphics.PathEffect {
@@ -13111,15 +13109,11 @@
     ctor public LightingColorFilter(int, int);
     method public int getColorAdd();
     method public int getColorMultiply();
-    method public void setColorAdd(int);
-    method public void setColorMultiply(int);
   }
 
   public class LinearGradient extends android.graphics.Shader {
     ctor public LinearGradient(float, float, float, float, int[], float[], android.graphics.Shader.TileMode);
     ctor public LinearGradient(float, float, float, float, int, int, android.graphics.Shader.TileMode);
-    method public void set(float, float, float, float, int[], float[], android.graphics.Shader.TileMode);
-    method public void set(float, float, float, float, int, int, android.graphics.Shader.TileMode);
   }
 
   public class MaskFilter {
@@ -13629,10 +13623,6 @@
 
   public class PorterDuffColorFilter extends android.graphics.ColorFilter {
     ctor public PorterDuffColorFilter(int, android.graphics.PorterDuff.Mode);
-    method public int getColor();
-    method public android.graphics.PorterDuff.Mode getMode();
-    method public void setColor(int);
-    method public void setMode(android.graphics.PorterDuff.Mode);
   }
 
   public class PorterDuffXfermode extends android.graphics.Xfermode {
@@ -13642,8 +13632,6 @@
   public class RadialGradient extends android.graphics.Shader {
     ctor public RadialGradient(float, float, float, int[], float[], android.graphics.Shader.TileMode);
     ctor public RadialGradient(float, float, float, int, int, android.graphics.Shader.TileMode);
-    method public void set(float, float, float, int[], float[], android.graphics.Shader.TileMode);
-    method public void set(float, float, float, int, int, android.graphics.Shader.TileMode);
   }
 
   public final class Rect implements android.os.Parcelable {
@@ -13828,8 +13816,6 @@
   public class SweepGradient extends android.graphics.Shader {
     ctor public SweepGradient(float, float, int[], float[]);
     ctor public SweepGradient(float, float, int, int);
-    method public void set(float, float, int[], float[]);
-    method public void set(float, float, int, int);
   }
 
   public class Typeface {
@@ -20907,13 +20893,10 @@
     method public boolean hasSpeedAccuracy();
     method public boolean hasVerticalAccuracy();
     method public boolean isFromMockProvider();
-    method public void removeAccuracy();
-    method public void removeAltitude();
-    method public void removeBearing();
-    method public void removeBearingAccuracy();
-    method public void removeSpeed();
-    method public void removeSpeedAccuracy();
-    method public void removeVerticalAccuracy();
+    method public deprecated void removeAccuracy();
+    method public deprecated void removeAltitude();
+    method public deprecated void removeBearing();
+    method public deprecated void removeSpeed();
     method public void reset();
     method public void set(android.location.Location);
     method public void setAccuracy(float);
@@ -22882,7 +22865,7 @@
     method public android.media.MediaPlayer.DrmInfo getDrmInfo();
     method public java.lang.String getDrmPropertyString(java.lang.String) throws android.media.MediaPlayer.NoDrmSchemeException;
     method public int getDuration();
-    method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer.NoDrmSchemeException;
+    method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer.NoDrmSchemeException;
     method public android.os.PersistableBundle getMetrics();
     method public android.media.PlaybackParams getPlaybackParams();
     method public int getSelectedTrack(int) throws java.lang.IllegalStateException;
@@ -22896,7 +22879,7 @@
     method public void pause() throws java.lang.IllegalStateException;
     method public void prepare() throws java.io.IOException, java.lang.IllegalStateException;
     method public void prepareAsync() throws java.lang.IllegalStateException;
-    method public void prepareDrm(java.util.UUID) throws android.media.MediaPlayer.ProvisioningErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException;
+    method public void prepareDrm(java.util.UUID) throws android.media.MediaPlayer.ProvisioningNetworkErrorException, android.media.MediaPlayer.ProvisioningServerErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException;
     method public byte[] provideKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.MediaPlayer.NoDrmSchemeException;
     method public void release();
     method public void releaseDrm() throws android.media.MediaPlayer.NoDrmSchemeException;
@@ -22923,7 +22906,7 @@
     method public void setNextMediaPlayer(android.media.MediaPlayer);
     method public void setOnBufferingUpdateListener(android.media.MediaPlayer.OnBufferingUpdateListener);
     method public void setOnCompletionListener(android.media.MediaPlayer.OnCompletionListener);
-    method public void setOnDrmConfigListener(android.media.MediaPlayer.OnDrmConfigListener);
+    method public void setOnDrmConfigHelper(android.media.MediaPlayer.OnDrmConfigHelper);
     method public void setOnDrmInfoListener(android.media.MediaPlayer.OnDrmInfoListener);
     method public void setOnDrmInfoListener(android.media.MediaPlayer.OnDrmInfoListener, android.os.Handler);
     method public void setOnDrmPreparedListener(android.media.MediaPlayer.OnDrmPreparedListener);
@@ -22964,6 +22947,10 @@
     field public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; // 0x3
     field public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; // 0x2bc
     field public static final java.lang.String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
+    field public static final int PREPARE_DRM_STATUS_PREPARATION_ERROR = 3; // 0x3
+    field public static final int PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR = 1; // 0x1
+    field public static final int PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR = 2; // 0x2
+    field public static final int PREPARE_DRM_STATUS_SUCCESS = 0; // 0x0
     field public static final int SEEK_CLOSEST = 3; // 0x3
     field public static final int SEEK_CLOSEST_SYNC = 2; // 0x2
     field public static final int SEEK_NEXT_SYNC = 1; // 0x1
@@ -22973,7 +22960,6 @@
   }
 
   public static final class MediaPlayer.DrmInfo {
-    method public java.lang.String[] getMimes();
     method public java.util.Map<java.util.UUID, byte[]> getPssh();
     method public java.util.UUID[] getSupportedSchemes();
   }
@@ -23005,7 +22991,7 @@
     method public abstract void onCompletion(android.media.MediaPlayer);
   }
 
-  public static abstract interface MediaPlayer.OnDrmConfigListener {
+  public static abstract interface MediaPlayer.OnDrmConfigHelper {
     method public abstract void onDrmConfig(android.media.MediaPlayer);
   }
 
@@ -23014,7 +23000,7 @@
   }
 
   public static abstract interface MediaPlayer.OnDrmPreparedListener {
-    method public abstract void onDrmPrepared(android.media.MediaPlayer, boolean);
+    method public abstract void onDrmPrepared(android.media.MediaPlayer, int);
   }
 
   public static abstract interface MediaPlayer.OnErrorListener {
@@ -23045,8 +23031,12 @@
     method public abstract void onVideoSizeChanged(android.media.MediaPlayer, int, int);
   }
 
-  public static final class MediaPlayer.ProvisioningErrorException extends android.media.MediaDrmException {
-    ctor public MediaPlayer.ProvisioningErrorException(java.lang.String);
+  public static final class MediaPlayer.ProvisioningNetworkErrorException extends android.media.MediaDrmException {
+    ctor public MediaPlayer.ProvisioningNetworkErrorException(java.lang.String);
+  }
+
+  public static final class MediaPlayer.ProvisioningServerErrorException extends android.media.MediaDrmException {
+    ctor public MediaPlayer.ProvisioningServerErrorException(java.lang.String);
   }
 
   public static class MediaPlayer.TrackInfo implements android.os.Parcelable {
@@ -25635,14 +25625,10 @@
   }
 
   public final class IpSecManager {
-    method public void applyTransportModeTransform(java.net.Socket, android.net.IpSecTransform) throws java.io.IOException;
-    method public void applyTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform) throws java.io.IOException;
     method public void applyTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException;
     method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
     method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
-    method public void removeTransportModeTransform(java.net.Socket, android.net.IpSecTransform);
-    method public void removeTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform);
-    method public void removeTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform);
+    method public void removeTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException;
     method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress) throws android.net.IpSecManager.ResourceUnavailableException;
     method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
     field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0
@@ -25661,7 +25647,7 @@
   }
 
   public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable {
-    method public void close();
+    method public void close() throws java.io.IOException;
     method public int getPort();
     method public java.io.FileDescriptor getSocket();
   }
@@ -26859,8 +26845,8 @@
   }
 
   public class DiscoverySession {
-    method public java.lang.String createNetworkSpecifierOpen(android.net.wifi.aware.PeerHandle);
-    method public java.lang.String createNetworkSpecifierPassphrase(android.net.wifi.aware.PeerHandle, java.lang.String);
+    method public android.net.NetworkSpecifier createNetworkSpecifierOpen(android.net.wifi.aware.PeerHandle);
+    method public android.net.NetworkSpecifier createNetworkSpecifierPassphrase(android.net.wifi.aware.PeerHandle, java.lang.String);
     method public void destroy();
     method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[]);
   }
@@ -26946,8 +26932,8 @@
   }
 
   public class WifiAwareSession {
-    method public java.lang.String createNetworkSpecifierOpen(int, byte[]);
-    method public java.lang.String createNetworkSpecifierPassphrase(int, byte[], java.lang.String);
+    method public android.net.NetworkSpecifier createNetworkSpecifierOpen(int, byte[]);
+    method public android.net.NetworkSpecifier createNetworkSpecifierPassphrase(int, byte[], java.lang.String);
     method public void destroy();
     method public void publish(android.net.wifi.aware.PublishConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler);
     method public void subscribe(android.net.wifi.aware.SubscribeConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler);
@@ -37201,6 +37187,7 @@
   public abstract class AutofillService extends android.app.Service {
     ctor public AutofillService();
     method public final deprecated void disableSelf();
+    method public final android.service.autofill.FillEventHistory getFillEventHistory();
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onConnected();
     method public void onDisconnected();
@@ -37223,6 +37210,7 @@
     ctor public Dataset.Builder();
     method public android.service.autofill.Dataset build();
     method public android.service.autofill.Dataset.Builder setAuthentication(android.content.IntentSender);
+    method public android.service.autofill.Dataset.Builder setId(java.lang.String);
     method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue);
     method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, android.widget.RemoteViews);
   }
@@ -37240,6 +37228,23 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.FillContext> CREATOR;
   }
 
+  public final class FillEventHistory implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.os.Bundle getClientState();
+    method public java.util.List<android.service.autofill.FillEventHistory.Event> getEvents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.FillEventHistory> CREATOR;
+  }
+
+  public static final class FillEventHistory.Event {
+    method public java.lang.String getDatasetId();
+    method public int getType();
+    field public static final int TYPE_AUTHENTICATION_SELECTED = 2; // 0x2
+    field public static final int TYPE_DATASET_AUTHENTICATION_SELECTED = 1; // 0x1
+    field public static final int TYPE_DATASET_SELECTED = 0; // 0x0
+    field public static final int TYPE_SAVE_SHOWN = 3; // 0x3
+  }
+
   public final class FillRequest implements android.os.Parcelable {
     method public int describeContents();
     method public android.os.Bundle getClientState();
@@ -37264,6 +37269,7 @@
     method public android.service.autofill.FillResponse.Builder setAuthentication(android.view.autofill.AutofillId[], android.content.IntentSender, android.widget.RemoteViews);
     method public android.service.autofill.FillResponse.Builder setClientState(android.os.Bundle);
     method public deprecated android.service.autofill.FillResponse.Builder setExtras(android.os.Bundle);
+    method public android.service.autofill.FillResponse.Builder setIgnoredIds(android.view.autofill.AutofillId...);
     method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo);
   }
 
@@ -37276,6 +37282,7 @@
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.autofill.SaveInfo> CREATOR;
+    field public static final int FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE = 1; // 0x1
     field public static final int SAVE_DATA_TYPE_ADDRESS = 2; // 0x2
     field public static final int SAVE_DATA_TYPE_CREDIT_CARD = 4; // 0x4
     field public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 16; // 0x10
@@ -37288,9 +37295,9 @@
     ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]);
     method public android.service.autofill.SaveInfo build();
     method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence);
+    method public android.service.autofill.SaveInfo.Builder setFlags(int);
     method public android.service.autofill.SaveInfo.Builder setNegativeAction(java.lang.CharSequence, android.content.IntentSender);
     method public android.service.autofill.SaveInfo.Builder setOptionalIds(android.view.autofill.AutofillId[]);
-    method public android.service.autofill.SaveInfo.Builder setSaveOnAllViewsInvisible(boolean);
   }
 
   public final class SaveRequest implements android.os.Parcelable {
@@ -41101,7 +41108,6 @@
     method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException;
     method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
     method public android.content.ComponentName startService(android.content.Intent);
-    method public android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification);
     method public boolean stopService(android.content.Intent);
     method public void unbindService(android.content.ServiceConnection);
     method public void unregisterReceiver(android.content.BroadcastReceiver);
@@ -46310,7 +46316,9 @@
     field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1
     field public static final int IMPORTANT_FOR_AUTOFILL_AUTO = 0; // 0x0
     field public static final int IMPORTANT_FOR_AUTOFILL_NO = 2; // 0x2
+    field public static final int IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS = 8; // 0x8
     field public static final int IMPORTANT_FOR_AUTOFILL_YES = 1; // 0x1
+    field public static final int IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS = 4; // 0x4
     field public static final int INVISIBLE = 4; // 0x4
     field public static final int KEEP_SCREEN_ON = 67108864; // 0x4000000
     field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2
@@ -55783,6 +55791,7 @@
     method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.util.List<java.lang.Class<?>>);
     method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.lang.Class<?>...);
     method public static java.lang.invoke.MethodHandle exactInvoker(java.lang.invoke.MethodType);
+    method public static java.lang.invoke.MethodHandle explicitCastArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType);
     method public static java.lang.invoke.MethodHandle filterArguments(java.lang.invoke.MethodHandle, int, java.lang.invoke.MethodHandle...);
     method public static java.lang.invoke.MethodHandle filterReturnValue(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
     method public static java.lang.invoke.MethodHandle foldArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
diff --git a/api/test-removed.txt b/api/test-removed.txt
index 0f5b81a..b6c2a98 100644
--- a/api/test-removed.txt
+++ b/api/test-removed.txt
@@ -63,6 +63,12 @@
     field public static final int REQUESTED_PERMISSION_REQUIRED = 1; // 0x1
   }
 
+  public final class SharedLibraryInfo implements android.os.Parcelable {
+    method public boolean isBuiltin();
+    method public boolean isDynamic();
+    method public boolean isStatic();
+  }
+
 }
 
 package android.database {
@@ -126,6 +132,16 @@
 
 }
 
+package android.location {
+
+  public class Location implements android.os.Parcelable {
+    method public deprecated void removeBearingAccuracy();
+    method public deprecated void removeSpeedAccuracy();
+    method public deprecated void removeVerticalAccuracy();
+  }
+
+}
+
 package android.media {
 
   public final class AudioFormat implements android.os.Parcelable {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 169dcb0..c4b7ed7 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2070,6 +2070,7 @@
             if (args == null) {
                 throw new IllegalArgumentException("Expected non-null picture-in-picture args");
             }
+            updatePictureInPictureArgsForContentInsets(args);
             return ActivityManagerNative.getDefault().enterPictureInPictureMode(mToken, args);
         } catch (RemoteException e) {
             return false;
@@ -2087,11 +2088,27 @@
             if (args == null) {
                 throw new IllegalArgumentException("Expected non-null picture-in-picture args");
             }
+            updatePictureInPictureArgsForContentInsets(args);
             ActivityManagerNative.getDefault().setPictureInPictureArgs(mToken, args);
         } catch (RemoteException e) {
         }
     }
 
+    /**
+     * Updates the provided {@param args} with the last known content insets for this activity, to
+     * be used with the source hint rect for the transition into PiP.
+     */
+    private void updatePictureInPictureArgsForContentInsets(PictureInPictureArgs args) {
+        if (args != null && args.hasSourceBoundsHint() && getWindow() != null &&
+                getWindow().peekDecorView() != null &&
+                getWindow().peekDecorView().getViewRootImpl() != null) {
+            args.setSourceRectHintInsets(
+                    getWindow().peekDecorView().getViewRootImpl().getLastContentInsets());
+        } else {
+            args.setSourceRectHintInsets(null);
+        }
+    }
+
     void dispatchMovedToDisplay(int displayId, Configuration config) {
         updateDisplay(displayId);
         onMovedToDisplay(displayId, config);
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 66167a3..a4dcf63 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -2564,6 +2564,10 @@
      * <p><b>Note: this method is only intended for debugging or implementing
      * service management type user interfaces.</b></p>
      *
+     * @deprecated As of {@link android.os.Build.VERSION_CODES#O}, this method
+     * is no longer available to third party applications.  For backwards compatibility,
+     * it will still return the caller's own services.
+     *
      * @param maxNum The maximum number of entries to return in the list.  The
      * actual number returned may be smaller, depending on how many services
      * are running.
@@ -2571,6 +2575,7 @@
      * @return Returns a list of RunningServiceInfo records describing each of
      * the running tasks.
      */
+    @Deprecated
     public List<RunningServiceInfo> getRunningServices(int maxNum)
             throws SecurityException {
         try {
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 02d1884..63e8cc6 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -1211,9 +1211,6 @@
      * methods that take an options Bundle.
      */
     public Bundle toBundle() {
-        if (mAnimationType == ANIM_DEFAULT) {
-            return null;
-        }
         Bundle b = new Bundle();
         if (mPackageName != null) {
             b.putString(KEY_PACKAGE_NAME, mPackageName);
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index e07b7e4..2ce02df 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -79,6 +79,8 @@
 import android.os.storage.VolumeInfo;
 import android.provider.Settings;
 import android.util.ArrayMap;
+import android.util.IconDrawableFactory;
+import android.util.LauncherIcons;
 import android.util.Log;
 import android.view.Display;
 
@@ -1245,16 +1247,9 @@
         if (!isManagedProfile(user.getIdentifier())) {
             return icon;
         }
-        Drawable badgeShadow = getDrawable("system",
-                com.android.internal.R.drawable.ic_corp_icon_badge_shadow, null);
-        Drawable badgeColor = getDrawable("system",
-                com.android.internal.R.drawable.ic_corp_icon_badge_color, null);
-        badgeColor.setTint(getUserBadgeColor(user));
-        Drawable badgeForeground = getDrawable("system",
-                com.android.internal.R.drawable.ic_corp_icon_badge_case, null);
-
-        Drawable badge = new LayerDrawable(
-                new Drawable[] {badgeShadow, badgeColor, badgeForeground });
+        Drawable badge = new LauncherIcons(mContext).getBadgeDrawable(
+                com.android.internal.R.drawable.ic_corp_icon_badge_case,
+                getUserBadgeColor(user));
         return getBadgedDrawable(icon, badge, null, true);
     }
 
@@ -1268,14 +1263,6 @@
         return getBadgedDrawable(drawable, badgeDrawable, badgeLocation, true);
     }
 
-    // Should have enough colors to cope with UserManagerService.getMaxManagedProfiles()
-    @VisibleForTesting
-    public static final int[] CORP_BADGE_COLORS = new int[] {
-        com.android.internal.R.color.profile_badge_1,
-        com.android.internal.R.color.profile_badge_2,
-        com.android.internal.R.color.profile_badge_3
-    };
-
     @VisibleForTesting
     public static final int[] CORP_BADGE_LABEL_RES_ID = new int[] {
         com.android.internal.R.string.managed_profile_label_badge,
@@ -1284,12 +1271,7 @@
     };
 
     private int getUserBadgeColor(UserHandle user) {
-        int badge = getUserManager().getManagedProfileBadge(user.getIdentifier());
-        if (badge < 0) {
-            badge = 0;
-        }
-        int resourceId = CORP_BADGE_COLORS[badge % CORP_BADGE_COLORS.length];
-        return Resources.getSystem().getColor(resourceId, null);
+        return IconDrawableFactory.getUserBadgeColor(getUserManager(), user.getIdentifier());
     }
 
     @Override
@@ -2648,4 +2630,13 @@
             throw e.rethrowAsRuntimeException();
         }
     }
+
+    @Override
+    public String getInstantAppAndroidId(String packageName, UserHandle user) {
+        try {
+            return mPM.getInstantAppAndroidId(packageName, user.getIdentifier());
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
 }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 6cc8a14..80de64b 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1447,21 +1447,13 @@
     @Override
     public ComponentName startService(Intent service) {
         warnIfCallingFromSystemProcess();
-        return startServiceCommon(service, -1, null, false, mUser);
+        return startServiceCommon(service, false, mUser);
     }
 
     @Override
     public ComponentName startForegroundService(Intent service) {
         warnIfCallingFromSystemProcess();
-        return startServiceCommon(service, -1, null, true, mUser);
-    }
-
-    // STOPSHIP: remove when NotificationManager.startServiceInForeground() is retired
-    @Override
-    public ComponentName startServiceInForeground(Intent service,
-            int id, Notification notification) {
-        warnIfCallingFromSystemProcess();
-        return startServiceCommon(service, id, notification, false, mUser);
+        return startServiceCommon(service, true, mUser);
     }
 
     @Override
@@ -1472,29 +1464,22 @@
 
     @Override
     public ComponentName startServiceAsUser(Intent service, UserHandle user) {
-        return startServiceCommon(service, -1, null, false, user);
+        return startServiceCommon(service, false, user);
     }
 
     @Override
     public ComponentName startForegroundServiceAsUser(Intent service, UserHandle user) {
-        return startServiceCommon(service, -1, null, true, user);
+        return startServiceCommon(service, true, user);
     }
 
-    // STOPSHIP: remove when NotificationManager.startServiceInForeground() is retired
-    @Override
-    public ComponentName startServiceInForegroundAsUser(Intent service,
-            int id, Notification notification, UserHandle user) {
-        return startServiceCommon(service, id, notification, false, user);
-    }
-
-    private ComponentName startServiceCommon(Intent service, int id, Notification notification,
-            boolean requireForeground, UserHandle user) {
+    private ComponentName startServiceCommon(Intent service, boolean requireForeground,
+            UserHandle user) {
         try {
             validateServiceIntent(service);
             service.prepareToLeaveProcess(this);
             ComponentName cn = ActivityManager.getService().startService(
                 mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
-                            getContentResolver()), id, notification, requireForeground,
+                            getContentResolver()), requireForeground,
                             getOpPackageName(), user.getIdentifier());
             if (cn != null) {
                 if (cn.getPackageName().equals("!")) {
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index c7bcc54..c5b93cd 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -509,6 +509,10 @@
     // True if mHidden has been changed and the animation should be scheduled.
     boolean mHiddenChanged;
 
+    // The cached value from onGetLayoutInflater(Bundle) that will be returned from
+    // getLayoutInflater()
+    LayoutInflater mLayoutInflater;
+
     /**
      * State information that has been retrieved from a fragment instance
      * through {@link FragmentManager#saveFragmentInstanceState(Fragment)
@@ -1389,6 +1393,38 @@
     }
 
     /**
+     * Returns the cached LayoutInflater used to inflate Views of this Fragment. If
+     * {@link #onGetLayoutInflater(Bundle)} has not been called {@link #onGetLayoutInflater(Bundle)}
+     * will be called with a {@code null} argument and that value will be cached.
+     * <p>
+     * The cached LayoutInflater will be replaced immediately prior to
+     * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} and cleared immediately after
+     * {@link #onDetach()}.
+     *
+     * @return The LayoutInflater used to inflate Views of this Fragment.
+     */
+    public final LayoutInflater getLayoutInflater() {
+        if (mLayoutInflater == null) {
+            return performGetLayoutInflater(null);
+        }
+        return mLayoutInflater;
+    }
+
+    /**
+     * Calls {@link #onGetLayoutInflater(Bundle)} and caches the result for use by
+     * {@link #getLayoutInflater()}.
+     *
+     * @param savedInstanceState If the fragment is being re-created from
+     * a previous saved state, this is the state.
+     * @return The LayoutInflater used to inflate Views of this Fragment.
+     */
+    LayoutInflater performGetLayoutInflater(Bundle savedInstanceState) {
+        LayoutInflater layoutInflater = onGetLayoutInflater(savedInstanceState);
+        mLayoutInflater = layoutInflater;
+        return mLayoutInflater;
+    }
+
+    /**
      * @deprecated Use {@link #onInflate(Context, AttributeSet, Bundle)} instead.
      */
     @Deprecated
@@ -2835,6 +2871,7 @@
     void performDetach() {
         mCalled = false;
         onDetach();
+        mLayoutInflater = null;
         if (!mCalled) {
             throw new SuperNotCalledException("Fragment " + this
                     + " did not call through to super.onDetach()");
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 91578a2..c1161a2 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -1259,7 +1259,7 @@
                                 }
                             }
                             f.mContainer = container;
-                            f.mView = f.performCreateView(f.onGetLayoutInflater(
+                            f.mView = f.performCreateView(f.performGetLayoutInflater(
                                     f.mSavedFragmentState), container, f.mSavedFragmentState);
                             if (f.mView != null) {
                                 f.mView.setSaveFromParentEnabled(false);
@@ -1431,7 +1431,7 @@
 
     void ensureInflatedFragmentView(Fragment f) {
         if (f.mFromLayout && !f.mPerformedCreateView) {
-            f.mView = f.performCreateView(f.onGetLayoutInflater(
+            f.mView = f.performCreateView(f.performGetLayoutInflater(
                     f.mSavedFragmentState), null, f.mSavedFragmentState);
             if (f.mView != null) {
                 f.mView.setSaveFromParentEnabled(false);
@@ -1462,18 +1462,22 @@
                     if (fragment.isHideReplaced()) {
                         fragment.setHideReplaced(false);
                     } else {
+                        final ViewGroup container = fragment.mContainer;
+                        final View animatingView = fragment.mView;
+                        container.startViewTransition(animatingView);
                         // Delay the actual hide operation until the animation finishes, otherwise
                         // the fragment will just immediately disappear
                         anim.addListener(new AnimatorListenerAdapter() {
                             @Override
                             public void onAnimationEnd(Animator animation) {
+                                container.endViewTransition(animatingView);
                                 animation.removeListener(this);
-                                if (fragment.mView != null) {
-                                    fragment.mView.setVisibility(View.GONE);
-                                }
+                                animatingView.setVisibility(View.GONE);
                             }
                         });
                     }
+                } else {
+                    fragment.mView.setVisibility(View.VISIBLE);
                 }
                 setHWLayerAnimListenerIfAlpha(fragment.mView, anim);
                 anim.start();
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 4210d2e..d270244 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -129,8 +129,7 @@
     void finishSubActivity(in IBinder token, in String resultWho, int requestCode);
     PendingIntent getRunningServiceControlPanel(in ComponentName service);
     ComponentName startService(in IApplicationThread caller, in Intent service,
-            in String resolvedType, int id, in Notification notification,
-            boolean requireForeground, in String callingPackage, int userId);
+            in String resolvedType, boolean requireForeground, in String callingPackage, int userId);
     int stopService(in IApplicationThread caller, in Intent service,
             in String resolvedType, int userId);
     int bindService(in IApplicationThread caller, in IBinder token, in Intent service,
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 72c5978..242d4a5 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1155,40 +1155,4 @@
         }
     }
 
-    /**
-     * Start a service directly into the "foreground service" state.  Unlike
-     * {@link android.content.Context#startService(Intent)}, this method
-     * can be used from within background operations like broadcast receivers
-     * or scheduled jobs.
-     *
-     * @param service Description of the service to be started.  The Intent must be either
-     *      fully explicit (supplying a component name) or specify a specific package
-     *      name it is targeted to.
-     * @param id The identifier for this notification as per
-     *      {@link #notify(int, Notification) NotificationManager.notify(int, Notification)};
-     *      must not be 0.
-     * @param notification The Notification to be displayed.
-     * @return If the service is being started or is already running, the
-     *      {@link ComponentName} of the actual service that was started is
-     *      returned; else if the service does not exist null is returned.
-     *
-     * @deprecated STOPSHIP transition away from this for O
-     */
-    @Nullable
-    @Deprecated
-    public ComponentName startServiceInForeground(Intent service,
-            int id, Notification notification) {
-        return mContext.startServiceInForeground(service, id, notification);
-    }
-
-    /**
-     * @hide like {@link #startServiceInForeground(Intent, int, Notification)}
-     * but for a specific user.
-     */
-    @Nullable
-    public ComponentName startServiceInForegroundAsUser(Intent service,
-            int id, Notification notification, UserHandle user) {
-        return mContext.startServiceInForegroundAsUser(service, id, notification, user);
-    }
-
 }
diff --git a/core/java/android/app/PictureInPictureArgs.java b/core/java/android/app/PictureInPictureArgs.java
index 0ce5eeb..2fa6360 100644
--- a/core/java/android/app/PictureInPictureArgs.java
+++ b/core/java/android/app/PictureInPictureArgs.java
@@ -49,6 +49,13 @@
     @Nullable
     private Rect mSourceRectHint;
 
+    /**
+     * The content insets that are used with the source hint rect for the transition into PiP where
+     * the insets are removed at the beginning of the transition.
+     */
+    @Nullable
+    private Rect mSourceRectHintInsets;
+
     PictureInPictureArgs(Parcel in) {
         if (in.readInt() != 0) {
             mAspectRatio = in.readFloat();
@@ -60,6 +67,9 @@
         if (in.readInt() != 0) {
             mSourceRectHint = Rect.CREATOR.createFromParcel(in);
         }
+        if (in.readInt() != 0) {
+            mSourceRectHintInsets = Rect.CREATOR.createFromParcel(in);
+        }
     }
 
     /**
@@ -94,6 +104,9 @@
         if (otherArgs.hasSourceBoundsHint()) {
             mSourceRectHint = new Rect(otherArgs.getSourceRectHint());
         }
+        if (otherArgs.hasSourceBoundsHintInsets()) {
+            mSourceRectHintInsets = new Rect(otherArgs.getSourceRectHintInsets());
+        }
     }
 
     /**
@@ -167,7 +180,19 @@
     }
 
     /**
-     * @return the launch bounds
+     * Sets the insets to be used with the source rect hint bounds.
+     * @hide
+     */
+    public void setSourceRectHintInsets(Rect insets) {
+        if (insets == null) {
+            mSourceRectHintInsets = null;
+        } else {
+            mSourceRectHintInsets = new Rect(insets);
+        }
+    }
+
+    /**
+     * @return the source rect hint
      * @hide
      */
     public Rect getSourceRectHint() {
@@ -175,6 +200,14 @@
     }
 
     /**
+     * @return the source rect hint insets.
+     * @hide
+     */
+    public Rect getSourceRectHintInsets() {
+        return mSourceRectHintInsets;
+    }
+
+    /**
      * @return whether there are launch bounds set
      * @hide
      */
@@ -182,12 +215,23 @@
         return mSourceRectHint != null && !mSourceRectHint.isEmpty();
     }
 
+    /**
+     * @return whether there are source rect hint insets set
+     * @hide
+     */
+    public boolean hasSourceBoundsHintInsets() {
+        return mSourceRectHintInsets != null;
+    }
+
     @Override
     public PictureInPictureArgs clone() {
         PictureInPictureArgs args = new PictureInPictureArgs(mAspectRatio, mUserActions);
         if (mSourceRectHint != null) {
             args.setSourceRectHint(mSourceRectHint);
         }
+        if (mSourceRectHintInsets != null) {
+            args.setSourceRectHintInsets(mSourceRectHintInsets);
+        }
         return args;
     }
 
@@ -216,6 +260,12 @@
         } else {
             out.writeInt(0);
         }
+        if (mSourceRectHintInsets != null) {
+            out.writeInt(1);
+            mSourceRectHintInsets.writeToParcel(out, 0);
+        } else {
+            out.writeInt(0);
+        }
     }
 
     public static final Creator<PictureInPictureArgs> CREATOR =
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 545aef5..9f911f5 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -964,6 +964,16 @@
          *
          * @return The hints for this view
          */
+        @Nullable public String[] getAutofillHints() {
+            return mAutofillHints;
+        }
+
+        /**
+         * @hide
+         * @deprecated use getAutofillHints() instead.
+         */
+        // TODO(b/33197203): remove once clients don't use it anymore...
+        @Deprecated
         @Nullable public String[] getAutoFillHints() {
             return mAutofillHints;
         }
diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java
index 6ba52b7..c1ff580 100644
--- a/core/java/android/appwidget/AppWidgetHost.java
+++ b/core/java/android/appwidget/AppWidgetHost.java
@@ -276,8 +276,6 @@
 
     /**
      * Gets a list of all the appWidgetIds that are bound to the current host
-     *
-     * @hide
      */
     public int[] getAppWidgetIds() {
         try {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 18120c7..46a7042 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -57,6 +57,7 @@
 import android.os.StatFs;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.storage.StorageManager;
 import android.provider.MediaStore;
 import android.util.AttributeSet;
 import android.view.Display;
@@ -1146,16 +1147,26 @@
 
     /**
      * Returns the absolute path to the application specific cache directory on
-     * the filesystem. These files will be ones that get deleted first when the
-     * device runs low on storage. There is no guarantee when these files will
-     * be deleted.
+     * the filesystem.
      * <p>
-     * <strong>Note: you should not <em>rely</em> on the system deleting these
-     * files for you; you should always have a reasonable maximum, such as 1 MB,
-     * for the amount of space you consume with cache files, and prune those
-     * files when exceeding that space.</strong> If your app requires a larger
-     * cache (larger than 1 MB), you should use {@link #getExternalCacheDir()}
-     * instead.
+     * The system will automatically delete files in this directory as disk
+     * space is needed elsewhere on the device. The system will always delete
+     * older files first, as reported by {@link File#lastModified()}. If
+     * desired, you can exert more control over how files are deleted using
+     * {@link StorageManager#setCacheBehaviorGroup(File, boolean)} and
+     * {@link StorageManager#setCacheBehaviorTombstone(File, boolean)}.
+     * <p>
+     * Apps are strongly encouraged to keep their usage of cache space below the
+     * quota returned by
+     * {@link StorageManager#getCacheQuotaBytes(java.util.UUID)}. If your app
+     * goes above this quota, your cached files will be some of the first to be
+     * deleted when additional disk space is needed. Conversely, if your app
+     * stays under this quota, your cached files will be some of the last to be
+     * deleted when additional disk space is needed.
+     * <p>
+     * Note that your cache quota will change over time depending on how
+     * frequently the user interacts with your app, and depending on how much
+     * system-wide disk space is used.
      * <p>
      * The returned path may change over time if the calling app is moved to an
      * adopted storage device, so only relative paths should be persisted.
@@ -1173,9 +1184,11 @@
 
     /**
      * Returns the absolute path to the application specific cache directory on
-     * the filesystem designed for storing cached code. The system will delete
-     * any files stored in this location both when your specific application is
-     * upgraded, and when the entire platform is upgraded.
+     * the filesystem designed for storing cached code.
+     * <p>
+     * The system will delete any files stored in this location both when your
+     * specific application is upgraded, and when the entire platform is
+     * upgraded.
      * <p>
      * This location is optimal for storing compiled or optimized code generated
      * by your application at runtime.
@@ -2647,18 +2660,6 @@
     public abstract ComponentName startForegroundServiceAsUser(Intent service, UserHandle user);
 
     /**
-     * Start a service directly into the "foreground service" state.  Unlike {@link #startService},
-     * this method can be used from within background operations like broadcast receivers
-     * or scheduled jobs.  The API entry point for this is in NotificationManager in order to
-     * preserve appropriate public package layering.
-     * @hide
-     * @deprecated STOPSHIP remove in favor of two-step startForegroundService() + startForeground()
-     */
-    @Nullable
-    public abstract ComponentName startServiceInForeground(Intent service,
-            int id, Notification notification);
-
-    /**
      * Request that a given application service be stopped.  If the service is
      * not running, nothing happens.  Otherwise it is stopped.  Note that calls
      * to startService() are not counted -- this stops the service no matter
@@ -2696,16 +2697,6 @@
     public abstract ComponentName startServiceAsUser(Intent service, UserHandle user);
 
     /**
-     * @hide like {@link #startServiceInForeground(Intent, int, Notification)}
-     * but for a specific user.
-     * @deprecated STOPSHIP remove when trial API is turned off
-     */
-    @Deprecated
-    @Nullable
-    public abstract ComponentName startServiceInForegroundAsUser(Intent service,
-            int id, Notification notification, UserHandle user);
-
-    /**
      * @hide like {@link #stopService(Intent)} but for a specific user.
      */
     public abstract boolean stopServiceAsUser(Intent service, UserHandle user);
@@ -3744,6 +3735,7 @@
     public static final String PRINT_SERVICE = "print";
 
     /**
+     * Use with {@link #getSystemService} to retrieve a
      * {@link android.companion.CompanionDeviceManager} for managing companion devices
      *
      * @see #getSystemService
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 53b021c..b59fc3dd 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -649,13 +649,6 @@
         return mBase.startForegroundService(service);
     }
 
-    /** @hide STOPSHIP remove when trial API is turned down */
-    @Override
-    public ComponentName startServiceInForeground(Intent service,
-            int id, Notification notification) {
-        return mBase.startServiceInForeground(service, id, notification);
-    }
-
     @Override
     public boolean stopService(Intent name) {
         return mBase.stopService(name);
@@ -673,13 +666,6 @@
         return mBase.startForegroundServiceAsUser(service, user);
     }
 
-    /** @hide STOPSHIP removed when trial API is turned down */
-    @Override
-    public ComponentName startServiceInForegroundAsUser(Intent service,
-            int id, Notification notification, UserHandle user) {
-        return mBase.startServiceInForegroundAsUser(service, id, notification, user);
-    }
-
     /** @hide */
     @Override
     public boolean stopServiceAsUser(Intent name, UserHandle user) {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 5ca4fa3..11eab61 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -8616,6 +8616,32 @@
      *         a single statement.
      * @see #setFlags(int)
      * @see #removeFlags(int)
+     *
+     * @see #FLAG_GRANT_READ_URI_PERMISSION
+     * @see #FLAG_GRANT_WRITE_URI_PERMISSION
+     * @see #FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+     * @see #FLAG_GRANT_PREFIX_URI_PERMISSION
+     * @see #FLAG_DEBUG_LOG_RESOLUTION
+     * @see #FLAG_FROM_BACKGROUND
+     * @see #FLAG_ACTIVITY_BROUGHT_TO_FRONT
+     * @see #FLAG_ACTIVITY_CLEAR_TASK
+     * @see #FLAG_ACTIVITY_CLEAR_TOP
+     * @see #FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
+     * @see #FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+     * @see #FLAG_ACTIVITY_FORWARD_RESULT
+     * @see #FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
+     * @see #FLAG_ACTIVITY_MULTIPLE_TASK
+     * @see #FLAG_ACTIVITY_NEW_DOCUMENT
+     * @see #FLAG_ACTIVITY_NEW_TASK
+     * @see #FLAG_ACTIVITY_NO_ANIMATION
+     * @see #FLAG_ACTIVITY_NO_HISTORY
+     * @see #FLAG_ACTIVITY_NO_USER_ACTION
+     * @see #FLAG_ACTIVITY_PREVIOUS_IS_TOP
+     * @see #FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+     * @see #FLAG_ACTIVITY_REORDER_TO_FRONT
+     * @see #FLAG_ACTIVITY_SINGLE_TOP
+     * @see #FLAG_ACTIVITY_TASK_ON_HOME
+     * @see #FLAG_RECEIVER_REGISTERED_ONLY
      */
     public Intent addFlags(int flags) {
         mFlags |= flags;
@@ -8628,6 +8654,32 @@
      * @param flags The flags to remove.
      * @see #setFlags(int)
      * @see #addFlags(int)
+     *
+     * @see #FLAG_GRANT_READ_URI_PERMISSION
+     * @see #FLAG_GRANT_WRITE_URI_PERMISSION
+     * @see #FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+     * @see #FLAG_GRANT_PREFIX_URI_PERMISSION
+     * @see #FLAG_DEBUG_LOG_RESOLUTION
+     * @see #FLAG_FROM_BACKGROUND
+     * @see #FLAG_ACTIVITY_BROUGHT_TO_FRONT
+     * @see #FLAG_ACTIVITY_CLEAR_TASK
+     * @see #FLAG_ACTIVITY_CLEAR_TOP
+     * @see #FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
+     * @see #FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+     * @see #FLAG_ACTIVITY_FORWARD_RESULT
+     * @see #FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
+     * @see #FLAG_ACTIVITY_MULTIPLE_TASK
+     * @see #FLAG_ACTIVITY_NEW_DOCUMENT
+     * @see #FLAG_ACTIVITY_NEW_TASK
+     * @see #FLAG_ACTIVITY_NO_ANIMATION
+     * @see #FLAG_ACTIVITY_NO_HISTORY
+     * @see #FLAG_ACTIVITY_NO_USER_ACTION
+     * @see #FLAG_ACTIVITY_PREVIOUS_IS_TOP
+     * @see #FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+     * @see #FLAG_ACTIVITY_REORDER_TO_FRONT
+     * @see #FLAG_ACTIVITY_SINGLE_TOP
+     * @see #FLAG_ACTIVITY_TASK_ON_HOME
+     * @see #FLAG_RECEIVER_REGISTERED_ONLY
      */
     public void removeFlags(int flags) {
         mFlags &= ~flags;
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 8b2809a..b0f8aa7 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1203,7 +1203,12 @@
         dest.writeInt(requiresSmallestWidthDp);
         dest.writeInt(compatibleWidthLimitDp);
         dest.writeInt(largestWidthLimitDp);
-        dest.writeUuid(storageUuid);
+        if (storageUuid != null) {
+            dest.writeInt(1);
+            dest.writeUuid(storageUuid);
+        } else {
+            dest.writeInt(0);
+        }
         dest.writeString(scanSourceDir);
         dest.writeString(scanPublicSourceDir);
         dest.writeString(sourceDir);
@@ -1265,8 +1270,10 @@
         requiresSmallestWidthDp = source.readInt();
         compatibleWidthLimitDp = source.readInt();
         largestWidthLimitDp = source.readInt();
-        storageUuid = source.readUuid();
-        volumeUuid = StorageManager.convert(storageUuid);
+        if (source.readInt() != 0) {
+            storageUuid = source.readUuid();
+            volumeUuid = StorageManager.convert(storageUuid);
+        }
         scanSourceDir = source.readString();
         scanPublicSourceDir = source.readString();
         sourceDir = source.readString();
diff --git a/core/java/android/content/pm/ChangedPackages.java b/core/java/android/content/pm/ChangedPackages.java
index 94b8a5d..78c057d 100644
--- a/core/java/android/content/pm/ChangedPackages.java
+++ b/core/java/android/content/pm/ChangedPackages.java
@@ -17,6 +17,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.content.Intent;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -26,6 +27,7 @@
 /**
  * Packages that have been changed since the last time they
  * were requested.
+ * @see PackageManager#getChangedPackages(int)
  */
 public final class ChangedPackages implements Parcelable {
     /** The last known sequence number for these changes */
@@ -33,6 +35,7 @@
     /** The names of the packages that have changed */
     private final List<String> mPackageNames;
 
+    @TestApi
     public ChangedPackages(int sequenceNumber, @NonNull List<String> packageNames) {
         this.mSequenceNumber = sequenceNumber;
         this.mPackageNames = packageNames;
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index bbc942a..bc7a612 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -634,4 +634,6 @@
     ComponentName getInstantAppResolverSettingsComponent();
 
     ComponentName getInstantAppInstallerComponent();
+
+    String getInstantAppAndroidId(String packageName, int userId);
 }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 09906be..7cc2334 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -54,6 +54,7 @@
 import android.os.UserManager;
 import android.os.storage.StorageManager;
 import android.os.storage.VolumeInfo;
+import android.provider.Settings;
 import android.util.AndroidException;
 import android.util.Log;
 
@@ -3983,6 +3984,8 @@
      * <p>If no packages have been changed, returns <code>null</code>.
      * <p>The sequence number starts at <code>0</code> and is
      * reset every boot.
+     * @param sequenceNumber The first sequence number for which to retrieve package changes.
+     * @see Settings.Global#BOOT_COUNT
      */
     public abstract @Nullable ChangedPackages getChangedPackages(
             @IntRange(from=0) int sequenceNumber);
@@ -5937,8 +5940,20 @@
      * <p>
      * This hint can only be set by the app which installed this package, as
      * determined by {@link #getInstallerPackageName(String)}.
+     *
+     * @param packageName the package to change the category hint for.
+     * @param categoryHint the category hint to set; one of
+     *            {@link ApplicationInfo#CATEGORY_AUDIO},
+     *            {@link ApplicationInfo#CATEGORY_GAME},
+     *            {@link ApplicationInfo#CATEGORY_IMAGE},
+     *            {@link ApplicationInfo#CATEGORY_MAPS},
+     *            {@link ApplicationInfo#CATEGORY_NEWS},
+     *            {@link ApplicationInfo#CATEGORY_PRODUCTIVITY},
+     *            {@link ApplicationInfo#CATEGORY_SOCIAL},
+     *            {@link ApplicationInfo#CATEGORY_UNDEFINED}, or
+     *            {@link ApplicationInfo#CATEGORY_VIDEO}.
      */
-    public abstract void setApplicationCategoryHint(String packageName,
+    public abstract void setApplicationCategoryHint(@NonNull String packageName,
             @ApplicationInfo.Category int categoryHint);
 
     /** {@hide} */
@@ -6256,18 +6271,18 @@
 
     /**
      * Checks whether the calling package is allowed to request package installs through package
-     * installer. Apps are encouraged to call this api before launching the package installer via
+     * installer. Apps are encouraged to call this API before launching the package installer via
      * intent {@link android.content.Intent#ACTION_INSTALL_PACKAGE}. Starting from Android O, the
      * user can explicitly choose what external sources they trust to install apps on the device.
-     * If this api returns false, the install request will be blocked by the package installer and
+     * If this API returns false, the install request will be blocked by the package installer and
      * a dialog will be shown to the user with an option to launch settings to change their
      * preference. An application must target Android O or higher and declare permission
-     * {@link android.Manifest.permission#REQUEST_INSTALL_PACKAGES} in order to use this api.
+     * {@link android.Manifest.permission#REQUEST_INSTALL_PACKAGES} in order to use this API.
      *
      * @return true if the calling package is trusted by the user to request install packages on
      * the device, false otherwise.
-     * @see {@link android.content.Intent#ACTION_INSTALL_PACKAGE}
-     * @see {@link android.provider.Settings#ACTION_MANAGE_UNKNOWN_APP_SOURCES}
+     * @see android.content.Intent#ACTION_INSTALL_PACKAGE
+     * @see android.provider.Settings#ACTION_MANAGE_UNKNOWN_APP_SOURCES
      */
     public abstract boolean canRequestPackageInstalls();
 
@@ -6290,4 +6305,12 @@
      */
     @SystemApi
     public abstract ComponentName getInstantAppInstallerComponent();
+
+    /**
+     * Return the Android Id for a given Instant App.
+     *
+     * @see {@link android.provider.Settings.Secure#ANDROID_ID}
+     * @hide
+     */
+    public abstract String getInstantAppAndroidId(String packageName, @NonNull UserHandle user);
 }
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index d79deb2..0ad4874 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -16,11 +16,14 @@
 
 package android.content.pm;
 
+import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Collections;
 import java.util.List;
 
@@ -31,28 +34,37 @@
  * static - updatable non backwards-compatible emulating static linking.
  */
 public final class SharedLibraryInfo implements Parcelable {
+
+    /** @hide */
+    @IntDef(
+        flag = true,
+        value = {
+                TYPE_BUILTIN,
+                TYPE_DYNAMIC,
+                TYPE_STATIC,
+        })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface Type{}
+
     /**
      * Shared library type: this library is a part of the OS
      * and cannot be updated or uninstalled.
-     * @hide
      */
-    public static final int TYPE_BUILTIN = 0x1<<0;
+    public static final int TYPE_BUILTIN = 0;
 
     /**
      * Shared library type: this library is backwards-compatible, can
      * be updated, and updates can be uninstalled. Clients link against
      * the latest version of the library.
-     * @hide
      */
-    public static final int TYPE_DYNAMIC = 0x1<<1;
+    public static final int TYPE_DYNAMIC = 1;
 
     /**
      * Shared library type: this library is <strong>not</strong> backwards
      * -compatible, can be updated and updates can be uninstalled. Clients
      * link against a specific version of the library.
-     * @hide
      */
-    public static final int TYPE_STATIC = 0x1<<2;
+    public static final int TYPE_STATIC = 2;
 
     /**
      * Constant for referring to an undefined version.
@@ -60,8 +72,10 @@
     public static final int VERSION_UNDEFINED = -1;
 
     private final String mName;
+
+    // TODO: Make long when we change the paltform to use longs
     private final int mVersion;
-    private final int mType;
+    private final @Type int mType;
     private final VersionedPackage mDeclaringPackage;
     private final List<VersionedPackage> mDependentPackages;
 
@@ -90,13 +104,18 @@
                 parcel.readParcelable(null), parcel.readArrayList(null));
     }
 
-    /** @hide */
-    public int getType() {
+    /**
+     * Gets the type of this library.
+     *
+     * @return The library type.
+     */
+    public @Type int getType() {
         return mType;
     }
 
     /**
-     * Gets the library name.
+     * Gets the library name an app defines in its manifest
+     * to depend on the library.
      *
      * @return The name.
      */
@@ -105,40 +124,36 @@
     }
 
     /**
-     * Gets the version of the library. For {@link #isStatic()}  static} libraries
-     * this is the declared version and for {@link #isDynamic()} dynamic} and
-     * {@link #isBuiltin()} builtin} it is {@link #VERSION_UNDEFINED} as these
+     * Gets the version of the library. For {@link #TYPE_STATIC static} libraries
+     * this is the declared version and for {@link #TYPE_DYNAMIC dynamic} and
+     * {@link #TYPE_BUILTIN builtin} it is {@link #VERSION_UNDEFINED} as these
      * are not versioned.
      *
      * @return The version.
      */
-    public @IntRange(from = -1) int getVersion() {
+    public @IntRange(from = -1) long getVersion() {
         return mVersion;
     }
 
     /**
-     * @return whether this library is builtin which means that it
-     * is a part of the OS and cannot be updated or uninstalled.
+     * @hide
+     * @removed
      */
     public boolean isBuiltin() {
         return mType == TYPE_BUILTIN;
     }
 
     /**
-     * @return whether this library is dynamic which means that it
-     * is backwards-compatible, can be updated, and updates can be
-     * uninstalled. Clients link against the latest version of the
-     * library.
+     * @hide
+     * @removed
      */
     public boolean isDynamic() {
         return mType == TYPE_DYNAMIC;
     }
 
     /**
-     * @return whether this library is dynamic which means that it
-     * is <strong>not</strong> backwards-compatible, can be updated
-     * and updates can be uninstalled. Clients link against a specific
-     * version of the library.
+     * @hide
+     * @removed
      */
     public boolean isStatic() {
         return mType == TYPE_STATIC;
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index f48afb5..88bb1a4 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -56,12 +56,13 @@
         // Reset the assets, which may have changed due to configuration changes
         // or further resource loading.
         attrs.mAssets = res.getAssets();
+        attrs.mMetrics = res.getDisplayMetrics();
         attrs.resize(len);
         return attrs;
     }
 
     private final Resources mResources;
-    private final DisplayMetrics mMetrics;
+    private DisplayMetrics mMetrics;
     private AssetManager mAssets;
 
     private boolean mRecycled;
diff --git a/core/java/android/metrics/LogMaker.java b/core/java/android/metrics/LogMaker.java
index 0448221..3c6baa7 100644
--- a/core/java/android/metrics/LogMaker.java
+++ b/core/java/android/metrics/LogMaker.java
@@ -54,7 +54,11 @@
 
     /* Deserialize from the eventlog */
     public LogMaker(Object[] items) {
-      deserialize(items);
+        if (items != null) {
+            deserialize(items);
+        } else {
+            setCategory(MetricsEvent.VIEW_UNKNOWN);
+        }
     }
 
     /** @param category to replace the existing setting. */
@@ -94,6 +98,16 @@
     }
 
     /**
+     * Set event latency.
+     *
+     * @hide // TODO Expose in the future?  Too late for O.
+     */
+    public LogMaker setLatency(long milliseconds) {
+        entries.put(MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS, milliseconds);
+        return this;
+    }
+
+    /**
      * This will be set by the system when the log is persisted.
      * Client-supplied values will be ignored.
      *
@@ -363,13 +377,13 @@
      */
     public void deserialize(Object[] items) {
         int i = 0;
-        while (i < items.length) {
+        while (items != null && i < items.length) {
             Object key = items[i++];
             Object value = i < items.length ? items[i++] : null;
             if (key instanceof Integer) {
                 entries.put((Integer) key, value);
             } else {
-                Log.i(TAG, "Invalid key " + key.toString());
+                Log.i(TAG, "Invalid key " + (key == null ? "null" : key.toString()));
             }
         }
     }
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index f8702e2..375b7ee 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -245,6 +245,7 @@
      *
      * @param socket a stream socket
      * @param transform an {@link IpSecTransform}, which must be an active Transport Mode transform.
+     * @hide
      */
     public void applyTransportModeTransform(Socket socket, IpSecTransform transform)
             throws IOException {
@@ -262,6 +263,7 @@
      *
      * @param socket a datagram socket
      * @param transform an {@link IpSecTransform}, which must be an active Transport Mode transform.
+     * @hide
      */
     public void applyTransportModeTransform(DatagramSocket socket, IpSecTransform transform)
             throws IOException {
@@ -284,7 +286,7 @@
      * address associated with that transform will throw an IOException. In addition, if the
      * IpSecTransform is later deactivated, the socket will throw an IOException on any calls to
      * send() or receive() until the transform is removed from the socket by calling {@link
-     * #removeTransportModeTransform(Socket, IpSecTransform)};
+     * #removeTransportModeTransform(FileDescriptor, IpSecTransform)};
      *
      * @param socket a socket file descriptor
      * @param transform an {@link IpSecTransform}, which must be an active Transport Mode transform.
@@ -316,8 +318,10 @@
      *
      * @param socket a socket that previously had a transform applied to it.
      * @param transform the IPsec Transform that was previously applied to the given socket
+     * @hide
      */
-    public void removeTransportModeTransform(Socket socket, IpSecTransform transform) {
+    public void removeTransportModeTransform(Socket socket, IpSecTransform transform)
+            throws IOException {
         removeTransportModeTransform(ParcelFileDescriptor.fromSocket(socket), transform);
     }
 
@@ -330,8 +334,10 @@
      *
      * @param socket a socket that previously had a transform applied to it.
      * @param transform the IPsec Transform that was previously applied to the given socket
+     * @hide
      */
-    public void removeTransportModeTransform(DatagramSocket socket, IpSecTransform transform) {
+    public void removeTransportModeTransform(DatagramSocket socket, IpSecTransform transform)
+            throws IOException {
         removeTransportModeTransform(ParcelFileDescriptor.fromDatagramSocket(socket), transform);
     }
 
@@ -345,7 +351,8 @@
      * @param socket a socket file descriptor that previously had a transform applied to it.
      * @param transform the IPsec Transform that was previously applied to the given socket
      */
-    public void removeTransportModeTransform(FileDescriptor socket, IpSecTransform transform) {
+    public void removeTransportModeTransform(FileDescriptor socket, IpSecTransform transform)
+            throws IOException {
         removeTransportModeTransform(new ParcelFileDescriptor(socket), transform);
     }
 
@@ -419,7 +426,7 @@
          *
          * @param fd a file descriptor previously returned as a UDP Encapsulation socket.
          */
-        public void close() {
+        public void close() throws IOException {
             // TODO: Go close the socket
             mCloseGuard.close();
         }
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index afca0b0..bf7207c 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -19,6 +19,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.BitUtils;
 
 import java.util.Objects;
@@ -230,7 +231,8 @@
      * Capabilities that suggest that a network is restricted.
      * {@see #maybeMarkCapabilitiesRestricted}.
      */
-    private static final long RESTRICTED_CAPABILITIES =
+    @VisibleForTesting
+    /* package */ static final long RESTRICTED_CAPABILITIES =
             (1 << NET_CAPABILITY_CBS) |
             (1 << NET_CAPABILITY_DUN) |
             (1 << NET_CAPABILITY_EIMS) |
@@ -241,6 +243,17 @@
             (1 << NET_CAPABILITY_XCAP);
 
     /**
+     * Capabilities that suggest that a network is unrestricted.
+     * {@see #maybeMarkCapabilitiesRestricted}.
+     */
+    @VisibleForTesting
+    /* package */ static final long UNRESTRICTED_CAPABILITIES =
+            (1 << NET_CAPABILITY_INTERNET) |
+            (1 << NET_CAPABILITY_MMS) |
+            (1 << NET_CAPABILITY_SUPL) |
+            (1 << NET_CAPABILITY_WIFI_P2P);
+
+    /**
      * Adds the given capability to this {@code NetworkCapability} instance.
      * Multiple capabilities may be applied sequentially.  Note that when searching
      * for a network to satisfy a request, all capabilities requested must be satisfied.
@@ -353,12 +366,16 @@
      * @hide
      */
     public void maybeMarkCapabilitiesRestricted() {
-        // If all the capabilities are typically provided by restricted networks, conclude that this
-        // network is restricted.
-        if ((mNetworkCapabilities & ~(DEFAULT_CAPABILITIES | RESTRICTED_CAPABILITIES)) == 0 &&
-                // Must have at least some restricted capabilities, otherwise a request for an
-                // internet-less network will get marked restricted.
-                (mNetworkCapabilities & RESTRICTED_CAPABILITIES) != 0) {
+        // Verify there aren't any unrestricted capabilities.  If there are we say
+        // the whole thing is unrestricted.
+        final boolean hasUnrestrictedCapabilities =
+                ((mNetworkCapabilities & UNRESTRICTED_CAPABILITIES) != 0);
+
+        // Must have at least some restricted capabilities.
+        final boolean hasRestrictedCapabilities =
+                ((mNetworkCapabilities & RESTRICTED_CAPABILITIES) != 0);
+
+        if (hasRestrictedCapabilities && !hasUnrestrictedCapabilities) {
             removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
         }
     }
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 5f66abd..447f280 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -16,6 +16,8 @@
 
 package android.os;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
+
 import android.annotation.SystemApi;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -24,9 +26,12 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import libcore.io.Streams;
+
 import java.io.ByteArrayInputStream;
 import java.io.BufferedReader;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileReader;
 import java.io.FileWriter;
@@ -39,6 +44,7 @@
 import java.security.SignatureException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
+import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -46,6 +52,7 @@
 import java.util.Locale;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
+import java.util.zip.ZipInputStream;
 
 import com.android.internal.logging.MetricsLogger;
 
@@ -317,6 +324,70 @@
         } finally {
             raf.close();
         }
+
+        // Additionally verify the package compatibility.
+        if (!readAndVerifyPackageCompatibilityEntry(packageFile)) {
+            throw new SignatureException("package compatibility verification failed");
+        }
+    }
+
+    /**
+     * Verifies the compatibility entry from an {@link InputStream}.
+     *
+     * @return the verification result.
+     */
+    private static boolean verifyPackageCompatibility(InputStream inputStream) throws IOException {
+        ArrayList<String> list = new ArrayList<>();
+        ZipInputStream zis = new ZipInputStream(inputStream);
+        ZipEntry entry;
+        while ((entry = zis.getNextEntry()) != null) {
+            long entrySize = entry.getSize();
+            if (entrySize > Integer.MAX_VALUE || entrySize < 0) {
+                throw new IOException(
+                        "invalid entry size (" + entrySize + ") in the compatibility file");
+            }
+            byte[] bytes = new byte[(int) entrySize];
+            Streams.readFully(zis, bytes);
+            list.add(new String(bytes, UTF_8));
+        }
+        if (list.isEmpty()) {
+            throw new IOException("no entries found in the compatibility file");
+        }
+        return (VintfObject.verify(list.toArray(new String[list.size()])) == 0);
+    }
+
+    /**
+     * Reads and verifies the compatibility entry in an OTA zip package. The compatibility entry is
+     * a zip file (inside the OTA package zip).
+     *
+     * @return {@code true} if the entry doesn't exist or verification passes.
+     */
+    private static boolean readAndVerifyPackageCompatibilityEntry(File packageFile)
+            throws IOException {
+        try (ZipFile zip = new ZipFile(packageFile)) {
+            ZipEntry entry = zip.getEntry("compatibility.zip");
+            if (entry == null) {
+                return true;
+            }
+            InputStream inputStream = zip.getInputStream(entry);
+            return verifyPackageCompatibility(inputStream);
+        }
+    }
+
+    /**
+     * Verifies the package compatibility info against the current system.
+     *
+     * @param compatibilityFile the {@link File} that contains the package compatibility info.
+     * @throws IOException if there were any errors reading the compatibility file.
+     * @return the compatibility verification result.
+     *
+     * {@hide}
+     */
+    @SystemApi
+    public static boolean verifyPackageCompatibility(File compatibilityFile) throws IOException {
+        try (InputStream inputStream = new FileInputStream(compatibilityFile)) {
+            return verifyPackageCompatibility(inputStream);
+        }
     }
 
     /**
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 3942531..1c15004 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1518,7 +1518,8 @@
      * last to be deleted when additional disk space is needed.
      * <p>
      * This quota will change over time depending on how frequently the user
-     * interacts with your app, and depending on how much disk space is used.
+     * interacts with your app, and depending on how much system-wide disk space
+     * is used.
      * <p class="note">
      * Note: if your app uses the {@code android:sharedUserId} manifest feature,
      * then cached data for all packages in your shared UID is tracked together
diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java
index d3adce7..7496cb2 100644
--- a/core/java/android/preference/Preference.java
+++ b/core/java/android/preference/Preference.java
@@ -135,6 +135,7 @@
     private boolean mDependencyMet = true;
     private boolean mParentDependencyMet = true;
     private boolean mRecycleEnabled = true;
+    private boolean mHasSingleLineTitleAttr;
     private boolean mSingleLineTitle = true;
     private boolean mIconSpaceReserved;
 
@@ -303,6 +304,7 @@
 
                 case com.android.internal.R.styleable.Preference_singleLineTitle:
                     mSingleLineTitle = a.getBoolean(attr, mSingleLineTitle);
+                    mHasSingleLineTitleAttr = true;
                     break;
 
                 case com.android.internal.R.styleable.Preference_iconSpaceReserved:
@@ -609,7 +611,9 @@
             if (!TextUtils.isEmpty(title)) {
                 titleView.setText(title);
                 titleView.setVisibility(View.VISIBLE);
-                titleView.setSingleLine(mSingleLineTitle);
+                if (mHasSingleLineTitleAttr) {
+                    titleView.setSingleLine(mSingleLineTitle);
+                }
             } else {
                 titleView.setVisibility(View.GONE);
             }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index c6ea958..22de27f 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5082,6 +5082,10 @@
          * (available on certain devices running Android 4.2 or higher), each user appears as a
          * completely separate device, so the {@code ANDROID_ID} value is unique to each
          * user.</p>
+         *
+         * <p class="note"><strong>Note:</strong> If the caller is an Instant App the id is scoped
+         * to the Instant App, it is generated when the Instant App is first installed and reset if
+         * the user clears the Instant App.
          */
         public static final String ANDROID_ID = "android_id";
 
@@ -7077,6 +7081,7 @@
             CLONE_TO_MANAGED_PROFILE.add(ACCESSIBILITY_ENABLED);
             CLONE_TO_MANAGED_PROFILE.add(ALLOW_MOCK_LOCATION);
             CLONE_TO_MANAGED_PROFILE.add(ALLOWED_GEOLOCATION_ORIGINS);
+            CLONE_TO_MANAGED_PROFILE.add(AUTOFILL_SERVICE);
             CLONE_TO_MANAGED_PROFILE.add(DEFAULT_INPUT_METHOD);
             CLONE_TO_MANAGED_PROFILE.add(ENABLED_ACCESSIBILITY_SERVICES);
             CLONE_TO_MANAGED_PROFILE.add(ENABLED_INPUT_METHODS);
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 5e49b8f..813c54f 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -247,7 +247,7 @@
      * @param callback object used to notify the result of the request.
      */
     public void onSaveRequest(@NonNull SaveRequest request, @NonNull SaveCallback callback) {
-        List<FillContext> contexts = request.getFillContexts();
+        final List<FillContext> contexts = request.getFillContexts();
         onSaveRequest(contexts.get(contexts.size() - 1).getStructure(),
                 request.getClientState(), callback);
     }
@@ -285,4 +285,22 @@
         // TODO(b/33197203): Remove when GCore has migrated off this API
         getSystemService(AutofillManager.class).disableOwnedAutofillServices();
     }
+
+    /**
+     * Returns the {@link FillEventHistory.Event events} since the last {@link FillResponse} was
+     * returned.
+     *
+     * <p>The history is not persisted over reboots.
+     *
+     * @return The history or {@code null} if there are not events.
+     */
+    @Nullable public final FillEventHistory getFillEventHistory() {
+        AutofillManager afm = getSystemService(AutofillManager.class);
+
+        if (afm == null) {
+            return null;
+        } else {
+            return afm.getFillEventHistory();
+        }
+    }
 }
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index e77bd0d..e04fae7 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -24,6 +24,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillValue;
 import android.widget.RemoteViews;
 import com.android.internal.util.Preconditions;
@@ -50,6 +51,7 @@
     private final ArrayList<RemoteViews> mFieldPresentations;
     private final RemoteViews mPresentation;
     private final IntentSender mAuthentication;
+    @Nullable String mId;
 
     private Dataset(Builder builder) {
         mFieldIds = builder.mFieldIds;
@@ -57,6 +59,7 @@
         mFieldPresentations = builder.mFieldPresentations;
         mPresentation = builder.mPresentation;
         mAuthentication = builder.mAuthentication;
+        mId = builder.mId;
     }
 
     /** @hide */
@@ -89,7 +92,7 @@
     public String toString() {
         if (!DEBUG) return super.toString();
 
-        return new StringBuilder("Dataset [")
+        return new StringBuilder("Dataset " + mId + " [")
                 .append("fieldIds=").append(mFieldIds)
                 .append(", fieldValues=").append(mFieldValues)
                 .append(", fieldPresentations=")
@@ -100,6 +103,17 @@
     }
 
     /**
+     * Gets the id of this dataset.
+     *
+     * @return The id of this dataset or {@code null} if not set
+     *
+     * @hide
+     */
+    public String getId() {
+        return mId;
+    }
+
+    /**
      * A builder for {@link Dataset} objects. You must to provide at least
      * one value for a field or set an authentication intent.
      */
@@ -110,6 +124,7 @@
         private RemoteViews mPresentation;
         private IntentSender mAuthentication;
         private boolean mDestroyed;
+        @Nullable private String mId;
 
         /**
          * Creates a new builder.
@@ -173,6 +188,25 @@
         }
 
         /**
+         * Sets the id for the dataset.
+         *
+         * <p>The id of the last selected dataset can be read from
+         * {@link AutofillService#getFillEventHistory()}. If the id is not set it will not be clear
+         * if a dataset was selected as {@link AutofillService#getFillEventHistory()} uses
+         * {@code null} to indicate that no dataset was selected.
+         *
+         * @param id id for this dataset or {@code null} to unset.
+
+         * @return This builder.
+         */
+        public @NonNull Builder setId(@Nullable String id) {
+            throwIfDestroyed();
+
+            mId = id;
+            return this;
+        }
+
+        /**
          * Sets the value of a field.
          *
          * @param id id returned by {@link
@@ -269,6 +303,7 @@
         parcel.writeTypedArrayList(mFieldValues, flags);
         parcel.writeParcelableList(mFieldPresentations, flags);
         parcel.writeParcelable(mAuthentication, flags);
+        parcel.writeString(mId);
     }
 
     public static final Creator<Dataset> CREATOR = new Creator<Dataset>() {
@@ -295,6 +330,7 @@
                 builder.setValueAndPresentation(id, value, fieldPresentation);
             }
             builder.setAuthentication(parcel.readParcelable(null));
+            builder.setId(parcel.readString());
             return builder.build();
         }
 
diff --git a/core/java/android/service/autofill/FillCallback.java b/core/java/android/service/autofill/FillCallback.java
index a009be8..7774bdd 100644
--- a/core/java/android/service/autofill/FillCallback.java
+++ b/core/java/android/service/autofill/FillCallback.java
@@ -18,7 +18,6 @@
 
 import android.annotation.Nullable;
 import android.app.Activity;
-import android.os.Bundle;
 import android.os.RemoteException;
 
 /**
@@ -38,8 +37,8 @@
 
     /**
      * Notifies the Android System that an
-     * {@link AutofillService#onFillRequest(android.app.assist.AssistStructure, Bundle,
-     * int, android.os.CancellationSignal, FillCallback)} was successfully fulfilled by the service.
+     * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
+     * FillCallback)} was successfully fulfilled by the service.
      *
      * @param response autofill information for that activity, or {@code null} when the activity
      * cannot be autofilled (for example, if it only contains read-only fields). See
@@ -57,9 +56,8 @@
 
     /**
      * Notifies the Android System that an
-     * {@link AutofillService#onFillRequest(android.app.assist.AssistStructure,
-     * Bundle, int, android.os.CancellationSignal, FillCallback)}
-     * could not be fulfilled by the service.
+     * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
+     * FillCallback)} could not be fulfilled by the service.
      *
      * @param message error message to be displayed to the user.
      */
diff --git a/core/java/android/service/autofill/FillEventHistory.aidl b/core/java/android/service/autofill/FillEventHistory.aidl
new file mode 100644
index 0000000..3c48524
--- /dev/null
+++ b/core/java/android/service/autofill/FillEventHistory.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2017, 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.service.autofill;
+
+parcelable FillEventHistory;
diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java
new file mode 100644
index 0000000..3d72fcc
--- /dev/null
+++ b/core/java/android/service/autofill/FillEventHistory.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2017 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.service.autofill;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.content.IntentSender;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.autofill.AutofillId;
+import android.widget.RemoteViews;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Describes what happened after the latest call to {@link FillCallback#onSuccess(FillResponse)}.
+ */
+public final class FillEventHistory implements Parcelable {
+    /**
+     * Not in parcel. The UID of the {@link AutofillService} that created the {@link FillResponse}.
+     */
+    private final int mServiceUid;
+
+    @Nullable private final Bundle mClientState;
+    @Nullable List<Event> mEvents;
+
+    /**
+     * Gets the UID of the {@link AutofillService} that created the {@link FillResponse}.
+     *
+     * @return The UID of the {@link AutofillService}
+     *
+     * @hide
+     */
+    public int getServiceUid() {
+        return mServiceUid;
+    }
+
+    /**
+     * Returns the client state of the {@link FillResponse}.
+     *
+     * @return The client state set by the last {@link FillResponse}
+     */
+    @Nullable public Bundle getClientState() {
+        return mClientState;
+    }
+
+    /**
+     * Returns the events occurred after the latest call to
+     * {@link FillCallback#onSuccess(FillResponse)}.
+     *
+     * @return The list of events or {@code null} if non occurred.
+     */
+    @Nullable public List<Event> getEvents() {
+        return mEvents;
+    }
+
+    /**
+     * @hide
+     */
+    public void addEvent(Event event) {
+        if (mEvents == null) {
+            mEvents = new ArrayList<>(1);
+        }
+        mEvents.add(event);
+    }
+
+    /**
+     * @hide
+     */
+    public FillEventHistory(int serviceUid, @Nullable Bundle clientState) {
+        mClientState = clientState;
+        mServiceUid = serviceUid;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeBundle(mClientState);
+
+        if (mEvents == null) {
+            dest.writeInt(0);
+        } else {
+            dest.writeInt(mEvents.size());
+
+            int numEvents = mEvents.size();
+            for (int i = 0; i < numEvents; i++) {
+                Event event = mEvents.get(i);
+                dest.writeInt(event.getType());
+                dest.writeString(event.getDatasetId());
+            }
+        }
+    }
+
+    /**
+     * Description of an event that occured after the latest call to
+     * {@link FillCallback#onSuccess(FillResponse)}.
+     */
+    public static final class Event {
+        /**
+         * A dataset was selected. The dataset selected can be read from {@link #getDatasetId()}.
+         */
+        public static final int TYPE_DATASET_SELECTED = 0;
+
+        /**
+         * A {@link Dataset.Builder#setAuthentication(IntentSender) dataset authentication} was
+         * selected. The dataset authenticated can be read from {@link #getDatasetId()}.
+         */
+        public static final int TYPE_DATASET_AUTHENTICATION_SELECTED = 1;
+
+        /**
+         * A {@link FillResponse.Builder#setAuthentication(AutofillId[], IntentSender, RemoteViews)
+         * fill response authentication} was selected.
+         */
+        public static final int TYPE_AUTHENTICATION_SELECTED = 2;
+
+        /** A save UI was shown. */
+        public static final int TYPE_SAVE_SHOWN = 3;
+
+        /** @hide */
+        @IntDef(
+                value = {TYPE_DATASET_SELECTED,
+                        TYPE_DATASET_AUTHENTICATION_SELECTED,
+                        TYPE_AUTHENTICATION_SELECTED,
+                        TYPE_SAVE_SHOWN})
+        @Retention(RetentionPolicy.SOURCE)
+        @interface EventIds{}
+
+        @EventIds private final int mEventType;
+        @Nullable private final String mDatasetId;
+
+        /**
+         * Returns the type of the event.
+         *
+         * @return The type of the event
+         */
+        public int getType() {
+            return mEventType;
+        }
+
+        /**
+         * Returns the id of dataset the id was on.
+         *
+         * @return The id of dataset, or {@code null} the event is not associated with a dataset.
+         */
+        @Nullable public String getDatasetId() {
+            return mDatasetId;
+        }
+
+        /**
+         * Creates a new event.
+         *
+         * @param eventType The type of the event
+         * @param datasetId The dataset the event was on, or {@code null} if the event was on the
+         *                  whole response.
+         *
+         * @hide
+         */
+        public Event(int eventType, String datasetId) {
+            mEventType = Preconditions.checkArgumentInRange(eventType, 0, TYPE_SAVE_SHOWN,
+                    "eventType");
+            mDatasetId = datasetId;
+        }
+    }
+
+    public static final Parcelable.Creator<FillEventHistory> CREATOR =
+            new Parcelable.Creator<FillEventHistory>() {
+                @Override
+                public FillEventHistory createFromParcel(Parcel parcel) {
+                    FillEventHistory selection = new FillEventHistory(0, parcel.readBundle());
+
+                    int numEvents = parcel.readInt();
+                    for (int i = 0; i < numEvents; i++) {
+                        selection.addEvent(new Event(parcel.readInt(), parcel.readString()));
+                    }
+
+                    return selection;
+                }
+
+                @Override
+                public FillEventHistory[] newArray(int size) {
+                    return new FillEventHistory[size];
+                }
+            };
+}
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 8c8060a..0025365 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -32,8 +32,7 @@
 
 /**
  * Response for a {@link
- * AutofillService#onFillRequest(android.app.assist.AssistStructure,
- * Bundle, int, android.os.CancellationSignal, FillCallback)}.
+ * AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)}.
  *
  * <p>The response typically contains one or more {@link Dataset}s, each representing a set of
  * fields that can be autofilled together, and the Android system displays a dataset picker UI
@@ -258,6 +257,19 @@
         }
 
         /**
+         * Specifies views that should not trigger new
+         * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
+         * FillCallback)} requests.
+         *
+         * <p>This is typically used when the service cannot autofill the view; for example, an
+         * {@code EditText} representing a captcha.
+         */
+        public Builder setIgnoredIds(AutofillId...ids) {
+            // TODO: implement
+            return this;
+        }
+
+        /**
          * Adds a new {@link Dataset} to this response.
          *
          * @return This builder.
@@ -289,6 +301,9 @@
             return this;
         }
 
+        /**
+         * @deprecated Use {@link #setClientState(Bundle)} instead.
+         */
         @Deprecated
         public Builder setExtras(@Nullable Bundle extras) {
             throwIfDestroyed();
@@ -299,11 +314,9 @@
         /**
          * Sets a {@link Bundle state} that will be passed to subsequent APIs that
          * manipulate this response. For example, they are passed to subsequent
-         * calls to {@link AutofillService#onFillRequest(
-         * android.app.assist.AssistStructure, Bundle, int,
-         * android.os.CancellationSignal, FillCallback)} and {@link AutofillService#onSaveRequest(
-         * android.app.assist.AssistStructure, Bundle, SaveCallback)}. You can use
-         * this to store intermediate state that is persistent across multiple
+         * calls to {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
+         * FillCallback)} and {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)}.
+         * You can use this to store intermediate state that is persistent across multiple
          * fill requests and the subsequent save request.
          *
          * <p>If this method is called on multiple {@link FillResponse} objects for the same
diff --git a/core/java/android/service/autofill/SaveCallback.java b/core/java/android/service/autofill/SaveCallback.java
index 2c4ba6c..3a70138 100644
--- a/core/java/android/service/autofill/SaveCallback.java
+++ b/core/java/android/service/autofill/SaveCallback.java
@@ -17,7 +17,6 @@
 package android.service.autofill;
 
 import android.app.Activity;
-import android.os.Bundle;
 import android.os.RemoteException;
 
 /**
@@ -35,8 +34,8 @@
 
     /**
      * Notifies the Android System that an
-     * {@link AutofillService#onSaveRequest (android.app.assist.AssistStructure, Bundle,
-     * SaveCallback)} was successfully fulfilled by the service.
+     * {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)} was successfully fulfilled
+     * by the service.
      *
      * @throws RuntimeException if an error occurred while calling the Android System.
      */
@@ -52,8 +51,8 @@
 
     /**
      * Notifies the Android System that an
-     * {@link AutofillService#onSaveRequest(android.app.assist.AssistStructure, Bundle,
-     * SaveCallback)} could not be fulfilled by the service.
+     * {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)} could not be fulfilled
+     * by the service.
      *
      * @param message error message to be displayed to the user.
      *
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index 7f960df..f796444 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -40,7 +40,7 @@
 /**
  * Information used to indicate that an {@link AutofillService} is interested on saving the
  * user-inputed data for future use, through a
- * {@link AutofillService#onSaveRequest(android.app.assist.AssistStructure, Bundle, SaveCallback)}
+ * {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)}
  * call.
  *
  * <p>A {@link SaveInfo} is always associated with a {@link FillResponse}, and it contains at least
@@ -94,7 +94,7 @@
  * </pre>
  *
  * The
- * {@link AutofillService#onSaveRequest(android.app.assist.AssistStructure, Bundle, SaveCallback)}
+ * {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)}
  * is triggered after a call to {@link AutofillManager#commit()}, but only when all conditions
  * below are met:
  *
@@ -153,13 +153,27 @@
     @Retention(RetentionPolicy.SOURCE)
     @interface SaveDataType{}
 
+    /**
+     * Usually {@link AutofillService#onSaveRequest(AssistStructure, Bundle, SaveCallback)}
+     * is called once the activity finishes. If this flag is set it is called once all saved views
+     * become invisible.
+     */
+    public static final int FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE = 0x1;
+
+    /** @hide */
+    @IntDef(
+            flag = true,
+            value = {FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE})
+    @Retention(RetentionPolicy.SOURCE)
+    @interface SaveInfoFlags{}
+
     private final @SaveDataType int mType;
     private final CharSequence mNegativeActionTitle;
     private final IntentSender mNegativeActionListener;
     private final AutofillId[] mRequiredIds;
     private final AutofillId[] mOptionalIds;
     private final CharSequence mDescription;
-    private final boolean mSaveOnAllViewsInvisible;
+    private final int mFlags;
 
     private SaveInfo(Builder builder) {
         mType = builder.mType;
@@ -168,7 +182,7 @@
         mRequiredIds = builder.mRequiredIds;
         mOptionalIds = builder.mOptionalIds;
         mDescription = builder.mDescription;
-        mSaveOnAllViewsInvisible = builder.mSaveOnAllViewsInvisible;
+        mFlags = builder.mFlags;
     }
 
     /** @hide */
@@ -197,8 +211,8 @@
     }
 
     /** @hide */
-    public boolean saveOnAllViewsInvisible() {
-        return mSaveOnAllViewsInvisible;
+    public @SaveInfoFlags int getFlags() {
+        return mFlags;
     }
 
     /** @hide */
@@ -219,7 +233,7 @@
         private AutofillId[] mOptionalIds;
         private CharSequence mDescription;
         private boolean mDestroyed;
-        private boolean mSaveOnAllViewsInvisible;
+        private int mFlags;
 
         /**
          * Creates a new builder.
@@ -268,17 +282,15 @@
         }
 
         /**
-         * Usually {@link AutofillService#onSaveRequest(AssistStructure, Bundle, SaveCallback)}
-         * is called once the activity finishes. If this property is set it is called once all
-         * autofillable or saved views become invisible.
+         * Set flags changing the save behavior.
          *
-         * @param saveOnAllViewsInvisible Set to {@code true} if the data should be saved once
-         *                                all the views become invisible.
+         * @param flags {@link #FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE} or 0.
          * @return This builder.
          */
-        public @NonNull Builder setSaveOnAllViewsInvisible(boolean saveOnAllViewsInvisible) {
+        public @NonNull Builder setFlags(@SaveInfoFlags int flags) {
             throwIfDestroyed();
-            mSaveOnAllViewsInvisible = saveOnAllViewsInvisible;
+
+            mFlags = Preconditions.checkFlagsArgument(flags, FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE);
             return this;
         }
 
@@ -378,7 +390,7 @@
                 .append(", requiredIds=").append(Arrays.toString(mRequiredIds))
                 .append(", optionalIds=").append(Arrays.toString(mOptionalIds))
                 .append(", description=").append(mDescription)
-                .append(", saveOnNoVisibleTrackedViews=").append(mSaveOnAllViewsInvisible)
+                .append(", mFlags=").append(mFlags)
                 .append("]").toString();
     }
 
@@ -399,7 +411,7 @@
         parcel.writeParcelable(mNegativeActionListener, flags);
         parcel.writeParcelableArray(mOptionalIds, flags);
         parcel.writeCharSequence(mDescription);
-        parcel.writeBoolean(mSaveOnAllViewsInvisible);
+        parcel.writeInt(mFlags);
     }
 
     public static final Parcelable.Creator<SaveInfo> CREATOR = new Parcelable.Creator<SaveInfo>() {
@@ -413,7 +425,7 @@
             builder.setNegativeAction(parcel.readCharSequence(), parcel.readParcelable(null));
             builder.setOptionalIds(parcel.readParcelableArray(null, AutofillId.class));
             builder.setDescription(parcel.readCharSequence());
-            builder.setSaveOnAllViewsInvisible(parcel.readBoolean());
+            builder.setFlags(parcel.readInt());
             return builder.build();
         }
 
diff --git a/core/java/android/util/IconDrawableFactory.java b/core/java/android/util/IconDrawableFactory.java
new file mode 100644
index 0000000..b07942f
--- /dev/null
+++ b/core/java/android/util/IconDrawableFactory.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2017 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.util;
+
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Utility class to load app drawables with appropriate badging.
+ *
+ * @hide
+ */
+public class IconDrawableFactory {
+
+    protected final Context mContext;
+    protected final PackageManager mPm;
+    protected final UserManager mUm;
+    protected final LauncherIcons mLauncherIcons;
+    protected final boolean mEmbedShadow;
+
+    private IconDrawableFactory(Context context, boolean embedShadow) {
+        mContext = context;
+        mPm = context.getPackageManager();
+        mUm = context.getSystemService(UserManager.class);
+        mLauncherIcons = new LauncherIcons(context);
+        mEmbedShadow = embedShadow;
+    }
+
+    protected boolean needsBadging(ApplicationInfo appInfo, @UserIdInt int userId) {
+        return appInfo.isInstantApp() || mUm.isManagedProfile(userId);
+    }
+
+    public Drawable getBadgedIcon(ApplicationInfo appInfo) {
+        return getBadgedIcon(appInfo, UserHandle.getUserId(appInfo.uid));
+    }
+
+    public Drawable getBadgedIcon(ApplicationInfo appInfo, @UserIdInt int userId) {
+        return getBadgedIcon(appInfo, appInfo, userId);
+    }
+
+    public Drawable getBadgedIcon(PackageItemInfo itemInfo, ApplicationInfo appInfo,
+            @UserIdInt int userId) {
+        Drawable icon = mPm.loadUnbadgedItemIcon(itemInfo, appInfo);
+        if (!mEmbedShadow && !needsBadging(appInfo, userId)) {
+            return icon;
+        }
+
+        // Before badging, add shadow to adaptive icon if needed.
+        icon = mLauncherIcons.wrapIconDrawableWithShadow(icon);
+        if (appInfo.isInstantApp()) {
+            int badgeColor = Resources.getSystem().getColor(
+                    com.android.internal.R.color.instant_app_badge, null);
+            icon = mLauncherIcons.getBadgedDrawable(icon,
+                    com.android.internal.R.drawable.ic_instant_icon_badge_bolt,
+                    badgeColor);
+        }
+        if (mUm.isManagedProfile(userId)) {
+            icon = mLauncherIcons.getBadgedDrawable(icon,
+                    com.android.internal.R.drawable.ic_corp_icon_badge_case,
+                    getUserBadgeColor(mUm, userId));
+        }
+        return icon;
+    }
+
+    // Should have enough colors to cope with UserManagerService.getMaxManagedProfiles()
+    @VisibleForTesting
+    public static final int[] CORP_BADGE_COLORS = new int[] {
+            com.android.internal.R.color.profile_badge_1,
+            com.android.internal.R.color.profile_badge_2,
+            com.android.internal.R.color.profile_badge_3
+    };
+
+    public static int getUserBadgeColor(UserManager um, @UserIdInt int userId) {
+        int badge = um.getManagedProfileBadge(userId);
+        if (badge < 0) {
+            badge = 0;
+        }
+        int resourceId = CORP_BADGE_COLORS[badge % CORP_BADGE_COLORS.length];
+        return Resources.getSystem().getColor(resourceId, null);
+    }
+
+    public static IconDrawableFactory newInstance(Context context) {
+        return new IconDrawableFactory(context, true);
+    }
+
+    public static IconDrawableFactory newInstance(Context context, boolean embedShadow) {
+        return new IconDrawableFactory(context, embedShadow);
+    }
+}
diff --git a/core/java/android/util/LauncherIcons.java b/core/java/android/util/LauncherIcons.java
index 89b9646..402bef9 100644
--- a/core/java/android/util/LauncherIcons.java
+++ b/core/java/android/util/LauncherIcons.java
@@ -21,10 +21,11 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
-import android.graphics.PaintFlagsDrawFilter;
+import android.graphics.Rect;
 import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.DrawableWrapper;
+import android.graphics.drawable.LayerDrawable;
 
 /**
  * Utility class to handle icon treatments (e.g., shadow generation) for the Launcher icons.
@@ -32,78 +33,152 @@
  */
 public final class LauncherIcons {
 
-    private final Paint mPaint = new Paint();
-    private final Canvas mCanvas = new Canvas();
+    // Percent of actual icon size
+    private static final float ICON_SIZE_BLUR_FACTOR = 0.5f/48;
+    // Percent of actual icon size
+    private static final float ICON_SIZE_KEY_SHADOW_DELTA_FACTOR = 1f/48;
 
     private static final int KEY_SHADOW_ALPHA = 61;
     private static final int AMBIENT_SHADOW_ALPHA = 30;
-    private static final float BLUR_FACTOR = 0.5f / 48;
-    private int mShadowInset;
-    private Bitmap mShadowBitmap;
-    private int mIconSize;
-    private Resources mRes;
+
+    private final SparseArray<Bitmap> mShadowCache = new SparseArray<>();
+    private final int mIconSize;
+    private final Resources mRes;
 
     public LauncherIcons(Context context) {
         mRes = context.getResources();
-        DisplayMetrics metrics = mRes.getDisplayMetrics();
-        mShadowInset = (int)(2 * metrics.density);
-        mCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG,
-            Paint.FILTER_BITMAP_FLAG));
-        mIconSize = (int) mRes.getDimensionPixelSize(android.R.dimen.app_icon_size);
-    }
-
-    /**
-     * Draw the drawable into a bitmap.
-     */
-    public Bitmap createIconBitmap(Drawable icon) {
-        final Bitmap bitmap = Bitmap.createBitmap(mIconSize, mIconSize, Bitmap.Config.ARGB_8888);
-        mPaint.setAlpha(255);
-        mCanvas.setBitmap(bitmap);
-        int iconInset = 0;
-        if (mShadowBitmap != null) {
-            mCanvas.drawBitmap(mShadowBitmap, 0, 0, mPaint);
-            iconInset = mShadowInset;
-        }
-
-        icon.setBounds(iconInset, iconInset, mIconSize - iconInset,
-            mIconSize - iconInset);
-        icon.draw(mCanvas);
-        mCanvas.setBitmap(null);
-        return bitmap;
+        mIconSize = mRes.getDimensionPixelSize(android.R.dimen.app_icon_size);
     }
 
     public Drawable wrapIconDrawableWithShadow(Drawable drawable) {
         if (!(drawable instanceof AdaptiveIconDrawable)) {
             return drawable;
         }
-        AdaptiveIconDrawable d =
-            (AdaptiveIconDrawable) drawable.getConstantState().newDrawable().mutate();
-        getShadowBitmap(d);
-        Bitmap iconbitmap = createIconBitmap(d);
-        return new BitmapDrawable(mRes, iconbitmap);
+        Bitmap shadow = getShadowBitmap((AdaptiveIconDrawable) drawable);
+        return new ShadowDrawable(shadow, drawable);
     }
 
     private Bitmap getShadowBitmap(AdaptiveIconDrawable d) {
-        if (mShadowBitmap != null) {
-            return mShadowBitmap;
+        int shadowSize = Math.max(mIconSize, d.getIntrinsicHeight());
+        synchronized (mShadowCache) {
+            Bitmap shadow = mShadowCache.get(shadowSize);
+            if (shadow != null) {
+                return shadow;
+            }
         }
 
-        mShadowBitmap = Bitmap.createBitmap(mIconSize, mIconSize, Bitmap.Config.ALPHA_8);
-        mCanvas.setBitmap(mShadowBitmap);
+        d.setBounds(0, 0, shadowSize, shadowSize);
 
-        // Draw key shadow
-        mPaint.setColor(Color.TRANSPARENT);
-        float blur = BLUR_FACTOR * mIconSize;
-        mPaint.setShadowLayer(blur, 0, mShadowInset, KEY_SHADOW_ALPHA << 24);
-        d.setBounds(mShadowInset, mShadowInset, mIconSize - mShadowInset, mIconSize - mShadowInset);
-        mCanvas.drawPath(d.getIconMask(), mPaint);
+        float blur = ICON_SIZE_BLUR_FACTOR * shadowSize;
+        float keyShadowDistance = ICON_SIZE_KEY_SHADOW_DELTA_FACTOR * shadowSize;
+
+        int bitmapSize = (int) (shadowSize + 2 * blur + keyShadowDistance);
+        Bitmap shadow = Bitmap.createBitmap(bitmapSize, bitmapSize, Bitmap.Config.ARGB_8888);
+
+        Canvas canvas = new Canvas(shadow);
+        canvas.translate(blur + keyShadowDistance / 2, blur);
+
+        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        paint.setColor(Color.TRANSPARENT);
 
         // Draw ambient shadow
-        mPaint.setShadowLayer(blur, 0, 0, AMBIENT_SHADOW_ALPHA << 24);
-        d.setBounds(mShadowInset, 2 * mShadowInset, mIconSize - mShadowInset, mIconSize);
-        mCanvas.drawPath(d.getIconMask(), mPaint);
-        mPaint.clearShadowLayer();
+        paint.setShadowLayer(blur, 0, 0, AMBIENT_SHADOW_ALPHA << 24);
+        canvas.drawPath(d.getIconMask(), paint);
 
-        return mShadowBitmap;
+        // Draw key shadow
+        canvas.translate(0, keyShadowDistance);
+        paint.setShadowLayer(blur, 0, 0, KEY_SHADOW_ALPHA << 24);
+        canvas.drawPath(d.getIconMask(), paint);
+
+        canvas.setBitmap(null);
+        synchronized (mShadowCache) {
+            mShadowCache.put(shadowSize, shadow);
+        }
+        return shadow;
+    }
+
+    public Drawable getBadgeDrawable(int foregroundRes, int backgroundColor) {
+        return getBadgedDrawable(null, foregroundRes, backgroundColor);
+    }
+
+    public Drawable getBadgedDrawable(Drawable base, int foregroundRes, int backgroundColor) {
+        Resources sysRes = Resources.getSystem();
+
+        Drawable badgeShadow = sysRes.getDrawable(
+                com.android.internal.R.drawable.ic_corp_icon_badge_shadow);
+
+        Drawable badgeColor = sysRes.getDrawable(
+                com.android.internal.R.drawable.ic_corp_icon_badge_color)
+                .getConstantState().newDrawable().mutate();
+        badgeColor.setTint(backgroundColor);
+
+        Drawable badgeForeground = sysRes.getDrawable(foregroundRes);
+
+        Drawable[] drawables = base == null
+                ? new Drawable[] {badgeShadow, badgeColor, badgeForeground }
+                : new Drawable[] {base, badgeShadow, badgeColor, badgeForeground };
+        return new LayerDrawable(drawables);
+    }
+
+    /**
+     * A drawable which draws a shadow bitmap behind a drawable
+     */
+    private static class ShadowDrawable extends DrawableWrapper {
+
+        final MyConstantState mState;
+
+        public ShadowDrawable(Bitmap shadow, Drawable dr) {
+            super(dr);
+            mState = new MyConstantState(shadow, dr.getConstantState());
+        }
+
+        ShadowDrawable(MyConstantState state) {
+            super(state.mChildState.newDrawable());
+            mState = state;
+        }
+
+        @Override
+        public ConstantState getConstantState() {
+            return mState;
+        }
+
+        @Override
+        public void draw(Canvas canvas) {
+            Rect bounds = getBounds();
+            canvas.drawBitmap(mState.mShadow, null, bounds, mState.mPaint);
+            canvas.save();
+            // Ratio of child drawable size to shadow bitmap size
+            float factor = 1 / (1 + 2 * ICON_SIZE_BLUR_FACTOR + ICON_SIZE_KEY_SHADOW_DELTA_FACTOR);
+
+            canvas.translate(
+                    bounds.width() * factor *
+                            (ICON_SIZE_BLUR_FACTOR + ICON_SIZE_KEY_SHADOW_DELTA_FACTOR / 2),
+                    bounds.height() * factor * ICON_SIZE_BLUR_FACTOR);
+            canvas.scale(factor, factor);
+            super.draw(canvas);
+            canvas.restore();
+        }
+
+        private static class MyConstantState extends ConstantState {
+
+            final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
+            final Bitmap mShadow;
+            final ConstantState mChildState;
+
+            MyConstantState(Bitmap shadow, ConstantState childState) {
+                mShadow = shadow;
+                mChildState = childState;
+            }
+
+            @Override
+            public Drawable newDrawable() {
+                return new ShadowDrawable(this);
+            }
+
+            @Override
+            public int getChangingConfigurations() {
+                return mChildState.getChangingConfigurations();
+            }
+        }
     }
 }
diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java
index f47c355..1ccf16a 100644
--- a/core/java/android/view/FocusFinder.java
+++ b/core/java/android/view/FocusFinder.java
@@ -127,20 +127,23 @@
         if (focused == null || focused == root) {
             return root;
         }
-        ViewParent effective = focused.getParent();
+        ViewGroup effective = null;
+        ViewParent nextParent = focused.getParent();
         do {
-            if (effective == root) {
-                return root;
+            if (nextParent == root) {
+                return effective != null ? effective : root;
             }
-            ViewGroup vg = (ViewGroup) effective;
+            ViewGroup vg = (ViewGroup) nextParent;
             if (vg.getTouchscreenBlocksFocus()
                     && focused.getContext().getPackageManager().hasSystemFeature(
                             PackageManager.FEATURE_TOUCHSCREEN)
                     && vg.isKeyboardNavigationCluster()) {
-                return vg;
+                // Don't stop and return here because the cluster could be nested and we only
+                // care about the top-most one.
+                effective = vg;
             }
-            effective = effective.getParent();
-        } while (effective != null);
+            nextParent = nextParent.getParent();
+        } while (nextParent instanceof ViewGroup);
         return root;
     }
 
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 1a71679..9d40895 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -295,7 +295,15 @@
 
     @Override
     protected void onDetachedFromWindow() {
-        getViewRootImpl().removeWindowStoppedCallback(this);
+        ViewRootImpl viewRoot = getViewRootImpl();
+        // It's possible to create a SurfaceView using the default constructor and never
+        // attach it to a view hierarchy, this is a common use case when dealing with
+        // OpenGL. A developer will probably create a new GLSurfaceView, and let it manage
+        // the lifecycle. Instead of attaching it to a view, he/she can just pass
+        // the SurfaceHolder forward, most live wallpapers do it.
+        if (viewRoot != null) {
+            viewRoot.removeWindowStoppedCallback(this);
+        }
 
         mAttachedToWindow = false;
         if (mGlobalListenersAdded) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 7d2d77e..4f0aa32 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1135,27 +1135,39 @@
     @IntDef({
             IMPORTANT_FOR_AUTOFILL_AUTO,
             IMPORTANT_FOR_AUTOFILL_YES,
-            IMPORTANT_FOR_AUTOFILL_NO
+            IMPORTANT_FOR_AUTOFILL_NO,
+            IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS,
+            IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AutofillImportance {}
 
     /**
-     * Automatically determine whether a view is important for auto-fill.
+     * Automatically determine whether a view is important for autofill.
      */
     public static final int IMPORTANT_FOR_AUTOFILL_AUTO = 0x0;
 
     /**
-     * The view is important for important for auto-fill.
+     * The view is important for autofill, and its children (if any) will be traversed.
      */
     public static final int IMPORTANT_FOR_AUTOFILL_YES = 0x1;
 
     /**
-     * The view is not important for auto-fill.
+     * The view is not important for autofill, and its children (if any) will be traversed.
      */
     public static final int IMPORTANT_FOR_AUTOFILL_NO = 0x2;
 
     /**
+     * The view is important for autofill, but its children (if any) will not be traversed.
+     */
+    public static final int IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS = 0x4;
+
+    /**
+     * The view is not important for autofill, and its children (if any) will not be traversed.
+     */
+    public static final int IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS = 0x8;
+
+    /**
      * This view is enabled. Interpretation varies by subclass.
      * Use with ENABLED_MASK when calling setFlags.
      * {@hide}
@@ -7568,7 +7580,7 @@
      * should use {@link #setImportantForAutofill(int)} instead.
      *
      * <p><strong>Note:</strong> returning {@code false} does not guarantee the view will be
-     * excluded from the structure; for example, if the user explicitly requested auto-fill, the
+     * excluded from the structure; for example, if the user explicitly requested autofill, the
      * View might be always included.
      *
      * <p>This decision applies just for the view, not its children - if the view children are not
@@ -9720,7 +9732,7 @@
             @ViewDebug.IntToString(from = NOT_FOCUSABLE, to = "NOT_FOCUSABLE"),
             @ViewDebug.IntToString(from = FOCUSABLE, to = "FOCUSABLE"),
             @ViewDebug.IntToString(from = FOCUSABLE_AUTO, to = "FOCUSABLE_AUTO")
-            })
+            }, category = "focus")
     @Focusable
     public int getFocusable() {
         return (mViewFlags & FOCUSABLE_AUTO) > 0 ? FOCUSABLE_AUTO : mViewFlags & FOCUSABLE;
@@ -9734,7 +9746,7 @@
      * @return Whether the view is focusable in touch mode.
      * @attr ref android.R.styleable#View_focusableInTouchMode
      */
-    @ViewDebug.ExportedProperty
+    @ViewDebug.ExportedProperty(category = "focus")
     public final boolean isFocusableInTouchMode() {
         return FOCUSABLE_IN_TOUCH_MODE == (mViewFlags & FOCUSABLE_IN_TOUCH_MODE);
     }
@@ -9762,12 +9774,31 @@
      * @return True if this view is a root of a cluster, or false otherwise.
      * @attr ref android.R.styleable#View_keyboardNavigationCluster
      */
-    @ViewDebug.ExportedProperty(category = "keyboardNavigationCluster")
+    @ViewDebug.ExportedProperty(category = "focus")
     public final boolean isKeyboardNavigationCluster() {
         return (mPrivateFlags3 & PFLAG3_CLUSTER) != 0;
     }
 
     /**
+     * Searches up the view hierarchy to find the top-most cluster. All deeper/nested clusters
+     * will be ignored.
+     *
+     * @return the keyboard navigation cluster that this view is in (can be this view)
+     *         or {@code null} if not in one
+     */
+    View findKeyboardNavigationCluster() {
+        if (mParent instanceof View) {
+            View cluster = ((View) mParent).findKeyboardNavigationCluster();
+            if (cluster != null) {
+                return cluster;
+            } else if (isKeyboardNavigationCluster()) {
+                return this;
+            }
+        }
+        return null;
+    }
+
+    /**
      * Set whether this view is a root of a keyboard navigation cluster.
      *
      * @param isCluster If true, this view is a root of a cluster.
@@ -9789,9 +9820,20 @@
      *
      * @hide
      */
-    public void setFocusedInCluster() {
-        if (mParent instanceof ViewGroup) {
-            ((ViewGroup) mParent).setFocusInCluster(this);
+    public final void setFocusedInCluster() {
+        View top = findKeyboardNavigationCluster();
+        if (top == this) {
+            return;
+        }
+        ViewParent parent = mParent;
+        View child = this;
+        while (parent instanceof ViewGroup) {
+            ((ViewGroup) parent).setFocusedInCluster(child);
+            if (parent == top) {
+                return;
+            }
+            child = (View) parent;
+            parent = parent.getParent();
         }
     }
 
@@ -9807,7 +9849,7 @@
      * @return {@code true} if this view is the default-focus view, {@code false} otherwise
      * @attr ref android.R.styleable#View_focusedByDefault
      */
-    @ViewDebug.ExportedProperty(category = "focusedByDefault")
+    @ViewDebug.ExportedProperty(category = "focus")
     public final boolean isFocusedByDefault() {
         return (mPrivateFlags3 & PFLAG3_FOCUSED_BY_DEFAULT) != 0;
     }
@@ -9920,7 +9962,7 @@
      * @return True if this View should use a default focus highlight.
      * @attr ref android.R.styleable#View_defaultFocusHighlightEnabled
      */
-    @ViewDebug.ExportedProperty(category = "defaultFocusHighlightEnabled")
+    @ViewDebug.ExportedProperty(category = "focus")
     public final boolean getDefaultFocusHighlightEnabled() {
         return mDefaultFocusHighlightEnabled;
     }
@@ -25743,6 +25785,7 @@
         // focus
         stream.addProperty("focus:hasFocus", hasFocus());
         stream.addProperty("focus:isFocused", isFocused());
+        stream.addProperty("focus:focusable", getFocusable());
         stream.addProperty("focus:isFocusable", isFocusable());
         stream.addProperty("focus:isFocusableInTouchMode", isFocusableInTouchMode());
 
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index f9eb25d..1977ef5 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -807,33 +807,27 @@
         return mDefaultFocus != null || super.hasDefaultFocus();
     }
 
-    void setFocusInCluster(View child) {
-        // Stop at the root of the cluster
-        if (child.isKeyboardNavigationCluster()) {
-            return;
-        }
-
+    void setFocusedInCluster(View child) {
         mFocusedInCluster = child;
-
-        if (mParent instanceof ViewGroup) {
-            ((ViewGroup) mParent).setFocusInCluster(this);
-        }
     }
 
-    void clearFocusInCluster(View child) {
+    /**
+     * Removes {@code child} (and associated focusedInCluster chain) from the cluster containing
+     * it.
+     * <br>
+     * This is intended to be run on {@code child}'s immediate parent. This is necessary because
+     * the chain is sometimes cleared after {@code child} has been detached.
+     */
+    void clearFocusedInCluster(View child) {
         if (mFocusedInCluster != child) {
             return;
         }
-
-        if (child.isKeyboardNavigationCluster()) {
-            return;
-        }
-
-        mFocusedInCluster = null;
-
-        if (mParent instanceof ViewGroup) {
-            ((ViewGroup) mParent).clearFocusInCluster(this);
-        }
+        View top = findKeyboardNavigationCluster();
+        ViewParent parent = this;
+        do {
+            ((ViewGroup) parent).mFocusedInCluster = null;
+            parent = parent.getParent();
+        } while (parent != top && parent instanceof ViewGroup);
     }
 
     @Override
@@ -1281,7 +1275,7 @@
     public void setTouchscreenBlocksFocus(boolean touchscreenBlocksFocus) {
         if (touchscreenBlocksFocus) {
             mGroupFlags |= FLAG_TOUCHSCREEN_BLOCKS_FOCUS;
-            if (hasFocus()) {
+            if (hasFocus() && !isKeyboardNavigationCluster()) {
                 final View focusedChild = getDeepestFocusedChild();
                 if (!focusedChild.isFocusableInTouchMode()) {
                     final View newFocus = focusSearch(FOCUS_FORWARD);
@@ -1306,6 +1300,7 @@
     /**
      * Check whether this ViewGroup should ignore focus requests for itself and its children.
      */
+    @ViewDebug.ExportedProperty(category = "focus")
     public boolean getTouchscreenBlocksFocus() {
         return (mGroupFlags & FLAG_TOUCHSCREEN_BLOCKS_FOCUS) != 0;
     }
@@ -1316,7 +1311,8 @@
         // cluster, focus is free to move around within it.
         return getTouchscreenBlocksFocus() &&
                 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)
-                && (!hasFocus() || !isKeyboardNavigationCluster());
+                && !(isKeyboardNavigationCluster()
+                        && (hasFocus() || (findKeyboardNavigationCluster() != this)));
     }
 
     @Override
@@ -3217,8 +3213,7 @@
     }
 
     private boolean restoreFocusInClusterInternal(@FocusRealDirection int direction) {
-        if (mFocusedInCluster != null && !mFocusedInCluster.isKeyboardNavigationCluster()
-                && getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS
+        if (mFocusedInCluster != null && getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS
                 && (mFocusedInCluster.mViewFlags & VISIBILITY_MASK) == VISIBLE
                 && mFocusedInCluster.restoreFocusInCluster(direction)) {
             return true;
@@ -5182,7 +5177,7 @@
             clearChildFocus = true;
         }
         if (view == mFocusedInCluster) {
-            clearFocusInCluster(view);
+            clearFocusedInCluster(view);
         }
 
         view.clearAccessibilityFocus();
@@ -5302,7 +5297,7 @@
                 clearDefaultFocus = view;
             }
             if (view == mFocusedInCluster) {
-                clearFocusInCluster(view);
+                clearFocusedInCluster(view);
             }
 
             view.clearAccessibilityFocus();
@@ -5458,7 +5453,7 @@
             clearDefaultFocus(child);
         }
         if (child == mFocusedInCluster) {
-            clearFocusInCluster(child);
+            clearFocusedInCluster(child);
         }
 
         child.clearAccessibilityFocus();
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a7ececf..9ecced6 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1561,6 +1561,16 @@
         host.dispatchApplyWindowInsets(getWindowInsets(true /* forceConstruct */));
     }
 
+    /**
+     * @return the last content insets for use in adjusting the source hint rect for the
+     * picture-in-picture transition.
+     *
+     * @hide
+     */
+    public Rect getLastContentInsets() {
+        return mAttachInfo.mContentInsets;
+    }
+
     private static boolean shouldUseDisplaySize(final WindowManager.LayoutParams lp) {
         return lp.type == TYPE_STATUS_BAR_PANEL
                 || lp.type == TYPE_INPUT_METHOD
@@ -4665,7 +4675,8 @@
             if (focused == null && mView.restoreDefaultFocus()) {
                 return true;
             }
-            View cluster = focused.keyboardNavigationClusterSearch(null, direction);
+            View cluster = focused == null ? keyboardNavigationClusterSearch(null, direction)
+                    : focused.keyboardNavigationClusterSearch(null, direction);
 
             // Since requestFocus only takes "real" focus directions (and therefore also
             // restoreFocusInCluster), convert forward/backward focus into FOCUS_DOWN.
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index b157709..92b0d98 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -326,8 +326,8 @@
     /**
      * Sets whether the data on this node is sensitive; if it is, then its content (text, autofill
      * value, etc..) is striped before calls to {@link
-     * android.service.autofill.AutofillService#onFillRequest(android.app.assist.AssistStructure,
-     * Bundle, int, android.os.CancellationSignal, android.service.autofill.FillCallback)}.
+     * android.service.autofill.AutofillService#onFillRequest(android.service.autofill.FillRequest,
+     * android.os.CancellationSignal, android.service.autofill.FillCallback)}.
      *
      * <p>By default, all nodes are assumed to be sensitive, and only nodes that does not have PII
      * (Personally Identifiable Information - sensitive data such as email addresses, credit card
@@ -336,8 +336,8 @@
      *
      * <p>Notice that the content of even sensitive nodes are sent to the service (through the
      * {@link
-     * android.service.autofill.AutofillService#onSaveRequest(android.app.assist.AssistStructure,
-     * Bundle, android.service.autofill.SaveCallback)} call) when the user consented to save
+     * android.service.autofill.AutofillService#onSaveRequest(android.service.autofill.SaveRequest,
+     * android.service.autofill.SaveCallback)} call) when the user consented to save
      * thedata, so it is important to set the content of sensitive nodes as well, but mark them as
      * sensitive.
      *
diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java
index 6dbc09c..bf0e10f 100644
--- a/core/java/android/view/WindowManagerInternal.java
+++ b/core/java/android/view/WindowManagerInternal.java
@@ -303,13 +303,16 @@
      *                         hidden, no matter how WindowManagerService will react / has reacted
      *                         to corresponding API calls.  Note that this state is not guaranteed
      *                         to be synchronized with state in WindowManagerService.
+     * @param dismissImeOnBackKeyPressed {@code true} if the software keyboard is shown and the back
+     *                                   key is expected to dismiss the software keyboard.
      * @param targetWindowToken token to identify the target window that the IME is associated with.
      *                          {@code null} when application, system, or the IME itself decided to
      *                          change its window visibility before being associated with any target
      *                          window.
      */
     public abstract void updateInputMethodWindowStatus(@NonNull IBinder imeToken,
-            boolean imeWindowVisible, @Nullable IBinder targetWindowToken);
+            boolean imeWindowVisible, boolean dismissImeOnBackKeyPressed,
+            @Nullable IBinder targetWindowToken);
 
     /**
       * Returns true when the hardware keyboard is available.
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index bb6e0ee..030c78b 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -1537,6 +1537,18 @@
     public void setLastInputMethodWindowLw(WindowState ime, WindowState target);
 
     /**
+     * An internal callback (from InputMethodManagerService) to notify a state change regarding
+     * whether the back key should dismiss the software keyboard (IME) or not.
+     *
+     * @param newValue {@code true} if the software keyboard is shown and the back key is expected
+     *                 to dismiss the software keyboard.
+     * @hide
+     */
+    default void setDismissImeOnBackKeyPressed(boolean newValue) {
+        // Default implementation does nothing.
+    }
+
+    /**
      * Show the recents task list app.
      * @hide
      */
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index f9f400d..8ed0762 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -31,6 +31,8 @@
 import android.os.IBinder;
 import android.os.Parcelable;
 import android.os.RemoteException;
+import android.service.autofill.AutofillService;
+import android.service.autofill.FillEventHistory;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
@@ -105,6 +107,7 @@
      *
      * @deprecated Use {@link android.service.autofill.FillRequest#FLAG_MANUAL_REQUEST}
      */
+    // TODO(b/33197203): remove
     @Deprecated
     public static final int FLAG_MANUAL_REQUEST = 0x1;
 
@@ -334,6 +337,20 @@
     }
 
     /**
+     * Should always be called from {@link AutofillService#getFillEventHistory()}.
+     *
+     * @hide
+     */
+    @Nullable public FillEventHistory getFillEventHistory() {
+        try {
+            return mService.getFillEventHistory();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return null;
+        }
+    }
+
+    /**
      * Explicitly requests a new autofill context.
      *
      * <p>Normally, the autofill context is automatically started when autofillable views are
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index 68b3ccabc..df777c4 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -19,6 +19,7 @@
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.service.autofill.FillEventHistory;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillValue;
 import android.view.autofill.IAutoFillManagerClient;
@@ -33,6 +34,7 @@
     int startSession(IBinder activityToken, IBinder windowToken, in IBinder appCallback,
             in AutofillId autoFillId, in Rect bounds, in AutofillValue value, int userId,
             boolean hasCallback, int flags, String packageName);
+    FillEventHistory getFillEventHistory();
     boolean restoreSession(int sessionId, in IBinder activityToken, in IBinder appCallback);
     void setWindow(int sessionId, in IBinder windowToken);
     void updateSession(int sessionId, in AutofillId id, in Rect bounds,
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index f634a1b..022c157 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -53,7 +53,6 @@
 import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
-import java.util.StringJoiner;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -89,7 +88,7 @@
     @Override
     public TextSelection suggestSelection(
             @NonNull CharSequence text, int selectionStartIndex, int selectionEndIndex,
-            LocaleList defaultLocales) {
+            @Nullable LocaleList defaultLocales) {
         validateInput(text, selectionStartIndex, selectionEndIndex);
         try {
             if (text.length() > 0) {
@@ -128,7 +127,8 @@
 
     @Override
     public TextClassificationResult getTextClassificationResult(
-            @NonNull CharSequence text, int startIndex, int endIndex, LocaleList defaultLocales) {
+            @NonNull CharSequence text, int startIndex, int endIndex,
+            @Nullable LocaleList defaultLocales) {
         validateInput(text, startIndex, endIndex);
         try {
             if (text.length() > 0) {
@@ -156,7 +156,8 @@
     }
 
     @Override
-    public LinksInfo getLinks(CharSequence text, int linkMask, LocaleList defaultLocales) {
+    public LinksInfo getLinks(
+            @NonNull CharSequence text, int linkMask, @Nullable LocaleList defaultLocales) {
         Preconditions.checkArgument(text != null);
         try {
             return LinksInfoFactory.create(
@@ -199,12 +200,11 @@
     @GuardedBy("mSmartSelectionLock") // Do not call outside this lock.
     @Nullable
     private Locale findBestSupportedLocaleLocked(LocaleList localeList) {
-        final List<Locale.LanguageRange> languageRangeList = Locale.LanguageRange.parse(
-                new StringJoiner(",")
-                        // Specified localeList takes priority over the system default
-                        .add(localeList.toLanguageTags())
-                        .add(LocaleList.getDefault().toLanguageTags())
-                        .toString());
+        // Specified localeList takes priority over the system default, so it is listed first.
+        final String languages = localeList.isEmpty()
+                ? LocaleList.getDefault().toLanguageTags()
+                : localeList.toLanguageTags() + "," + LocaleList.getDefault().toLanguageTags();
+        final List<Locale.LanguageRange> languageRangeList = Locale.LanguageRange.parse(languages);
         return Locale.lookup(languageRangeList, loadModelFilePathsLocked().keySet());
     }
 
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 4fb7b19..0d3baa8 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -146,7 +146,7 @@
     private static final String UNDO_OWNER_TAG = "Editor";
 
     // Ordering constants used to place the Action Mode or context menu items in their menu.
-    private static final int MENU_ITEM_ORDER_ASSIST = 1;
+    private static final int MENU_ITEM_ORDER_ASSIST = 0;
     private static final int MENU_ITEM_ORDER_UNDO = 2;
     private static final int MENU_ITEM_ORDER_REDO = 3;
     private static final int MENU_ITEM_ORDER_CUT = 4;
@@ -156,8 +156,8 @@
     private static final int MENU_ITEM_ORDER_PASTE_AS_PLAIN_TEXT = 8;
     private static final int MENU_ITEM_ORDER_SELECT_ALL = 9;
     private static final int MENU_ITEM_ORDER_REPLACE = 10;
-    private static final int MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START = 11;
-    private static final int MENU_ITEM_ORDER_AUTOFILL = 12;
+    private static final int MENU_ITEM_ORDER_AUTOFILL = 11;
+    private static final int MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START = 100;
 
     // Each Editor manages its own undo stack.
     private final UndoManager mUndoManager = new UndoManager();
@@ -6322,9 +6322,10 @@
          * Adds "PROCESS_TEXT" menu items to the specified menu.
          */
         public void onInitializeMenu(Menu menu) {
-            int i = 0;
+            final int size = mSupportedActivities.size();
             loadSupportedActivities();
-            for (ResolveInfo resolveInfo : mSupportedActivities) {
+            for (int i = 0; i < size; i++) {
+                final ResolveInfo resolveInfo = mSupportedActivities.get(i);
                 menu.add(Menu.NONE, Menu.NONE,
                         Editor.MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START + i++,
                         getLabel(resolveInfo))
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 9245134..036b391 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -252,9 +252,7 @@
                 if (mEnterAnimationId != 0) {
                     opts = ActivityOptions.makeCustomAnimation(context, mEnterAnimationId, 0);
                 } else {
-                    opts = ActivityOptions.makeScaleUpAnimation(view,
-                            0, 0,
-                            view.getMeasuredWidth(), view.getMeasuredHeight());
+                    opts = ActivityOptions.makeBasic();
                 }
 
                 if (launchStackId != StackId.INVALID_STACK_ID) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index f9f10af..f1a3ff5 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -8196,9 +8196,11 @@
         mTempTextPaint.set(getPaint());
         mTempTextPaint.setTextSize(suggestedSizeInPx);
 
+        final int availableWidth = mHorizontallyScrolling
+                ? VERY_WIDE
+                : getMeasuredWidth() - getTotalPaddingLeft() - getTotalPaddingRight();
         final StaticLayout.Builder layoutBuilder = StaticLayout.Builder.obtain(
-                text, 0, text.length(),  mTempTextPaint,
-                getMeasuredWidth() - getTotalPaddingLeft() - getTotalPaddingRight());
+                text, 0, text.length(),  mTempTextPaint, availableWidth);
 
         layoutBuilder.setAlignment(getLayoutAlignment())
                 .setLineSpacing(getLineSpacingExtra(), getLineSpacingMultiplier())
diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
index 032c775..b9ed963 100644
--- a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
+++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
@@ -34,6 +34,7 @@
 import android.widget.TextView;
 
 import com.android.internal.R;
+import com.android.internal.widget.ResolverDrawerLayout;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -56,6 +57,11 @@
         super.onCreate(savedInstanceState);
         setContentView(R.layout.accessibility_button_chooser);
 
+        final ResolverDrawerLayout rdl = findViewById(R.id.contentPanel);
+        if (rdl != null) {
+            rdl.setOnDismissedListener(this::finish);
+        }
+
         String component = Settings.Secure.getString(getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT);
         if (TextUtils.isEmpty(component)) {
diff --git a/core/java/com/android/internal/graphics/ColorUtils.java b/core/java/com/android/internal/graphics/ColorUtils.java
new file mode 100644
index 0000000..6c1efa4
--- /dev/null
+++ b/core/java/com/android/internal/graphics/ColorUtils.java
@@ -0,0 +1,618 @@
+/*
+ * Copyright (C) 2017 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.internal.graphics;
+
+import android.annotation.ColorInt;
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.graphics.Color;
+
+/**
+ * Copied from: frameworks/support/core-utils/java/android/support/v4/graphics/ColorUtils.java
+ *
+ * A set of color-related utility methods, building upon those available in {@code Color}.
+ */
+public final class ColorUtils {
+
+    private static final double XYZ_WHITE_REFERENCE_X = 95.047;
+    private static final double XYZ_WHITE_REFERENCE_Y = 100;
+    private static final double XYZ_WHITE_REFERENCE_Z = 108.883;
+    private static final double XYZ_EPSILON = 0.008856;
+    private static final double XYZ_KAPPA = 903.3;
+
+    private static final int MIN_ALPHA_SEARCH_MAX_ITERATIONS = 10;
+    private static final int MIN_ALPHA_SEARCH_PRECISION = 1;
+
+    private static final ThreadLocal<double[]> TEMP_ARRAY = new ThreadLocal<>();
+
+    private ColorUtils() {}
+
+    /**
+     * Composite two potentially translucent colors over each other and returns the result.
+     */
+    public static int compositeColors(@ColorInt int foreground, @ColorInt int background) {
+        int bgAlpha = Color.alpha(background);
+        int fgAlpha = Color.alpha(foreground);
+        int a = compositeAlpha(fgAlpha, bgAlpha);
+
+        int r = compositeComponent(Color.red(foreground), fgAlpha,
+                Color.red(background), bgAlpha, a);
+        int g = compositeComponent(Color.green(foreground), fgAlpha,
+                Color.green(background), bgAlpha, a);
+        int b = compositeComponent(Color.blue(foreground), fgAlpha,
+                Color.blue(background), bgAlpha, a);
+
+        return Color.argb(a, r, g, b);
+    }
+
+    private static int compositeAlpha(int foregroundAlpha, int backgroundAlpha) {
+        return 0xFF - (((0xFF - backgroundAlpha) * (0xFF - foregroundAlpha)) / 0xFF);
+    }
+
+    private static int compositeComponent(int fgC, int fgA, int bgC, int bgA, int a) {
+        if (a == 0) return 0;
+        return ((0xFF * fgC * fgA) + (bgC * bgA * (0xFF - fgA))) / (a * 0xFF);
+    }
+
+    /**
+     * Returns the luminance of a color as a float between {@code 0.0} and {@code 1.0}.
+     * <p>Defined as the Y component in the XYZ representation of {@code color}.</p>
+     */
+    @FloatRange(from = 0.0, to = 1.0)
+    public static double calculateLuminance(@ColorInt int color) {
+        final double[] result = getTempDouble3Array();
+        colorToXYZ(color, result);
+        // Luminance is the Y component
+        return result[1] / 100;
+    }
+
+    /**
+     * Returns the contrast ratio between {@code foreground} and {@code background}.
+     * {@code background} must be opaque.
+     * <p>
+     * Formula defined
+     * <a href="http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef">here</a>.
+     */
+    public static double calculateContrast(@ColorInt int foreground, @ColorInt int background) {
+        if (Color.alpha(background) != 255) {
+            throw new IllegalArgumentException("background can not be translucent: #"
+                    + Integer.toHexString(background));
+        }
+        if (Color.alpha(foreground) < 255) {
+            // If the foreground is translucent, composite the foreground over the background
+            foreground = compositeColors(foreground, background);
+        }
+
+        final double luminance1 = calculateLuminance(foreground) + 0.05;
+        final double luminance2 = calculateLuminance(background) + 0.05;
+
+        // Now return the lighter luminance divided by the darker luminance
+        return Math.max(luminance1, luminance2) / Math.min(luminance1, luminance2);
+    }
+
+    /**
+     * Calculates the minimum alpha value which can be applied to {@code foreground} so that would
+     * have a contrast value of at least {@code minContrastRatio} when compared to
+     * {@code background}.
+     *
+     * @param foreground       the foreground color
+     * @param background       the opaque background color
+     * @param minContrastRatio the minimum contrast ratio
+     * @return the alpha value in the range 0-255, or -1 if no value could be calculated
+     */
+    public static int calculateMinimumAlpha(@ColorInt int foreground, @ColorInt int background,
+            float minContrastRatio) {
+        if (Color.alpha(background) != 255) {
+            throw new IllegalArgumentException("background can not be translucent: #"
+                    + Integer.toHexString(background));
+        }
+
+        // First lets check that a fully opaque foreground has sufficient contrast
+        int testForeground = setAlphaComponent(foreground, 255);
+        double testRatio = calculateContrast(testForeground, background);
+        if (testRatio < minContrastRatio) {
+            // Fully opaque foreground does not have sufficient contrast, return error
+            return -1;
+        }
+
+        // Binary search to find a value with the minimum value which provides sufficient contrast
+        int numIterations = 0;
+        int minAlpha = 0;
+        int maxAlpha = 255;
+
+        while (numIterations <= MIN_ALPHA_SEARCH_MAX_ITERATIONS &&
+                (maxAlpha - minAlpha) > MIN_ALPHA_SEARCH_PRECISION) {
+            final int testAlpha = (minAlpha + maxAlpha) / 2;
+
+            testForeground = setAlphaComponent(foreground, testAlpha);
+            testRatio = calculateContrast(testForeground, background);
+
+            if (testRatio < minContrastRatio) {
+                minAlpha = testAlpha;
+            } else {
+                maxAlpha = testAlpha;
+            }
+
+            numIterations++;
+        }
+
+        // Conservatively return the max of the range of possible alphas, which is known to pass.
+        return maxAlpha;
+    }
+
+    /**
+     * Convert RGB components to HSL (hue-saturation-lightness).
+     * <ul>
+     * <li>outHsl[0] is Hue [0 .. 360)</li>
+     * <li>outHsl[1] is Saturation [0...1]</li>
+     * <li>outHsl[2] is Lightness [0...1]</li>
+     * </ul>
+     *
+     * @param r      red component value [0..255]
+     * @param g      green component value [0..255]
+     * @param b      blue component value [0..255]
+     * @param outHsl 3-element array which holds the resulting HSL components
+     */
+    public static void RGBToHSL(@IntRange(from = 0x0, to = 0xFF) int r,
+            @IntRange(from = 0x0, to = 0xFF) int g, @IntRange(from = 0x0, to = 0xFF) int b,
+            @NonNull float[] outHsl) {
+        final float rf = r / 255f;
+        final float gf = g / 255f;
+        final float bf = b / 255f;
+
+        final float max = Math.max(rf, Math.max(gf, bf));
+        final float min = Math.min(rf, Math.min(gf, bf));
+        final float deltaMaxMin = max - min;
+
+        float h, s;
+        float l = (max + min) / 2f;
+
+        if (max == min) {
+            // Monochromatic
+            h = s = 0f;
+        } else {
+            if (max == rf) {
+                h = ((gf - bf) / deltaMaxMin) % 6f;
+            } else if (max == gf) {
+                h = ((bf - rf) / deltaMaxMin) + 2f;
+            } else {
+                h = ((rf - gf) / deltaMaxMin) + 4f;
+            }
+
+            s = deltaMaxMin / (1f - Math.abs(2f * l - 1f));
+        }
+
+        h = (h * 60f) % 360f;
+        if (h < 0) {
+            h += 360f;
+        }
+
+        outHsl[0] = constrain(h, 0f, 360f);
+        outHsl[1] = constrain(s, 0f, 1f);
+        outHsl[2] = constrain(l, 0f, 1f);
+    }
+
+    /**
+     * Convert the ARGB color to its HSL (hue-saturation-lightness) components.
+     * <ul>
+     * <li>outHsl[0] is Hue [0 .. 360)</li>
+     * <li>outHsl[1] is Saturation [0...1]</li>
+     * <li>outHsl[2] is Lightness [0...1]</li>
+     * </ul>
+     *
+     * @param color  the ARGB color to convert. The alpha component is ignored
+     * @param outHsl 3-element array which holds the resulting HSL components
+     */
+    public static void colorToHSL(@ColorInt int color, @NonNull float[] outHsl) {
+        RGBToHSL(Color.red(color), Color.green(color), Color.blue(color), outHsl);
+    }
+
+    /**
+     * Convert HSL (hue-saturation-lightness) components to a RGB color.
+     * <ul>
+     * <li>hsl[0] is Hue [0 .. 360)</li>
+     * <li>hsl[1] is Saturation [0...1]</li>
+     * <li>hsl[2] is Lightness [0...1]</li>
+     * </ul>
+     * If hsv values are out of range, they are pinned.
+     *
+     * @param hsl 3-element array which holds the input HSL components
+     * @return the resulting RGB color
+     */
+    @ColorInt
+    public static int HSLToColor(@NonNull float[] hsl) {
+        final float h = hsl[0];
+        final float s = hsl[1];
+        final float l = hsl[2];
+
+        final float c = (1f - Math.abs(2 * l - 1f)) * s;
+        final float m = l - 0.5f * c;
+        final float x = c * (1f - Math.abs((h / 60f % 2f) - 1f));
+
+        final int hueSegment = (int) h / 60;
+
+        int r = 0, g = 0, b = 0;
+
+        switch (hueSegment) {
+            case 0:
+                r = Math.round(255 * (c + m));
+                g = Math.round(255 * (x + m));
+                b = Math.round(255 * m);
+                break;
+            case 1:
+                r = Math.round(255 * (x + m));
+                g = Math.round(255 * (c + m));
+                b = Math.round(255 * m);
+                break;
+            case 2:
+                r = Math.round(255 * m);
+                g = Math.round(255 * (c + m));
+                b = Math.round(255 * (x + m));
+                break;
+            case 3:
+                r = Math.round(255 * m);
+                g = Math.round(255 * (x + m));
+                b = Math.round(255 * (c + m));
+                break;
+            case 4:
+                r = Math.round(255 * (x + m));
+                g = Math.round(255 * m);
+                b = Math.round(255 * (c + m));
+                break;
+            case 5:
+            case 6:
+                r = Math.round(255 * (c + m));
+                g = Math.round(255 * m);
+                b = Math.round(255 * (x + m));
+                break;
+        }
+
+        r = constrain(r, 0, 255);
+        g = constrain(g, 0, 255);
+        b = constrain(b, 0, 255);
+
+        return Color.rgb(r, g, b);
+    }
+
+    /**
+     * Set the alpha component of {@code color} to be {@code alpha}.
+     */
+    @ColorInt
+    public static int setAlphaComponent(@ColorInt int color,
+            @IntRange(from = 0x0, to = 0xFF) int alpha) {
+        if (alpha < 0 || alpha > 255) {
+            throw new IllegalArgumentException("alpha must be between 0 and 255.");
+        }
+        return (color & 0x00ffffff) | (alpha << 24);
+    }
+
+    /**
+     * Convert the ARGB color to its CIE Lab representative components.
+     *
+     * @param color  the ARGB color to convert. The alpha component is ignored
+     * @param outLab 3-element array which holds the resulting LAB components
+     */
+    public static void colorToLAB(@ColorInt int color, @NonNull double[] outLab) {
+        RGBToLAB(Color.red(color), Color.green(color), Color.blue(color), outLab);
+    }
+
+    /**
+     * Convert RGB components to its CIE Lab representative components.
+     *
+     * <ul>
+     * <li>outLab[0] is L [0 ...1)</li>
+     * <li>outLab[1] is a [-128...127)</li>
+     * <li>outLab[2] is b [-128...127)</li>
+     * </ul>
+     *
+     * @param r      red component value [0..255]
+     * @param g      green component value [0..255]
+     * @param b      blue component value [0..255]
+     * @param outLab 3-element array which holds the resulting LAB components
+     */
+    public static void RGBToLAB(@IntRange(from = 0x0, to = 0xFF) int r,
+            @IntRange(from = 0x0, to = 0xFF) int g, @IntRange(from = 0x0, to = 0xFF) int b,
+            @NonNull double[] outLab) {
+        // First we convert RGB to XYZ
+        RGBToXYZ(r, g, b, outLab);
+        // outLab now contains XYZ
+        XYZToLAB(outLab[0], outLab[1], outLab[2], outLab);
+        // outLab now contains LAB representation
+    }
+
+    /**
+     * Convert the ARGB color to its CIE XYZ representative components.
+     *
+     * <p>The resulting XYZ representation will use the D65 illuminant and the CIE
+     * 2° Standard Observer (1931).</p>
+     *
+     * <ul>
+     * <li>outXyz[0] is X [0 ...95.047)</li>
+     * <li>outXyz[1] is Y [0...100)</li>
+     * <li>outXyz[2] is Z [0...108.883)</li>
+     * </ul>
+     *
+     * @param color  the ARGB color to convert. The alpha component is ignored
+     * @param outXyz 3-element array which holds the resulting LAB components
+     */
+    public static void colorToXYZ(@ColorInt int color, @NonNull double[] outXyz) {
+        RGBToXYZ(Color.red(color), Color.green(color), Color.blue(color), outXyz);
+    }
+
+    /**
+     * Convert RGB components to its CIE XYZ representative components.
+     *
+     * <p>The resulting XYZ representation will use the D65 illuminant and the CIE
+     * 2° Standard Observer (1931).</p>
+     *
+     * <ul>
+     * <li>outXyz[0] is X [0 ...95.047)</li>
+     * <li>outXyz[1] is Y [0...100)</li>
+     * <li>outXyz[2] is Z [0...108.883)</li>
+     * </ul>
+     *
+     * @param r      red component value [0..255]
+     * @param g      green component value [0..255]
+     * @param b      blue component value [0..255]
+     * @param outXyz 3-element array which holds the resulting XYZ components
+     */
+    public static void RGBToXYZ(@IntRange(from = 0x0, to = 0xFF) int r,
+            @IntRange(from = 0x0, to = 0xFF) int g, @IntRange(from = 0x0, to = 0xFF) int b,
+            @NonNull double[] outXyz) {
+        if (outXyz.length != 3) {
+            throw new IllegalArgumentException("outXyz must have a length of 3.");
+        }
+
+        double sr = r / 255.0;
+        sr = sr < 0.04045 ? sr / 12.92 : Math.pow((sr + 0.055) / 1.055, 2.4);
+        double sg = g / 255.0;
+        sg = sg < 0.04045 ? sg / 12.92 : Math.pow((sg + 0.055) / 1.055, 2.4);
+        double sb = b / 255.0;
+        sb = sb < 0.04045 ? sb / 12.92 : Math.pow((sb + 0.055) / 1.055, 2.4);
+
+        outXyz[0] = 100 * (sr * 0.4124 + sg * 0.3576 + sb * 0.1805);
+        outXyz[1] = 100 * (sr * 0.2126 + sg * 0.7152 + sb * 0.0722);
+        outXyz[2] = 100 * (sr * 0.0193 + sg * 0.1192 + sb * 0.9505);
+    }
+
+    /**
+     * Converts a color from CIE XYZ to CIE Lab representation.
+     *
+     * <p>This method expects the XYZ representation to use the D65 illuminant and the CIE
+     * 2° Standard Observer (1931).</p>
+     *
+     * <ul>
+     * <li>outLab[0] is L [0 ...1)</li>
+     * <li>outLab[1] is a [-128...127)</li>
+     * <li>outLab[2] is b [-128...127)</li>
+     * </ul>
+     *
+     * @param x      X component value [0...95.047)
+     * @param y      Y component value [0...100)
+     * @param z      Z component value [0...108.883)
+     * @param outLab 3-element array which holds the resulting Lab components
+     */
+    public static void XYZToLAB(@FloatRange(from = 0f, to = XYZ_WHITE_REFERENCE_X) double x,
+            @FloatRange(from = 0f, to = XYZ_WHITE_REFERENCE_Y) double y,
+            @FloatRange(from = 0f, to = XYZ_WHITE_REFERENCE_Z) double z,
+            @NonNull double[] outLab) {
+        if (outLab.length != 3) {
+            throw new IllegalArgumentException("outLab must have a length of 3.");
+        }
+        x = pivotXyzComponent(x / XYZ_WHITE_REFERENCE_X);
+        y = pivotXyzComponent(y / XYZ_WHITE_REFERENCE_Y);
+        z = pivotXyzComponent(z / XYZ_WHITE_REFERENCE_Z);
+        outLab[0] = Math.max(0, 116 * y - 16);
+        outLab[1] = 500 * (x - y);
+        outLab[2] = 200 * (y - z);
+    }
+
+    /**
+     * Converts a color from CIE Lab to CIE XYZ representation.
+     *
+     * <p>The resulting XYZ representation will use the D65 illuminant and the CIE
+     * 2° Standard Observer (1931).</p>
+     *
+     * <ul>
+     * <li>outXyz[0] is X [0 ...95.047)</li>
+     * <li>outXyz[1] is Y [0...100)</li>
+     * <li>outXyz[2] is Z [0...108.883)</li>
+     * </ul>
+     *
+     * @param l      L component value [0...100)
+     * @param a      A component value [-128...127)
+     * @param b      B component value [-128...127)
+     * @param outXyz 3-element array which holds the resulting XYZ components
+     */
+    public static void LABToXYZ(@FloatRange(from = 0f, to = 100) final double l,
+            @FloatRange(from = -128, to = 127) final double a,
+            @FloatRange(from = -128, to = 127) final double b,
+            @NonNull double[] outXyz) {
+        final double fy = (l + 16) / 116;
+        final double fx = a / 500 + fy;
+        final double fz = fy - b / 200;
+
+        double tmp = Math.pow(fx, 3);
+        final double xr = tmp > XYZ_EPSILON ? tmp : (116 * fx - 16) / XYZ_KAPPA;
+        final double yr = l > XYZ_KAPPA * XYZ_EPSILON ? Math.pow(fy, 3) : l / XYZ_KAPPA;
+
+        tmp = Math.pow(fz, 3);
+        final double zr = tmp > XYZ_EPSILON ? tmp : (116 * fz - 16) / XYZ_KAPPA;
+
+        outXyz[0] = xr * XYZ_WHITE_REFERENCE_X;
+        outXyz[1] = yr * XYZ_WHITE_REFERENCE_Y;
+        outXyz[2] = zr * XYZ_WHITE_REFERENCE_Z;
+    }
+
+    /**
+     * Converts a color from CIE XYZ to its RGB representation.
+     *
+     * <p>This method expects the XYZ representation to use the D65 illuminant and the CIE
+     * 2° Standard Observer (1931).</p>
+     *
+     * @param x X component value [0...95.047)
+     * @param y Y component value [0...100)
+     * @param z Z component value [0...108.883)
+     * @return int containing the RGB representation
+     */
+    @ColorInt
+    public static int XYZToColor(@FloatRange(from = 0f, to = XYZ_WHITE_REFERENCE_X) double x,
+            @FloatRange(from = 0f, to = XYZ_WHITE_REFERENCE_Y) double y,
+            @FloatRange(from = 0f, to = XYZ_WHITE_REFERENCE_Z) double z) {
+        double r = (x * 3.2406 + y * -1.5372 + z * -0.4986) / 100;
+        double g = (x * -0.9689 + y * 1.8758 + z * 0.0415) / 100;
+        double b = (x * 0.0557 + y * -0.2040 + z * 1.0570) / 100;
+
+        r = r > 0.0031308 ? 1.055 * Math.pow(r, 1 / 2.4) - 0.055 : 12.92 * r;
+        g = g > 0.0031308 ? 1.055 * Math.pow(g, 1 / 2.4) - 0.055 : 12.92 * g;
+        b = b > 0.0031308 ? 1.055 * Math.pow(b, 1 / 2.4) - 0.055 : 12.92 * b;
+
+        return Color.rgb(
+                constrain((int) Math.round(r * 255), 0, 255),
+                constrain((int) Math.round(g * 255), 0, 255),
+                constrain((int) Math.round(b * 255), 0, 255));
+    }
+
+    /**
+     * Converts a color from CIE Lab to its RGB representation.
+     *
+     * @param l L component value [0...100]
+     * @param a A component value [-128...127]
+     * @param b B component value [-128...127]
+     * @return int containing the RGB representation
+     */
+    @ColorInt
+    public static int LABToColor(@FloatRange(from = 0f, to = 100) final double l,
+            @FloatRange(from = -128, to = 127) final double a,
+            @FloatRange(from = -128, to = 127) final double b) {
+        final double[] result = getTempDouble3Array();
+        LABToXYZ(l, a, b, result);
+        return XYZToColor(result[0], result[1], result[2]);
+    }
+
+    /**
+     * Returns the euclidean distance between two LAB colors.
+     */
+    public static double distanceEuclidean(@NonNull double[] labX, @NonNull double[] labY) {
+        return Math.sqrt(Math.pow(labX[0] - labY[0], 2)
+                + Math.pow(labX[1] - labY[1], 2)
+                + Math.pow(labX[2] - labY[2], 2));
+    }
+
+    private static float constrain(float amount, float low, float high) {
+        return amount < low ? low : (amount > high ? high : amount);
+    }
+
+    private static int constrain(int amount, int low, int high) {
+        return amount < low ? low : (amount > high ? high : amount);
+    }
+
+    private static double pivotXyzComponent(double component) {
+        return component > XYZ_EPSILON
+                ? Math.pow(component, 1 / 3.0)
+                : (XYZ_KAPPA * component + 16) / 116;
+    }
+
+    /**
+     * Blend between two ARGB colors using the given ratio.
+     *
+     * <p>A blend ratio of 0.0 will result in {@code color1}, 0.5 will give an even blend,
+     * 1.0 will result in {@code color2}.</p>
+     *
+     * @param color1 the first ARGB color
+     * @param color2 the second ARGB color
+     * @param ratio  the blend ratio of {@code color1} to {@code color2}
+     */
+    @ColorInt
+    public static int blendARGB(@ColorInt int color1, @ColorInt int color2,
+            @FloatRange(from = 0.0, to = 1.0) float ratio) {
+        final float inverseRatio = 1 - ratio;
+        float a = Color.alpha(color1) * inverseRatio + Color.alpha(color2) * ratio;
+        float r = Color.red(color1) * inverseRatio + Color.red(color2) * ratio;
+        float g = Color.green(color1) * inverseRatio + Color.green(color2) * ratio;
+        float b = Color.blue(color1) * inverseRatio + Color.blue(color2) * ratio;
+        return Color.argb((int) a, (int) r, (int) g, (int) b);
+    }
+
+    /**
+     * Blend between {@code hsl1} and {@code hsl2} using the given ratio. This will interpolate
+     * the hue using the shortest angle.
+     *
+     * <p>A blend ratio of 0.0 will result in {@code hsl1}, 0.5 will give an even blend,
+     * 1.0 will result in {@code hsl2}.</p>
+     *
+     * @param hsl1      3-element array which holds the first HSL color
+     * @param hsl2      3-element array which holds the second HSL color
+     * @param ratio     the blend ratio of {@code hsl1} to {@code hsl2}
+     * @param outResult 3-element array which holds the resulting HSL components
+     */
+    public static void blendHSL(@NonNull float[] hsl1, @NonNull float[] hsl2,
+            @FloatRange(from = 0.0, to = 1.0) float ratio, @NonNull float[] outResult) {
+        if (outResult.length != 3) {
+            throw new IllegalArgumentException("result must have a length of 3.");
+        }
+        final float inverseRatio = 1 - ratio;
+        // Since hue is circular we will need to interpolate carefully
+        outResult[0] = circularInterpolate(hsl1[0], hsl2[0], ratio);
+        outResult[1] = hsl1[1] * inverseRatio + hsl2[1] * ratio;
+        outResult[2] = hsl1[2] * inverseRatio + hsl2[2] * ratio;
+    }
+
+    /**
+     * Blend between two CIE-LAB colors using the given ratio.
+     *
+     * <p>A blend ratio of 0.0 will result in {@code lab1}, 0.5 will give an even blend,
+     * 1.0 will result in {@code lab2}.</p>
+     *
+     * @param lab1      3-element array which holds the first LAB color
+     * @param lab2      3-element array which holds the second LAB color
+     * @param ratio     the blend ratio of {@code lab1} to {@code lab2}
+     * @param outResult 3-element array which holds the resulting LAB components
+     */
+    public static void blendLAB(@NonNull double[] lab1, @NonNull double[] lab2,
+            @FloatRange(from = 0.0, to = 1.0) double ratio, @NonNull double[] outResult) {
+        if (outResult.length != 3) {
+            throw new IllegalArgumentException("outResult must have a length of 3.");
+        }
+        final double inverseRatio = 1 - ratio;
+        outResult[0] = lab1[0] * inverseRatio + lab2[0] * ratio;
+        outResult[1] = lab1[1] * inverseRatio + lab2[1] * ratio;
+        outResult[2] = lab1[2] * inverseRatio + lab2[2] * ratio;
+    }
+
+    static float circularInterpolate(float a, float b, float f) {
+        if (Math.abs(b - a) > 180) {
+            if (b > a) {
+                a += 360;
+            } else {
+                b += 360;
+            }
+        }
+        return (a + ((b - a) * f)) % 360;
+    }
+
+    private static double[] getTempDouble3Array() {
+        double[] result = TEMP_ARRAY.get();
+        if (result == null) {
+            result = new double[3];
+            TEMP_ARRAY.set(result);
+        }
+        return result;
+    }
+
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/graphics/palette/ColorCutQuantizer.java b/core/java/com/android/internal/graphics/palette/ColorCutQuantizer.java
new file mode 100644
index 0000000..2d0ad66
--- /dev/null
+++ b/core/java/com/android/internal/graphics/palette/ColorCutQuantizer.java
@@ -0,0 +1,535 @@
+/*
+ * Copyright (C) 2017 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.internal.graphics.palette;
+
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import android.graphics.Color;
+import android.util.TimingLogger;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+import java.util.PriorityQueue;
+
+import com.android.internal.graphics.ColorUtils;
+import com.android.internal.graphics.palette.Palette.Swatch;
+
+/**
+ * Copied from: frameworks/support/v7/palette/src/main/java/android/support/v7/
+ * graphics/ColorCutQuantizer.java
+ *
+ * An color quantizer based on the Median-cut algorithm, but optimized for picking out distinct
+ * colors rather than representation colors.
+ *
+ * The color space is represented as a 3-dimensional cube with each dimension being an RGB
+ * component. The cube is then repeatedly divided until we have reduced the color space to the
+ * requested number of colors. An average color is then generated from each cube.
+ *
+ * What makes this different to median-cut is that median-cut divided cubes so that all of the cubes
+ * have roughly the same population, where this quantizer divides boxes based on their color volume.
+ * This means that the color space is divided into distinct colors, rather than representative
+ * colors.
+ */
+final class ColorCutQuantizer {
+
+    private static final String LOG_TAG = "ColorCutQuantizer";
+    private static final boolean LOG_TIMINGS = false;
+
+    static final int COMPONENT_RED = -3;
+    static final int COMPONENT_GREEN = -2;
+    static final int COMPONENT_BLUE = -1;
+
+    private static final int QUANTIZE_WORD_WIDTH = 5;
+    private static final int QUANTIZE_WORD_MASK = (1 << QUANTIZE_WORD_WIDTH) - 1;
+
+    final int[] mColors;
+    final int[] mHistogram;
+    final List<Swatch> mQuantizedColors;
+    final TimingLogger mTimingLogger;
+    final Palette.Filter[] mFilters;
+
+    private final float[] mTempHsl = new float[3];
+
+    /**
+     * Constructor.
+     *
+     * @param pixels histogram representing an image's pixel data
+     * @param maxColors The maximum number of colors that should be in the result palette.
+     * @param filters Set of filters to use in the quantization stage
+     */
+    ColorCutQuantizer(final int[] pixels, final int maxColors, final Palette.Filter[] filters) {
+        mTimingLogger = LOG_TIMINGS ? new TimingLogger(LOG_TAG, "Creation") : null;
+        mFilters = filters;
+
+        final int[] hist = mHistogram = new int[1 << (QUANTIZE_WORD_WIDTH * 3)];
+        for (int i = 0; i < pixels.length; i++) {
+            final int quantizedColor = quantizeFromRgb888(pixels[i]);
+            // Now update the pixel value to the quantized value
+            pixels[i] = quantizedColor;
+            // And update the histogram
+            hist[quantizedColor]++;
+        }
+
+        if (LOG_TIMINGS) {
+            mTimingLogger.addSplit("Histogram created");
+        }
+
+        // Now let's count the number of distinct colors
+        int distinctColorCount = 0;
+        for (int color = 0; color < hist.length; color++) {
+            if (hist[color] > 0 && shouldIgnoreColor(color)) {
+                // If we should ignore the color, set the population to 0
+                hist[color] = 0;
+            }
+            if (hist[color] > 0) {
+                // If the color has population, increase the distinct color count
+                distinctColorCount++;
+            }
+        }
+
+        if (LOG_TIMINGS) {
+            mTimingLogger.addSplit("Filtered colors and distinct colors counted");
+        }
+
+        // Now lets go through create an array consisting of only distinct colors
+        final int[] colors = mColors = new int[distinctColorCount];
+        int distinctColorIndex = 0;
+        for (int color = 0; color < hist.length; color++) {
+            if (hist[color] > 0) {
+                colors[distinctColorIndex++] = color;
+            }
+        }
+
+        if (LOG_TIMINGS) {
+            mTimingLogger.addSplit("Distinct colors copied into array");
+        }
+
+        if (distinctColorCount <= maxColors) {
+            // The image has fewer colors than the maximum requested, so just return the colors
+            mQuantizedColors = new ArrayList<>();
+            for (int color : colors) {
+                mQuantizedColors.add(new Swatch(approximateToRgb888(color), hist[color]));
+            }
+
+            if (LOG_TIMINGS) {
+                mTimingLogger.addSplit("Too few colors present. Copied to Swatches");
+                mTimingLogger.dumpToLog();
+            }
+        } else {
+            // We need use quantization to reduce the number of colors
+            mQuantizedColors = quantizePixels(maxColors);
+
+            if (LOG_TIMINGS) {
+                mTimingLogger.addSplit("Quantized colors computed");
+                mTimingLogger.dumpToLog();
+            }
+        }
+    }
+
+    /**
+     * @return the list of quantized colors
+     */
+    List<Swatch> getQuantizedColors() {
+        return mQuantizedColors;
+    }
+
+    private List<Swatch> quantizePixels(int maxColors) {
+        // Create the priority queue which is sorted by volume descending. This means we always
+        // split the largest box in the queue
+        final PriorityQueue<Vbox> pq = new PriorityQueue<>(maxColors, VBOX_COMPARATOR_VOLUME);
+
+        // To start, offer a box which contains all of the colors
+        pq.offer(new Vbox(0, mColors.length - 1));
+
+        // Now go through the boxes, splitting them until we have reached maxColors or there are no
+        // more boxes to split
+        splitBoxes(pq, maxColors);
+
+        // Finally, return the average colors of the color boxes
+        return generateAverageColors(pq);
+    }
+
+    /**
+     * Iterate through the {@link java.util.Queue}, popping
+     * {@link ColorCutQuantizer.Vbox} objects from the queue
+     * and splitting them. Once split, the new box and the remaining box are offered back to the
+     * queue.
+     *
+     * @param queue {@link java.util.PriorityQueue} to poll for boxes
+     * @param maxSize Maximum amount of boxes to split
+     */
+    private void splitBoxes(final PriorityQueue<Vbox> queue, final int maxSize) {
+        while (queue.size() < maxSize) {
+            final Vbox vbox = queue.poll();
+
+            if (vbox != null && vbox.canSplit()) {
+                // First split the box, and offer the result
+                queue.offer(vbox.splitBox());
+
+                if (LOG_TIMINGS) {
+                    mTimingLogger.addSplit("Box split");
+                }
+                // Then offer the box back
+                queue.offer(vbox);
+            } else {
+                if (LOG_TIMINGS) {
+                    mTimingLogger.addSplit("All boxes split");
+                }
+                // If we get here then there are no more boxes to split, so return
+                return;
+            }
+        }
+    }
+
+    private List<Swatch> generateAverageColors(Collection<Vbox> vboxes) {
+        ArrayList<Swatch> colors = new ArrayList<>(vboxes.size());
+        for (Vbox vbox : vboxes) {
+            Swatch swatch = vbox.getAverageColor();
+            if (!shouldIgnoreColor(swatch)) {
+                // As we're averaging a color box, we can still get colors which we do not want, so
+                // we check again here
+                colors.add(swatch);
+            }
+        }
+        return colors;
+    }
+
+    /**
+     * Represents a tightly fitting box around a color space.
+     */
+    private class Vbox {
+        // lower and upper index are inclusive
+        private int mLowerIndex;
+        private int mUpperIndex;
+        // Population of colors within this box
+        private int mPopulation;
+
+        private int mMinRed, mMaxRed;
+        private int mMinGreen, mMaxGreen;
+        private int mMinBlue, mMaxBlue;
+
+        Vbox(int lowerIndex, int upperIndex) {
+            mLowerIndex = lowerIndex;
+            mUpperIndex = upperIndex;
+            fitBox();
+        }
+
+        final int getVolume() {
+            return (mMaxRed - mMinRed + 1) * (mMaxGreen - mMinGreen + 1) *
+                    (mMaxBlue - mMinBlue + 1);
+        }
+
+        final boolean canSplit() {
+            return getColorCount() > 1;
+        }
+
+        final int getColorCount() {
+            return 1 + mUpperIndex - mLowerIndex;
+        }
+
+        /**
+         * Recomputes the boundaries of this box to tightly fit the colors within the box.
+         */
+        final void fitBox() {
+            final int[] colors = mColors;
+            final int[] hist = mHistogram;
+
+            // Reset the min and max to opposite values
+            int minRed, minGreen, minBlue;
+            minRed = minGreen = minBlue = Integer.MAX_VALUE;
+            int maxRed, maxGreen, maxBlue;
+            maxRed = maxGreen = maxBlue = Integer.MIN_VALUE;
+            int count = 0;
+
+            for (int i = mLowerIndex; i <= mUpperIndex; i++) {
+                final int color = colors[i];
+                count += hist[color];
+
+                final int r = quantizedRed(color);
+                final int g = quantizedGreen(color);
+                final int b = quantizedBlue(color);
+                if (r > maxRed) {
+                    maxRed = r;
+                }
+                if (r < minRed) {
+                    minRed = r;
+                }
+                if (g > maxGreen) {
+                    maxGreen = g;
+                }
+                if (g < minGreen) {
+                    minGreen = g;
+                }
+                if (b > maxBlue) {
+                    maxBlue = b;
+                }
+                if (b < minBlue) {
+                    minBlue = b;
+                }
+            }
+
+            mMinRed = minRed;
+            mMaxRed = maxRed;
+            mMinGreen = minGreen;
+            mMaxGreen = maxGreen;
+            mMinBlue = minBlue;
+            mMaxBlue = maxBlue;
+            mPopulation = count;
+        }
+
+        /**
+         * Split this color box at the mid-point along its longest dimension
+         *
+         * @return the new ColorBox
+         */
+        final Vbox splitBox() {
+            if (!canSplit()) {
+                throw new IllegalStateException("Can not split a box with only 1 color");
+            }
+
+            // find median along the longest dimension
+            final int splitPoint = findSplitPoint();
+
+            Vbox newBox = new Vbox(splitPoint + 1, mUpperIndex);
+
+            // Now change this box's upperIndex and recompute the color boundaries
+            mUpperIndex = splitPoint;
+            fitBox();
+
+            return newBox;
+        }
+
+        /**
+         * @return the dimension which this box is largest in
+         */
+        final int getLongestColorDimension() {
+            final int redLength = mMaxRed - mMinRed;
+            final int greenLength = mMaxGreen - mMinGreen;
+            final int blueLength = mMaxBlue - mMinBlue;
+
+            if (redLength >= greenLength && redLength >= blueLength) {
+                return COMPONENT_RED;
+            } else if (greenLength >= redLength && greenLength >= blueLength) {
+                return COMPONENT_GREEN;
+            } else {
+                return COMPONENT_BLUE;
+            }
+        }
+
+        /**
+         * Finds the point within this box's lowerIndex and upperIndex index of where to split.
+         *
+         * This is calculated by finding the longest color dimension, and then sorting the
+         * sub-array based on that dimension value in each color. The colors are then iterated over
+         * until a color is found with at least the midpoint of the whole box's dimension midpoint.
+         *
+         * @return the index of the colors array to split from
+         */
+        final int findSplitPoint() {
+            final int longestDimension = getLongestColorDimension();
+            final int[] colors = mColors;
+            final int[] hist = mHistogram;
+
+            // We need to sort the colors in this box based on the longest color dimension.
+            // As we can't use a Comparator to define the sort logic, we modify each color so that
+            // its most significant is the desired dimension
+            modifySignificantOctet(colors, longestDimension, mLowerIndex, mUpperIndex);
+
+            // Now sort... Arrays.sort uses a exclusive toIndex so we need to add 1
+            Arrays.sort(colors, mLowerIndex, mUpperIndex + 1);
+
+            // Now revert all of the colors so that they are packed as RGB again
+            modifySignificantOctet(colors, longestDimension, mLowerIndex, mUpperIndex);
+
+            final int midPoint = mPopulation / 2;
+            for (int i = mLowerIndex, count = 0; i <= mUpperIndex; i++)  {
+                count += hist[colors[i]];
+                if (count >= midPoint) {
+                    return i;
+                }
+            }
+
+            return mLowerIndex;
+        }
+
+        /**
+         * @return the average color of this box.
+         */
+        final Swatch getAverageColor() {
+            final int[] colors = mColors;
+            final int[] hist = mHistogram;
+            int redSum = 0;
+            int greenSum = 0;
+            int blueSum = 0;
+            int totalPopulation = 0;
+
+            for (int i = mLowerIndex; i <= mUpperIndex; i++) {
+                final int color = colors[i];
+                final int colorPopulation = hist[color];
+
+                totalPopulation += colorPopulation;
+                redSum += colorPopulation * quantizedRed(color);
+                greenSum += colorPopulation * quantizedGreen(color);
+                blueSum += colorPopulation * quantizedBlue(color);
+            }
+
+            final int redMean = Math.round(redSum / (float) totalPopulation);
+            final int greenMean = Math.round(greenSum / (float) totalPopulation);
+            final int blueMean = Math.round(blueSum / (float) totalPopulation);
+
+            return new Swatch(approximateToRgb888(redMean, greenMean, blueMean), totalPopulation);
+        }
+    }
+
+    /**
+     * Modify the significant octet in a packed color int. Allows sorting based on the value of a
+     * single color component. This relies on all components being the same word size.
+     *
+     * @see Vbox#findSplitPoint()
+     */
+    static void modifySignificantOctet(final int[] a, final int dimension,
+            final int lower, final int upper) {
+        switch (dimension) {
+            case COMPONENT_RED:
+                // Already in RGB, no need to do anything
+                break;
+            case COMPONENT_GREEN:
+                // We need to do a RGB to GRB swap, or vice-versa
+                for (int i = lower; i <= upper; i++) {
+                    final int color = a[i];
+                    a[i] = quantizedGreen(color) << (QUANTIZE_WORD_WIDTH + QUANTIZE_WORD_WIDTH)
+                            | quantizedRed(color) << QUANTIZE_WORD_WIDTH
+                            | quantizedBlue(color);
+                }
+                break;
+            case COMPONENT_BLUE:
+                // We need to do a RGB to BGR swap, or vice-versa
+                for (int i = lower; i <= upper; i++) {
+                    final int color = a[i];
+                    a[i] = quantizedBlue(color) << (QUANTIZE_WORD_WIDTH + QUANTIZE_WORD_WIDTH)
+                            | quantizedGreen(color) << QUANTIZE_WORD_WIDTH
+                            | quantizedRed(color);
+                }
+                break;
+        }
+    }
+
+    private boolean shouldIgnoreColor(int color565) {
+        final int rgb = approximateToRgb888(color565);
+        ColorUtils.colorToHSL(rgb, mTempHsl);
+        return shouldIgnoreColor(rgb, mTempHsl);
+    }
+
+    private boolean shouldIgnoreColor(Swatch color) {
+        return shouldIgnoreColor(color.getRgb(), color.getHsl());
+    }
+
+    private boolean shouldIgnoreColor(int rgb, float[] hsl) {
+        if (mFilters != null && mFilters.length > 0) {
+            for (int i = 0, count = mFilters.length; i < count; i++) {
+                if (!mFilters[i].isAllowed(rgb, hsl)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Comparator which sorts {@link Vbox} instances based on their volume, in descending order
+     */
+    private static final Comparator<Vbox> VBOX_COMPARATOR_VOLUME = new Comparator<Vbox>() {
+        @Override
+        public int compare(Vbox lhs, Vbox rhs) {
+            return rhs.getVolume() - lhs.getVolume();
+        }
+    };
+
+    /**
+     * Quantized a RGB888 value to have a word width of {@value #QUANTIZE_WORD_WIDTH}.
+     */
+    private static int quantizeFromRgb888(int color) {
+        int r = modifyWordWidth(Color.red(color), 8, QUANTIZE_WORD_WIDTH);
+        int g = modifyWordWidth(Color.green(color), 8, QUANTIZE_WORD_WIDTH);
+        int b = modifyWordWidth(Color.blue(color), 8, QUANTIZE_WORD_WIDTH);
+        return r << (QUANTIZE_WORD_WIDTH + QUANTIZE_WORD_WIDTH) | g << QUANTIZE_WORD_WIDTH | b;
+    }
+
+    /**
+     * Quantized RGB888 values to have a word width of {@value #QUANTIZE_WORD_WIDTH}.
+     */
+    static int approximateToRgb888(int r, int g, int b) {
+        return Color.rgb(modifyWordWidth(r, QUANTIZE_WORD_WIDTH, 8),
+                modifyWordWidth(g, QUANTIZE_WORD_WIDTH, 8),
+                modifyWordWidth(b, QUANTIZE_WORD_WIDTH, 8));
+    }
+
+    private static int approximateToRgb888(int color) {
+        return approximateToRgb888(quantizedRed(color), quantizedGreen(color), quantizedBlue(color));
+    }
+
+    /**
+     * @return red component of the quantized color
+     */
+    static int quantizedRed(int color) {
+        return (color >> (QUANTIZE_WORD_WIDTH + QUANTIZE_WORD_WIDTH)) & QUANTIZE_WORD_MASK;
+    }
+
+    /**
+     * @return green component of a quantized color
+     */
+    static int quantizedGreen(int color) {
+        return (color >> QUANTIZE_WORD_WIDTH) & QUANTIZE_WORD_MASK;
+    }
+
+    /**
+     * @return blue component of a quantized color
+     */
+    static int quantizedBlue(int color) {
+        return color & QUANTIZE_WORD_MASK;
+    }
+
+    private static int modifyWordWidth(int value, int currentWidth, int targetWidth) {
+        final int newValue;
+        if (targetWidth > currentWidth) {
+            // If we're approximating up in word width, we'll shift up
+            newValue = value << (targetWidth - currentWidth);
+        } else {
+            // Else, we will just shift and keep the MSB
+            newValue = value >> (currentWidth - targetWidth);
+        }
+        return newValue & ((1 << targetWidth) - 1);
+    }
+
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/graphics/palette/Palette.java b/core/java/com/android/internal/graphics/palette/Palette.java
new file mode 100644
index 0000000..9f1504a
--- /dev/null
+++ b/core/java/com/android/internal/graphics/palette/Palette.java
@@ -0,0 +1,990 @@
+/*
+ * Copyright (C) 2017 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.internal.graphics.palette;
+
+import android.annotation.ColorInt;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.os.AsyncTask;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.SparseBooleanArray;
+import android.util.TimingLogger;
+
+import com.android.internal.graphics.ColorUtils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * Copied from: /frameworks/support/v7/palette/src/main/java/android/support/v7/
+ * graphics/Palette.java
+ *
+ * A helper class to extract prominent colors from an image.
+ * <p>
+ * A number of colors with different profiles are extracted from the image:
+ * <ul>
+ *     <li>Vibrant</li>
+ *     <li>Vibrant Dark</li>
+ *     <li>Vibrant Light</li>
+ *     <li>Muted</li>
+ *     <li>Muted Dark</li>
+ *     <li>Muted Light</li>
+ * </ul>
+ * These can be retrieved from the appropriate getter method.
+ *
+ * <p>
+ * Instances are created with a {@link Palette.Builder} which supports several options to tweak the
+ * generated Palette. See that class' documentation for more information.
+ * <p>
+ * Generation should always be completed on a background thread, ideally the one in
+ * which you load your image on. {@link Palette.Builder} supports both synchronous and asynchronous
+ * generation:
+ *
+ * <pre>
+ * // Synchronous
+ * Palette p = Palette.from(bitmap).generate();
+ *
+ * // Asynchronous
+ * Palette.from(bitmap).generate(new PaletteAsyncListener() {
+ *     public void onGenerated(Palette p) {
+ *         // Use generated instance
+ *     }
+ * });
+ * </pre>
+ */
+public final class Palette {
+
+    /**
+     * Listener to be used with {@link #generateAsync(Bitmap, Palette.PaletteAsyncListener)} or
+     * {@link #generateAsync(Bitmap, int, Palette.PaletteAsyncListener)}
+     */
+    public interface PaletteAsyncListener {
+
+        /**
+         * Called when the {@link Palette} has been generated.
+         */
+        void onGenerated(Palette palette);
+    }
+
+    static final int DEFAULT_RESIZE_BITMAP_AREA = 112 * 112;
+    static final int DEFAULT_CALCULATE_NUMBER_COLORS = 16;
+
+    static final float MIN_CONTRAST_TITLE_TEXT = 3.0f;
+    static final float MIN_CONTRAST_BODY_TEXT = 4.5f;
+
+    static final String LOG_TAG = "Palette";
+    static final boolean LOG_TIMINGS = false;
+
+    /**
+     * Start generating a {@link Palette} with the returned {@link Palette.Builder} instance.
+     */
+    public static Palette.Builder from(Bitmap bitmap) {
+        return new Palette.Builder(bitmap);
+    }
+
+    /**
+     * Generate a {@link Palette} from the pre-generated list of {@link Palette.Swatch} swatches.
+     * This is useful for testing, or if you want to resurrect a {@link Palette} instance from a
+     * list of swatches. Will return null if the {@code swatches} is null.
+     */
+    public static Palette from(List<Palette.Swatch> swatches) {
+        return new Palette.Builder(swatches).generate();
+    }
+
+    /**
+     * @deprecated Use {@link Palette.Builder} to generate the Palette.
+     */
+    @Deprecated
+    public static Palette generate(Bitmap bitmap) {
+        return from(bitmap).generate();
+    }
+
+    /**
+     * @deprecated Use {@link Palette.Builder} to generate the Palette.
+     */
+    @Deprecated
+    public static Palette generate(Bitmap bitmap, int numColors) {
+        return from(bitmap).maximumColorCount(numColors).generate();
+    }
+
+    /**
+     * @deprecated Use {@link Palette.Builder} to generate the Palette.
+     */
+    @Deprecated
+    public static AsyncTask<Bitmap, Void, Palette> generateAsync(
+            Bitmap bitmap, Palette.PaletteAsyncListener listener) {
+        return from(bitmap).generate(listener);
+    }
+
+    /**
+     * @deprecated Use {@link Palette.Builder} to generate the Palette.
+     */
+    @Deprecated
+    public static AsyncTask<Bitmap, Void, Palette> generateAsync(
+            final Bitmap bitmap, final int numColors, final Palette.PaletteAsyncListener listener) {
+        return from(bitmap).maximumColorCount(numColors).generate(listener);
+    }
+
+    private final List<Palette.Swatch> mSwatches;
+    private final List<Target> mTargets;
+
+    private final Map<Target, Palette.Swatch> mSelectedSwatches;
+    private final SparseBooleanArray mUsedColors;
+
+    private final Palette.Swatch mDominantSwatch;
+
+    Palette(List<Palette.Swatch> swatches, List<Target> targets) {
+        mSwatches = swatches;
+        mTargets = targets;
+
+        mUsedColors = new SparseBooleanArray();
+        mSelectedSwatches = new ArrayMap<>();
+
+        mDominantSwatch = findDominantSwatch();
+    }
+
+    /**
+     * Returns all of the swatches which make up the palette.
+     */
+    @NonNull
+    public List<Palette.Swatch> getSwatches() {
+        return Collections.unmodifiableList(mSwatches);
+    }
+
+    /**
+     * Returns the targets used to generate this palette.
+     */
+    @NonNull
+    public List<Target> getTargets() {
+        return Collections.unmodifiableList(mTargets);
+    }
+
+    /**
+     * Returns the most vibrant swatch in the palette. Might be null.
+     *
+     * @see Target#VIBRANT
+     */
+    @Nullable
+    public Palette.Swatch getVibrantSwatch() {
+        return getSwatchForTarget(Target.VIBRANT);
+    }
+
+    /**
+     * Returns a light and vibrant swatch from the palette. Might be null.
+     *
+     * @see Target#LIGHT_VIBRANT
+     */
+    @Nullable
+    public Palette.Swatch getLightVibrantSwatch() {
+        return getSwatchForTarget(Target.LIGHT_VIBRANT);
+    }
+
+    /**
+     * Returns a dark and vibrant swatch from the palette. Might be null.
+     *
+     * @see Target#DARK_VIBRANT
+     */
+    @Nullable
+    public Palette.Swatch getDarkVibrantSwatch() {
+        return getSwatchForTarget(Target.DARK_VIBRANT);
+    }
+
+    /**
+     * Returns a muted swatch from the palette. Might be null.
+     *
+     * @see Target#MUTED
+     */
+    @Nullable
+    public Palette.Swatch getMutedSwatch() {
+        return getSwatchForTarget(Target.MUTED);
+    }
+
+    /**
+     * Returns a muted and light swatch from the palette. Might be null.
+     *
+     * @see Target#LIGHT_MUTED
+     */
+    @Nullable
+    public Palette.Swatch getLightMutedSwatch() {
+        return getSwatchForTarget(Target.LIGHT_MUTED);
+    }
+
+    /**
+     * Returns a muted and dark swatch from the palette. Might be null.
+     *
+     * @see Target#DARK_MUTED
+     */
+    @Nullable
+    public Palette.Swatch getDarkMutedSwatch() {
+        return getSwatchForTarget(Target.DARK_MUTED);
+    }
+
+    /**
+     * Returns the most vibrant color in the palette as an RGB packed int.
+     *
+     * @param defaultColor value to return if the swatch isn't available
+     * @see #getVibrantSwatch()
+     */
+    @ColorInt
+    public int getVibrantColor(@ColorInt final int defaultColor) {
+        return getColorForTarget(Target.VIBRANT, defaultColor);
+    }
+
+    /**
+     * Returns a light and vibrant color from the palette as an RGB packed int.
+     *
+     * @param defaultColor value to return if the swatch isn't available
+     * @see #getLightVibrantSwatch()
+     */
+    @ColorInt
+    public int getLightVibrantColor(@ColorInt final int defaultColor) {
+        return getColorForTarget(Target.LIGHT_VIBRANT, defaultColor);
+    }
+
+    /**
+     * Returns a dark and vibrant color from the palette as an RGB packed int.
+     *
+     * @param defaultColor value to return if the swatch isn't available
+     * @see #getDarkVibrantSwatch()
+     */
+    @ColorInt
+    public int getDarkVibrantColor(@ColorInt final int defaultColor) {
+        return getColorForTarget(Target.DARK_VIBRANT, defaultColor);
+    }
+
+    /**
+     * Returns a muted color from the palette as an RGB packed int.
+     *
+     * @param defaultColor value to return if the swatch isn't available
+     * @see #getMutedSwatch()
+     */
+    @ColorInt
+    public int getMutedColor(@ColorInt final int defaultColor) {
+        return getColorForTarget(Target.MUTED, defaultColor);
+    }
+
+    /**
+     * Returns a muted and light color from the palette as an RGB packed int.
+     *
+     * @param defaultColor value to return if the swatch isn't available
+     * @see #getLightMutedSwatch()
+     */
+    @ColorInt
+    public int getLightMutedColor(@ColorInt final int defaultColor) {
+        return getColorForTarget(Target.LIGHT_MUTED, defaultColor);
+    }
+
+    /**
+     * Returns a muted and dark color from the palette as an RGB packed int.
+     *
+     * @param defaultColor value to return if the swatch isn't available
+     * @see #getDarkMutedSwatch()
+     */
+    @ColorInt
+    public int getDarkMutedColor(@ColorInt final int defaultColor) {
+        return getColorForTarget(Target.DARK_MUTED, defaultColor);
+    }
+
+    /**
+     * Returns the selected swatch for the given target from the palette, or {@code null} if one
+     * could not be found.
+     */
+    @Nullable
+    public Palette.Swatch getSwatchForTarget(@NonNull final Target target) {
+        return mSelectedSwatches.get(target);
+    }
+
+    /**
+     * Returns the selected color for the given target from the palette as an RGB packed int.
+     *
+     * @param defaultColor value to return if the swatch isn't available
+     */
+    @ColorInt
+    public int getColorForTarget(@NonNull final Target target, @ColorInt final int defaultColor) {
+        Palette.Swatch swatch = getSwatchForTarget(target);
+        return swatch != null ? swatch.getRgb() : defaultColor;
+    }
+
+    /**
+     * Returns the dominant swatch from the palette.
+     *
+     * <p>The dominant swatch is defined as the swatch with the greatest population (frequency)
+     * within the palette.</p>
+     */
+    @Nullable
+    public Palette.Swatch getDominantSwatch() {
+        return mDominantSwatch;
+    }
+
+    /**
+     * Returns the color of the dominant swatch from the palette, as an RGB packed int.
+     *
+     * @param defaultColor value to return if the swatch isn't available
+     * @see #getDominantSwatch()
+     */
+    @ColorInt
+    public int getDominantColor(@ColorInt int defaultColor) {
+        return mDominantSwatch != null ? mDominantSwatch.getRgb() : defaultColor;
+    }
+
+    void generate() {
+        // We need to make sure that the scored targets are generated first. This is so that
+        // inherited targets have something to inherit from
+        for (int i = 0, count = mTargets.size(); i < count; i++) {
+            final Target target = mTargets.get(i);
+            target.normalizeWeights();
+            mSelectedSwatches.put(target, generateScoredTarget(target));
+        }
+        // We now clear out the used colors
+        mUsedColors.clear();
+    }
+
+    private Palette.Swatch generateScoredTarget(final Target target) {
+        final Palette.Swatch maxScoreSwatch = getMaxScoredSwatchForTarget(target);
+        if (maxScoreSwatch != null && target.isExclusive()) {
+            // If we have a swatch, and the target is exclusive, add the color to the used list
+            mUsedColors.append(maxScoreSwatch.getRgb(), true);
+        }
+        return maxScoreSwatch;
+    }
+
+    private Palette.Swatch getMaxScoredSwatchForTarget(final Target target) {
+        float maxScore = 0;
+        Palette.Swatch maxScoreSwatch = null;
+        for (int i = 0, count = mSwatches.size(); i < count; i++) {
+            final Palette.Swatch swatch = mSwatches.get(i);
+            if (shouldBeScoredForTarget(swatch, target)) {
+                final float score = generateScore(swatch, target);
+                if (maxScoreSwatch == null || score > maxScore) {
+                    maxScoreSwatch = swatch;
+                    maxScore = score;
+                }
+            }
+        }
+        return maxScoreSwatch;
+    }
+
+    private boolean shouldBeScoredForTarget(final Palette.Swatch swatch, final Target target) {
+        // Check whether the HSL values are within the correct ranges, and this color hasn't
+        // been used yet.
+        final float hsl[] = swatch.getHsl();
+        return hsl[1] >= target.getMinimumSaturation() && hsl[1] <= target.getMaximumSaturation()
+                && hsl[2] >= target.getMinimumLightness() && hsl[2] <= target.getMaximumLightness()
+                && !mUsedColors.get(swatch.getRgb());
+    }
+
+    private float generateScore(Palette.Swatch swatch, Target target) {
+        final float[] hsl = swatch.getHsl();
+
+        float saturationScore = 0;
+        float luminanceScore = 0;
+        float populationScore = 0;
+
+        final int maxPopulation = mDominantSwatch != null ? mDominantSwatch.getPopulation() : 1;
+
+        if (target.getSaturationWeight() > 0) {
+            saturationScore = target.getSaturationWeight()
+                    * (1f - Math.abs(hsl[1] - target.getTargetSaturation()));
+        }
+        if (target.getLightnessWeight() > 0) {
+            luminanceScore = target.getLightnessWeight()
+                    * (1f - Math.abs(hsl[2] - target.getTargetLightness()));
+        }
+        if (target.getPopulationWeight() > 0) {
+            populationScore = target.getPopulationWeight()
+                    * (swatch.getPopulation() / (float) maxPopulation);
+        }
+
+        return saturationScore + luminanceScore + populationScore;
+    }
+
+    private Palette.Swatch findDominantSwatch() {
+        int maxPop = Integer.MIN_VALUE;
+        Palette.Swatch maxSwatch = null;
+        for (int i = 0, count = mSwatches.size(); i < count; i++) {
+            Palette.Swatch swatch = mSwatches.get(i);
+            if (swatch.getPopulation() > maxPop) {
+                maxSwatch = swatch;
+                maxPop = swatch.getPopulation();
+            }
+        }
+        return maxSwatch;
+    }
+
+    private static float[] copyHslValues(Palette.Swatch color) {
+        final float[] newHsl = new float[3];
+        System.arraycopy(color.getHsl(), 0, newHsl, 0, 3);
+        return newHsl;
+    }
+
+    /**
+     * Represents a color swatch generated from an image's palette. The RGB color can be retrieved
+     * by calling {@link #getRgb()}.
+     */
+    public static final class Swatch {
+        private final int mRed, mGreen, mBlue;
+        private final int mRgb;
+        private final int mPopulation;
+
+        private boolean mGeneratedTextColors;
+        private int mTitleTextColor;
+        private int mBodyTextColor;
+
+        private float[] mHsl;
+
+        public Swatch(@ColorInt int color, int population) {
+            mRed = Color.red(color);
+            mGreen = Color.green(color);
+            mBlue = Color.blue(color);
+            mRgb = color;
+            mPopulation = population;
+        }
+
+        Swatch(int red, int green, int blue, int population) {
+            mRed = red;
+            mGreen = green;
+            mBlue = blue;
+            mRgb = Color.rgb(red, green, blue);
+            mPopulation = population;
+        }
+
+        Swatch(float[] hsl, int population) {
+            this(ColorUtils.HSLToColor(hsl), population);
+            mHsl = hsl;
+        }
+
+        /**
+         * @return this swatch's RGB color value
+         */
+        @ColorInt
+        public int getRgb() {
+            return mRgb;
+        }
+
+        /**
+         * Return this swatch's HSL values.
+         *     hsv[0] is Hue [0 .. 360)
+         *     hsv[1] is Saturation [0...1]
+         *     hsv[2] is Lightness [0...1]
+         */
+        public float[] getHsl() {
+            if (mHsl == null) {
+                mHsl = new float[3];
+            }
+            ColorUtils.RGBToHSL(mRed, mGreen, mBlue, mHsl);
+            return mHsl;
+        }
+
+        /**
+         * @return the number of pixels represented by this swatch
+         */
+        public int getPopulation() {
+            return mPopulation;
+        }
+
+        /**
+         * Returns an appropriate color to use for any 'title' text which is displayed over this
+         * {@link Palette.Swatch}'s color. This color is guaranteed to have sufficient contrast.
+         */
+        @ColorInt
+        public int getTitleTextColor() {
+            ensureTextColorsGenerated();
+            return mTitleTextColor;
+        }
+
+        /**
+         * Returns an appropriate color to use for any 'body' text which is displayed over this
+         * {@link Palette.Swatch}'s color. This color is guaranteed to have sufficient contrast.
+         */
+        @ColorInt
+        public int getBodyTextColor() {
+            ensureTextColorsGenerated();
+            return mBodyTextColor;
+        }
+
+        private void ensureTextColorsGenerated() {
+            if (!mGeneratedTextColors) {
+                // First check white, as most colors will be dark
+                final int lightBodyAlpha = ColorUtils.calculateMinimumAlpha(
+                        Color.WHITE, mRgb, MIN_CONTRAST_BODY_TEXT);
+                final int lightTitleAlpha = ColorUtils.calculateMinimumAlpha(
+                        Color.WHITE, mRgb, MIN_CONTRAST_TITLE_TEXT);
+
+                if (lightBodyAlpha != -1 && lightTitleAlpha != -1) {
+                    // If we found valid light values, use them and return
+                    mBodyTextColor = ColorUtils.setAlphaComponent(Color.WHITE, lightBodyAlpha);
+                    mTitleTextColor = ColorUtils.setAlphaComponent(Color.WHITE, lightTitleAlpha);
+                    mGeneratedTextColors = true;
+                    return;
+                }
+
+                final int darkBodyAlpha = ColorUtils.calculateMinimumAlpha(
+                        Color.BLACK, mRgb, MIN_CONTRAST_BODY_TEXT);
+                final int darkTitleAlpha = ColorUtils.calculateMinimumAlpha(
+                        Color.BLACK, mRgb, MIN_CONTRAST_TITLE_TEXT);
+
+                if (darkBodyAlpha != -1 && darkTitleAlpha != -1) {
+                    // If we found valid dark values, use them and return
+                    mBodyTextColor = ColorUtils.setAlphaComponent(Color.BLACK, darkBodyAlpha);
+                    mTitleTextColor = ColorUtils.setAlphaComponent(Color.BLACK, darkTitleAlpha);
+                    mGeneratedTextColors = true;
+                    return;
+                }
+
+                // If we reach here then we can not find title and body values which use the same
+                // lightness, we need to use mismatched values
+                mBodyTextColor = lightBodyAlpha != -1
+                        ? ColorUtils.setAlphaComponent(Color.WHITE, lightBodyAlpha)
+                        : ColorUtils.setAlphaComponent(Color.BLACK, darkBodyAlpha);
+                mTitleTextColor = lightTitleAlpha != -1
+                        ? ColorUtils.setAlphaComponent(Color.WHITE, lightTitleAlpha)
+                        : ColorUtils.setAlphaComponent(Color.BLACK, darkTitleAlpha);
+                mGeneratedTextColors = true;
+            }
+        }
+
+        @Override
+        public String toString() {
+            return new StringBuilder(getClass().getSimpleName())
+                    .append(" [RGB: #").append(Integer.toHexString(getRgb())).append(']')
+                    .append(" [HSL: ").append(Arrays.toString(getHsl())).append(']')
+                    .append(" [Population: ").append(mPopulation).append(']')
+                    .append(" [Title Text: #").append(Integer.toHexString(getTitleTextColor()))
+                    .append(']')
+                    .append(" [Body Text: #").append(Integer.toHexString(getBodyTextColor()))
+                    .append(']').toString();
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            Palette.Swatch
+                    swatch = (Palette.Swatch) o;
+            return mPopulation == swatch.mPopulation && mRgb == swatch.mRgb;
+        }
+
+        @Override
+        public int hashCode() {
+            return 31 * mRgb + mPopulation;
+        }
+    }
+
+    /**
+     * Builder class for generating {@link Palette} instances.
+     */
+    public static final class Builder {
+        private final List<Palette.Swatch> mSwatches;
+        private final Bitmap mBitmap;
+
+        private final List<Target> mTargets = new ArrayList<>();
+
+        private int mMaxColors = DEFAULT_CALCULATE_NUMBER_COLORS;
+        private int mResizeArea = DEFAULT_RESIZE_BITMAP_AREA;
+        private int mResizeMaxDimension = -1;
+
+        private final List<Palette.Filter> mFilters = new ArrayList<>();
+        private Rect mRegion;
+
+        /**
+         * Construct a new {@link Palette.Builder} using a source {@link Bitmap}
+         */
+        public Builder(Bitmap bitmap) {
+            if (bitmap == null || bitmap.isRecycled()) {
+                throw new IllegalArgumentException("Bitmap is not valid");
+            }
+            mFilters.add(DEFAULT_FILTER);
+            mBitmap = bitmap;
+            mSwatches = null;
+
+            // Add the default targets
+            mTargets.add(Target.LIGHT_VIBRANT);
+            mTargets.add(Target.VIBRANT);
+            mTargets.add(Target.DARK_VIBRANT);
+            mTargets.add(Target.LIGHT_MUTED);
+            mTargets.add(Target.MUTED);
+            mTargets.add(Target.DARK_MUTED);
+        }
+
+        /**
+         * Construct a new {@link Palette.Builder} using a list of {@link Palette.Swatch} instances.
+         * Typically only used for testing.
+         */
+        public Builder(List<Palette.Swatch> swatches) {
+            if (swatches == null || swatches.isEmpty()) {
+                throw new IllegalArgumentException("List of Swatches is not valid");
+            }
+            mFilters.add(DEFAULT_FILTER);
+            mSwatches = swatches;
+            mBitmap = null;
+        }
+
+        /**
+         * Set the maximum number of colors to use in the quantization step when using a
+         * {@link android.graphics.Bitmap} as the source.
+         * <p>
+         * Good values for depend on the source image type. For landscapes, good values are in
+         * the range 10-16. For images which are largely made up of people's faces then this
+         * value should be increased to ~24.
+         */
+        @NonNull
+        public Palette.Builder maximumColorCount(int colors) {
+            mMaxColors = colors;
+            return this;
+        }
+
+        /**
+         * Set the resize value when using a {@link android.graphics.Bitmap} as the source.
+         * If the bitmap's largest dimension is greater than the value specified, then the bitmap
+         * will be resized so that its largest dimension matches {@code maxDimension}. If the
+         * bitmap is smaller or equal, the original is used as-is.
+         *
+         * @deprecated Using {@link #resizeBitmapArea(int)} is preferred since it can handle
+         * abnormal aspect ratios more gracefully.
+         *
+         * @param maxDimension the number of pixels that the max dimension should be scaled down to,
+         *                     or any value <= 0 to disable resizing.
+         */
+        @NonNull
+        @Deprecated
+        public Palette.Builder resizeBitmapSize(final int maxDimension) {
+            mResizeMaxDimension = maxDimension;
+            mResizeArea = -1;
+            return this;
+        }
+
+        /**
+         * Set the resize value when using a {@link android.graphics.Bitmap} as the source.
+         * If the bitmap's area is greater than the value specified, then the bitmap
+         * will be resized so that its area matches {@code area}. If the
+         * bitmap is smaller or equal, the original is used as-is.
+         * <p>
+         * This value has a large effect on the processing time. The larger the resized image is,
+         * the greater time it will take to generate the palette. The smaller the image is, the
+         * more detail is lost in the resulting image and thus less precision for color selection.
+         *
+         * @param area the number of pixels that the intermediary scaled down Bitmap should cover,
+         *             or any value <= 0 to disable resizing.
+         */
+        @NonNull
+        public Palette.Builder resizeBitmapArea(final int area) {
+            mResizeArea = area;
+            mResizeMaxDimension = -1;
+            return this;
+        }
+
+        /**
+         * Clear all added filters. This includes any default filters added automatically by
+         * {@link Palette}.
+         */
+        @NonNull
+        public Palette.Builder clearFilters() {
+            mFilters.clear();
+            return this;
+        }
+
+        /**
+         * Add a filter to be able to have fine grained control over which colors are
+         * allowed in the resulting palette.
+         *
+         * @param filter filter to add.
+         */
+        @NonNull
+        public Palette.Builder addFilter(
+                Palette.Filter filter) {
+            if (filter != null) {
+                mFilters.add(filter);
+            }
+            return this;
+        }
+
+        /**
+         * Set a region of the bitmap to be used exclusively when calculating the palette.
+         * <p>This only works when the original input is a {@link Bitmap}.</p>
+         *
+         * @param left The left side of the rectangle used for the region.
+         * @param top The top of the rectangle used for the region.
+         * @param right The right side of the rectangle used for the region.
+         * @param bottom The bottom of the rectangle used for the region.
+         */
+        @NonNull
+        public Palette.Builder setRegion(int left, int top, int right, int bottom) {
+            if (mBitmap != null) {
+                if (mRegion == null) mRegion = new Rect();
+                // Set the Rect to be initially the whole Bitmap
+                mRegion.set(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
+                // Now just get the intersection with the region
+                if (!mRegion.intersect(left, top, right, bottom)) {
+                    throw new IllegalArgumentException("The given region must intersect with "
+                            + "the Bitmap's dimensions.");
+                }
+            }
+            return this;
+        }
+
+        /**
+         * Clear any previously region set via {@link #setRegion(int, int, int, int)}.
+         */
+        @NonNull
+        public Palette.Builder clearRegion() {
+            mRegion = null;
+            return this;
+        }
+
+        /**
+         * Add a target profile to be generated in the palette.
+         *
+         * <p>You can retrieve the result via {@link Palette#getSwatchForTarget(Target)}.</p>
+         */
+        @NonNull
+        public Palette.Builder addTarget(@NonNull final Target target) {
+            if (!mTargets.contains(target)) {
+                mTargets.add(target);
+            }
+            return this;
+        }
+
+        /**
+         * Clear all added targets. This includes any default targets added automatically by
+         * {@link Palette}.
+         */
+        @NonNull
+        public Palette.Builder clearTargets() {
+            if (mTargets != null) {
+                mTargets.clear();
+            }
+            return this;
+        }
+
+        /**
+         * Generate and return the {@link Palette} synchronously.
+         */
+        @NonNull
+        public Palette generate() {
+            final TimingLogger logger = LOG_TIMINGS
+                    ? new TimingLogger(LOG_TAG, "Generation")
+                    : null;
+
+            List<Palette.Swatch> swatches;
+
+            if (mBitmap != null) {
+                // We have a Bitmap so we need to use quantization to reduce the number of colors
+
+                // First we'll scale down the bitmap if needed
+                final Bitmap bitmap = scaleBitmapDown(mBitmap);
+
+                if (logger != null) {
+                    logger.addSplit("Processed Bitmap");
+                }
+
+                final Rect region = mRegion;
+                if (bitmap != mBitmap && region != null) {
+                    // If we have a scaled bitmap and a selected region, we need to scale down the
+                    // region to match the new scale
+                    final double scale = bitmap.getWidth() / (double) mBitmap.getWidth();
+                    region.left = (int) Math.floor(region.left * scale);
+                    region.top = (int) Math.floor(region.top * scale);
+                    region.right = Math.min((int) Math.ceil(region.right * scale),
+                            bitmap.getWidth());
+                    region.bottom = Math.min((int) Math.ceil(region.bottom * scale),
+                            bitmap.getHeight());
+                }
+
+                // Now generate a quantizer from the Bitmap
+                final ColorCutQuantizer quantizer = new ColorCutQuantizer(
+                        getPixelsFromBitmap(bitmap),
+                        mMaxColors,
+                        mFilters.isEmpty() ? null : mFilters.toArray(new Palette.Filter[mFilters.size()]));
+
+                // If created a new bitmap, recycle it
+                if (bitmap != mBitmap) {
+                    bitmap.recycle();
+                }
+
+                swatches = quantizer.getQuantizedColors();
+
+                if (logger != null) {
+                    logger.addSplit("Color quantization completed");
+                }
+            } else {
+                // Else we're using the provided swatches
+                swatches = mSwatches;
+            }
+
+            // Now create a Palette instance
+            final Palette p = new Palette(swatches, mTargets);
+            // And make it generate itself
+            p.generate();
+
+            if (logger != null) {
+                logger.addSplit("Created Palette");
+                logger.dumpToLog();
+            }
+
+            return p;
+        }
+
+        /**
+         * Generate the {@link Palette} asynchronously. The provided listener's
+         * {@link Palette.PaletteAsyncListener#onGenerated} method will be called with the palette when
+         * generated.
+         */
+        @NonNull
+        public AsyncTask<Bitmap, Void, Palette> generate(final Palette.PaletteAsyncListener listener) {
+            if (listener == null) {
+                throw new IllegalArgumentException("listener can not be null");
+            }
+
+            return new AsyncTask<Bitmap, Void, Palette>() {
+                @Override
+                protected Palette doInBackground(Bitmap... params) {
+                    try {
+                        return generate();
+                    } catch (Exception e) {
+                        Log.e(LOG_TAG, "Exception thrown during async generate", e);
+                        return null;
+                    }
+                }
+
+                @Override
+                protected void onPostExecute(Palette colorExtractor) {
+                    listener.onGenerated(colorExtractor);
+                }
+            }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mBitmap);
+        }
+
+        private int[] getPixelsFromBitmap(Bitmap bitmap) {
+            final int bitmapWidth = bitmap.getWidth();
+            final int bitmapHeight = bitmap.getHeight();
+            final int[] pixels = new int[bitmapWidth * bitmapHeight];
+            bitmap.getPixels(pixels, 0, bitmapWidth, 0, 0, bitmapWidth, bitmapHeight);
+
+            if (mRegion == null) {
+                // If we don't have a region, return all of the pixels
+                return pixels;
+            } else {
+                // If we do have a region, lets create a subset array containing only the region's
+                // pixels
+                final int regionWidth = mRegion.width();
+                final int regionHeight = mRegion.height();
+                // pixels contains all of the pixels, so we need to iterate through each row and
+                // copy the regions pixels into a new smaller array
+                final int[] subsetPixels = new int[regionWidth * regionHeight];
+                for (int row = 0; row < regionHeight; row++) {
+                    System.arraycopy(pixels, ((row + mRegion.top) * bitmapWidth) + mRegion.left,
+                            subsetPixels, row * regionWidth, regionWidth);
+                }
+                return subsetPixels;
+            }
+        }
+
+        /**
+         * Scale the bitmap down as needed.
+         */
+        private Bitmap scaleBitmapDown(final Bitmap bitmap) {
+            double scaleRatio = -1;
+
+            if (mResizeArea > 0) {
+                final int bitmapArea = bitmap.getWidth() * bitmap.getHeight();
+                if (bitmapArea > mResizeArea) {
+                    scaleRatio = Math.sqrt(mResizeArea / (double) bitmapArea);
+                }
+            } else if (mResizeMaxDimension > 0) {
+                final int maxDimension = Math.max(bitmap.getWidth(), bitmap.getHeight());
+                if (maxDimension > mResizeMaxDimension) {
+                    scaleRatio = mResizeMaxDimension / (double) maxDimension;
+                }
+            }
+
+            if (scaleRatio <= 0) {
+                // Scaling has been disabled or not needed so just return the Bitmap
+                return bitmap;
+            }
+
+            return Bitmap.createScaledBitmap(bitmap,
+                    (int) Math.ceil(bitmap.getWidth() * scaleRatio),
+                    (int) Math.ceil(bitmap.getHeight() * scaleRatio),
+                    false);
+        }
+    }
+
+    /**
+     * A Filter provides a mechanism for exercising fine-grained control over which colors
+     * are valid within a resulting {@link Palette}.
+     */
+    public interface Filter {
+        /**
+         * Hook to allow clients to be able filter colors from resulting palette.
+         *
+         * @param rgb the color in RGB888.
+         * @param hsl HSL representation of the color.
+         *
+         * @return true if the color is allowed, false if not.
+         *
+         * @see Palette.Builder#addFilter(Palette.Filter)
+         */
+        boolean isAllowed(int rgb, float[] hsl);
+    }
+
+    /**
+     * The default filter.
+     */
+    static final Palette.Filter
+            DEFAULT_FILTER = new Palette.Filter() {
+        private static final float BLACK_MAX_LIGHTNESS = 0.05f;
+        private static final float WHITE_MIN_LIGHTNESS = 0.95f;
+
+        @Override
+        public boolean isAllowed(int rgb, float[] hsl) {
+            return !isWhite(hsl) && !isBlack(hsl) && !isNearRedILine(hsl);
+        }
+
+        /**
+         * @return true if the color represents a color which is close to black.
+         */
+        private boolean isBlack(float[] hslColor) {
+            return hslColor[2] <= BLACK_MAX_LIGHTNESS;
+        }
+
+        /**
+         * @return true if the color represents a color which is close to white.
+         */
+        private boolean isWhite(float[] hslColor) {
+            return hslColor[2] >= WHITE_MIN_LIGHTNESS;
+        }
+
+        /**
+         * @return true if the color lies close to the red side of the I line.
+         */
+        private boolean isNearRedILine(float[] hslColor) {
+            return hslColor[0] >= 10f && hslColor[0] <= 37f && hslColor[1] <= 0.82f;
+        }
+    };
+}
diff --git a/core/java/com/android/internal/graphics/palette/Target.java b/core/java/com/android/internal/graphics/palette/Target.java
new file mode 100644
index 0000000..0540d80
--- /dev/null
+++ b/core/java/com/android/internal/graphics/palette/Target.java
@@ -0,0 +1,435 @@
+/*
+ * Copyright (C) 2017 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.internal.graphics.palette;
+
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import android.annotation.FloatRange;
+
+/**
+ * Copied from: frameworks/support/v7/palette/src/main/java/android/support/v7/graphics/Target.java
+ *
+ * A class which allows custom selection of colors in a {@link Palette}'s generation. Instances
+ * can be created via the {@link android.support.v7.graphics.Target.Builder} class.
+ *
+ * <p>To use the target, use the {@link Palette.Builder#addTarget(Target)} API when building a
+ * Palette.</p>
+ */
+public final class Target {
+
+    private static final float TARGET_DARK_LUMA = 0.26f;
+    private static final float MAX_DARK_LUMA = 0.45f;
+
+    private static final float MIN_LIGHT_LUMA = 0.55f;
+    private static final float TARGET_LIGHT_LUMA = 0.74f;
+
+    private static final float MIN_NORMAL_LUMA = 0.3f;
+    private static final float TARGET_NORMAL_LUMA = 0.5f;
+    private static final float MAX_NORMAL_LUMA = 0.7f;
+
+    private static final float TARGET_MUTED_SATURATION = 0.3f;
+    private static final float MAX_MUTED_SATURATION = 0.4f;
+
+    private static final float TARGET_VIBRANT_SATURATION = 1f;
+    private static final float MIN_VIBRANT_SATURATION = 0.35f;
+
+    private static final float WEIGHT_SATURATION = 0.24f;
+    private static final float WEIGHT_LUMA = 0.52f;
+    private static final float WEIGHT_POPULATION = 0.24f;
+
+    static final int INDEX_MIN = 0;
+    static final int INDEX_TARGET = 1;
+    static final int INDEX_MAX = 2;
+
+    static final int INDEX_WEIGHT_SAT = 0;
+    static final int INDEX_WEIGHT_LUMA = 1;
+    static final int INDEX_WEIGHT_POP = 2;
+
+    /**
+     * A target which has the characteristics of a vibrant color which is light in luminance.
+     */
+    public static final Target LIGHT_VIBRANT;
+
+    /**
+     * A target which has the characteristics of a vibrant color which is neither light or dark.
+     */
+    public static final Target VIBRANT;
+
+    /**
+     * A target which has the characteristics of a vibrant color which is dark in luminance.
+     */
+    public static final Target DARK_VIBRANT;
+
+    /**
+     * A target which has the characteristics of a muted color which is light in luminance.
+     */
+    public static final Target LIGHT_MUTED;
+
+    /**
+     * A target which has the characteristics of a muted color which is neither light or dark.
+     */
+    public static final Target MUTED;
+
+    /**
+     * A target which has the characteristics of a muted color which is dark in luminance.
+     */
+    public static final Target DARK_MUTED;
+
+    static {
+        LIGHT_VIBRANT = new Target();
+        setDefaultLightLightnessValues(LIGHT_VIBRANT);
+        setDefaultVibrantSaturationValues(LIGHT_VIBRANT);
+
+        VIBRANT = new Target();
+        setDefaultNormalLightnessValues(VIBRANT);
+        setDefaultVibrantSaturationValues(VIBRANT);
+
+        DARK_VIBRANT = new Target();
+        setDefaultDarkLightnessValues(DARK_VIBRANT);
+        setDefaultVibrantSaturationValues(DARK_VIBRANT);
+
+        LIGHT_MUTED = new Target();
+        setDefaultLightLightnessValues(LIGHT_MUTED);
+        setDefaultMutedSaturationValues(LIGHT_MUTED);
+
+        MUTED = new Target();
+        setDefaultNormalLightnessValues(MUTED);
+        setDefaultMutedSaturationValues(MUTED);
+
+        DARK_MUTED = new Target();
+        setDefaultDarkLightnessValues(DARK_MUTED);
+        setDefaultMutedSaturationValues(DARK_MUTED);
+    }
+
+    final float[] mSaturationTargets = new float[3];
+    final float[] mLightnessTargets = new float[3];
+    final float[] mWeights = new float[3];
+    boolean mIsExclusive = true; // default to true
+
+    Target() {
+        setTargetDefaultValues(mSaturationTargets);
+        setTargetDefaultValues(mLightnessTargets);
+        setDefaultWeights();
+    }
+
+    Target(Target from) {
+        System.arraycopy(from.mSaturationTargets, 0, mSaturationTargets, 0,
+                mSaturationTargets.length);
+        System.arraycopy(from.mLightnessTargets, 0, mLightnessTargets, 0,
+                mLightnessTargets.length);
+        System.arraycopy(from.mWeights, 0, mWeights, 0, mWeights.length);
+    }
+
+    /**
+     * The minimum saturation value for this target.
+     */
+    @FloatRange(from = 0, to = 1)
+    public float getMinimumSaturation() {
+        return mSaturationTargets[INDEX_MIN];
+    }
+
+    /**
+     * The target saturation value for this target.
+     */
+    @FloatRange(from = 0, to = 1)
+    public float getTargetSaturation() {
+        return mSaturationTargets[INDEX_TARGET];
+    }
+
+    /**
+     * The maximum saturation value for this target.
+     */
+    @FloatRange(from = 0, to = 1)
+    public float getMaximumSaturation() {
+        return mSaturationTargets[INDEX_MAX];
+    }
+
+    /**
+     * The minimum lightness value for this target.
+     */
+    @FloatRange(from = 0, to = 1)
+    public float getMinimumLightness() {
+        return mLightnessTargets[INDEX_MIN];
+    }
+
+    /**
+     * The target lightness value for this target.
+     */
+    @FloatRange(from = 0, to = 1)
+    public float getTargetLightness() {
+        return mLightnessTargets[INDEX_TARGET];
+    }
+
+    /**
+     * The maximum lightness value for this target.
+     */
+    @FloatRange(from = 0, to = 1)
+    public float getMaximumLightness() {
+        return mLightnessTargets[INDEX_MAX];
+    }
+
+    /**
+     * Returns the weight of importance that this target places on a color's saturation within
+     * the image.
+     *
+     * <p>The larger the weight, relative to the other weights, the more important that a color
+     * being close to the target value has on selection.</p>
+     *
+     * @see #getTargetSaturation()
+     */
+    public float getSaturationWeight() {
+        return mWeights[INDEX_WEIGHT_SAT];
+    }
+
+    /**
+     * Returns the weight of importance that this target places on a color's lightness within
+     * the image.
+     *
+     * <p>The larger the weight, relative to the other weights, the more important that a color
+     * being close to the target value has on selection.</p>
+     *
+     * @see #getTargetLightness()
+     */
+    public float getLightnessWeight() {
+        return mWeights[INDEX_WEIGHT_LUMA];
+    }
+
+    /**
+     * Returns the weight of importance that this target places on a color's population within
+     * the image.
+     *
+     * <p>The larger the weight, relative to the other weights, the more important that a
+     * color's population being close to the most populous has on selection.</p>
+     */
+    public float getPopulationWeight() {
+        return mWeights[INDEX_WEIGHT_POP];
+    }
+
+    /**
+     * Returns whether any color selected for this target is exclusive for this target only.
+     *
+     * <p>If false, then the color can be selected for other targets.</p>
+     */
+    public boolean isExclusive() {
+        return mIsExclusive;
+    }
+
+    private static void setTargetDefaultValues(final float[] values) {
+        values[INDEX_MIN] = 0f;
+        values[INDEX_TARGET] = 0.5f;
+        values[INDEX_MAX] = 1f;
+    }
+
+    private void setDefaultWeights() {
+        mWeights[INDEX_WEIGHT_SAT] = WEIGHT_SATURATION;
+        mWeights[INDEX_WEIGHT_LUMA] = WEIGHT_LUMA;
+        mWeights[INDEX_WEIGHT_POP] = WEIGHT_POPULATION;
+    }
+
+    void normalizeWeights() {
+        float sum = 0;
+        for (int i = 0, z = mWeights.length; i < z; i++) {
+            float weight = mWeights[i];
+            if (weight > 0) {
+                sum += weight;
+            }
+        }
+        if (sum != 0) {
+            for (int i = 0, z = mWeights.length; i < z; i++) {
+                if (mWeights[i] > 0) {
+                    mWeights[i] /= sum;
+                }
+            }
+        }
+    }
+
+    private static void setDefaultDarkLightnessValues(Target target) {
+        target.mLightnessTargets[INDEX_TARGET] = TARGET_DARK_LUMA;
+        target.mLightnessTargets[INDEX_MAX] = MAX_DARK_LUMA;
+    }
+
+    private static void setDefaultNormalLightnessValues(Target target) {
+        target.mLightnessTargets[INDEX_MIN] = MIN_NORMAL_LUMA;
+        target.mLightnessTargets[INDEX_TARGET] = TARGET_NORMAL_LUMA;
+        target.mLightnessTargets[INDEX_MAX] = MAX_NORMAL_LUMA;
+    }
+
+    private static void setDefaultLightLightnessValues(Target target) {
+        target.mLightnessTargets[INDEX_MIN] = MIN_LIGHT_LUMA;
+        target.mLightnessTargets[INDEX_TARGET] = TARGET_LIGHT_LUMA;
+    }
+
+    private static void setDefaultVibrantSaturationValues(Target target) {
+        target.mSaturationTargets[INDEX_MIN] = MIN_VIBRANT_SATURATION;
+        target.mSaturationTargets[INDEX_TARGET] = TARGET_VIBRANT_SATURATION;
+    }
+
+    private static void setDefaultMutedSaturationValues(Target target) {
+        target.mSaturationTargets[INDEX_TARGET] = TARGET_MUTED_SATURATION;
+        target.mSaturationTargets[INDEX_MAX] = MAX_MUTED_SATURATION;
+    }
+
+    /**
+     * Builder class for generating custom {@link Target} instances.
+     */
+    public final static class Builder {
+        private final Target mTarget;
+
+        /**
+         * Create a new {@link Target} builder from scratch.
+         */
+        public Builder() {
+            mTarget = new Target();
+        }
+
+        /**
+         * Create a new builder based on an existing {@link Target}.
+         */
+        public Builder(Target target) {
+            mTarget = new Target(target);
+        }
+
+        /**
+         * Set the minimum saturation value for this target.
+         */
+        public Target.Builder setMinimumSaturation(@FloatRange(from = 0, to = 1) float value) {
+            mTarget.mSaturationTargets[INDEX_MIN] = value;
+            return this;
+        }
+
+        /**
+         * Set the target/ideal saturation value for this target.
+         */
+        public Target.Builder setTargetSaturation(@FloatRange(from = 0, to = 1) float value) {
+            mTarget.mSaturationTargets[INDEX_TARGET] = value;
+            return this;
+        }
+
+        /**
+         * Set the maximum saturation value for this target.
+         */
+        public Target.Builder setMaximumSaturation(@FloatRange(from = 0, to = 1) float value) {
+            mTarget.mSaturationTargets[INDEX_MAX] = value;
+            return this;
+        }
+
+        /**
+         * Set the minimum lightness value for this target.
+         */
+        public Target.Builder setMinimumLightness(@FloatRange(from = 0, to = 1) float value) {
+            mTarget.mLightnessTargets[INDEX_MIN] = value;
+            return this;
+        }
+
+        /**
+         * Set the target/ideal lightness value for this target.
+         */
+        public Target.Builder setTargetLightness(@FloatRange(from = 0, to = 1) float value) {
+            mTarget.mLightnessTargets[INDEX_TARGET] = value;
+            return this;
+        }
+
+        /**
+         * Set the maximum lightness value for this target.
+         */
+        public Target.Builder setMaximumLightness(@FloatRange(from = 0, to = 1) float value) {
+            mTarget.mLightnessTargets[INDEX_MAX] = value;
+            return this;
+        }
+
+        /**
+         * Set the weight of importance that this target will place on saturation values.
+         *
+         * <p>The larger the weight, relative to the other weights, the more important that a color
+         * being close to the target value has on selection.</p>
+         *
+         * <p>A weight of 0 means that it has no weight, and thus has no
+         * bearing on the selection.</p>
+         *
+         * @see #setTargetSaturation(float)
+         */
+        public Target.Builder setSaturationWeight(@FloatRange(from = 0) float weight) {
+            mTarget.mWeights[INDEX_WEIGHT_SAT] = weight;
+            return this;
+        }
+
+        /**
+         * Set the weight of importance that this target will place on lightness values.
+         *
+         * <p>The larger the weight, relative to the other weights, the more important that a color
+         * being close to the target value has on selection.</p>
+         *
+         * <p>A weight of 0 means that it has no weight, and thus has no
+         * bearing on the selection.</p>
+         *
+         * @see #setTargetLightness(float)
+         */
+        public Target.Builder setLightnessWeight(@FloatRange(from = 0) float weight) {
+            mTarget.mWeights[INDEX_WEIGHT_LUMA] = weight;
+            return this;
+        }
+
+        /**
+         * Set the weight of importance that this target will place on a color's population within
+         * the image.
+         *
+         * <p>The larger the weight, relative to the other weights, the more important that a
+         * color's population being close to the most populous has on selection.</p>
+         *
+         * <p>A weight of 0 means that it has no weight, and thus has no
+         * bearing on the selection.</p>
+         */
+        public Target.Builder setPopulationWeight(@FloatRange(from = 0) float weight) {
+            mTarget.mWeights[INDEX_WEIGHT_POP] = weight;
+            return this;
+        }
+
+        /**
+         * Set whether any color selected for this target is exclusive to this target only.
+         * Defaults to true.
+         *
+         * @param exclusive true if any the color is exclusive to this target, or false is the
+         *                  color can be selected for other targets.
+         */
+        public Target.Builder setExclusive(boolean exclusive) {
+            mTarget.mIsExclusive = exclusive;
+            return this;
+        }
+
+        /**
+         * Builds and returns the resulting {@link Target}.
+         */
+        public Target build() {
+            return mTarget;
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java
index cc3f58c..e28079f 100644
--- a/core/java/com/android/internal/os/WebViewZygoteInit.java
+++ b/core/java/com/android/internal/os/WebViewZygoteInit.java
@@ -70,6 +70,7 @@
         @Override
         protected boolean handlePreloadPackage(String packagePath, String libsPath,
                                                String cacheKey) {
+            Log.i(TAG, "Beginning package preload");
             // Ask ApplicationLoaders to create and cache a classloader for the WebView APK so that
             // our children will reuse the same classloader instead of creating their own.
             // This enables us to preload Java and native code in the webview zygote process and
@@ -97,6 +98,7 @@
                      IllegalAccessException | InvocationTargetException e) {
                 Log.e(TAG, "Exception while preloading package", e);
             }
+            Log.i(TAG, "Package preload done");
             return false;
         }
     }
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index baf6db9..80b6b08 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -82,6 +82,7 @@
 
 import static android.app.ActivityManager.StackId;
 import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -2182,19 +2183,22 @@
         final boolean wasAdjustedForStack = mElevationAdjustedForStack;
         // Do not use a shadow when we are in resizing mode (mBackdropFrameRenderer not null)
         // since the shadow is bound to the content size and not the target size.
-        if (StackId.hasWindowShadow(mStackId) && !isResizing()) {
+        if ((mStackId == FREEFORM_WORKSPACE_STACK_ID) && !isResizing()) {
             elevation = hasWindowFocus() ?
                     DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP : DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
             // Add a maximum shadow height value to the top level view.
             // Note that pinned stack doesn't have focus
             // so maximum shadow height adjustment isn't needed.
             // TODO(skuhne): Remove this if clause once b/22668382 got fixed.
-            if (!mAllowUpdateElevation && mStackId != PINNED_STACK_ID) {
+            if (!mAllowUpdateElevation) {
                 elevation = DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
             }
             // Convert the DP elevation into physical pixels.
             elevation = dipToPx(elevation);
             mElevationAdjustedForStack = true;
+        } else if (mStackId == PINNED_STACK_ID) {
+            elevation = dipToPx(DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP);
+            mElevationAdjustedForStack = true;
         } else {
             mElevationAdjustedForStack = false;
         }
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index 818cc2c..8c71cf7 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -233,6 +233,7 @@
 
     private void doShow() {
         List<MenuItem> menuItems = getVisibleAndEnabledMenuItems(mMenu);
+        tidy(menuItems);
         if (!isCurrentlyShowing(menuItems) || mWidthChanged) {
             mPopup.dismiss();
             mPopup.layoutMenuItems(menuItems, mMenuItemClickListener, mSuggestedWidth);
@@ -274,6 +275,36 @@
         return menuItems;
     }
 
+    /**
+     * Update the list of menu items to conform to certain requirements.
+     */
+    private void tidy(List<MenuItem> menuItems) {
+        int assistItemIndex = -1;
+        Drawable assistItemDrawable = null;
+
+        final int size = menuItems.size();
+        for (int i = 0; i < size; i++) {
+            final MenuItem menuItem = menuItems.get(i);
+
+            if (menuItem.getItemId() == android.R.id.textAssist) {
+                assistItemIndex = i;
+                assistItemDrawable = menuItem.getIcon();
+            }
+
+            // Remove icons for all menu items with text.
+            if (!TextUtils.isEmpty(menuItem.getTitle())) {
+                menuItem.setIcon(null);
+            }
+        }
+        if (assistItemIndex > -1) {
+            final MenuItem assistMenuItem = menuItems.remove(assistItemIndex);
+            // Ensure the assist menu item preserves its icon.
+            assistMenuItem.setIcon(assistItemDrawable);
+            // Ensure the assist menu item is always the first item.
+            menuItems.add(0, assistMenuItem);
+        }
+    }
+
     private List<Object> getShowingMenuItemsReferences(List<MenuItem> menuItems) {
         List<Object> references = new ArrayList<Object>();
         for (MenuItem menuItem : menuItems) {
diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
index 4a9a2c5..c1f443f 100644
--- a/core/java/com/android/server/BootReceiver.java
+++ b/core/java/com/android/server/BootReceiver.java
@@ -35,6 +35,7 @@
 import android.util.Slog;
 import android.util.Xml;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
@@ -89,6 +90,11 @@
 
     // example: fs_stat,/dev/block/platform/soc/by-name/userdata,0x5
     private static final String FS_STAT_PATTERN = "fs_stat,[^,]*/([^/,]+),(0x[0-9a-fA-F]+)";
+    private static final int FS_STAT_FS_FIXED = 0x400; // should match with fs_mgr.cpp:FsStatFlags
+    private static final String FSCK_PASS_PATTERN = "Pass ([1-9]E?):";
+    private static final String FSCK_TREE_OPTIMIZATION_PATTERN =
+            "Inode [0-9]+ extent tree.*could be shorter";
+    private static final String FSCK_FS_MODIFIED = "FILE SYSTEM WAS MODIFIED";
     // ro.boottime.init.mount_all. + postfix for mount_all duration
     private static final String[] MOUNT_DURATION_PROPS_POSTFIX =
             new String[] { "early", "default", "late" };
@@ -334,17 +340,22 @@
 
         String log = FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n");
         Pattern pattern = Pattern.compile(FS_STAT_PATTERN);
-        for (String line : log.split("\n")) { // should check all lines
-            if (line.contains("FILE SYSTEM WAS MODIFIED")) {
+        String lines[] = log.split("\n");
+        int lineNumber = 0;
+        int lastFsStatLineNumber = 0;
+        for (String line : lines) { // should check all lines
+            if (line.contains(FSCK_FS_MODIFIED)) {
                 uploadNeeded = true;
             } else if (line.contains("fs_stat")){
                 Matcher matcher = pattern.matcher(line);
                 if (matcher.find()) {
-                    handleFsckFsStat(matcher);
+                    handleFsckFsStat(matcher, lines, lastFsStatLineNumber, lineNumber);
+                    lastFsStatLineNumber = lineNumber;
                 } else {
                     Slog.w(TAG, "cannot parse fs_stat:" + line);
                 }
             }
+            lineNumber++;
         }
 
         if (uploadEnabled && uploadNeeded ) {
@@ -403,7 +414,88 @@
         }
     }
 
-    private static void handleFsckFsStat(Matcher match) {
+    /**
+     * Fix fs_stat from e2fsck.
+     * For now, only handle the case of quota warning caused by tree optimization. Clear fs fix
+     * flag (=0x400) caused by that.
+     *
+     * @param partition partition name
+     * @param statOrg original stat reported from e2fsck log
+     * @param lines e2fsck logs broken down into lines
+     * @param startLineNumber start line to parse
+     * @param endLineNumber end line. exclusive.
+     * @return updated fs_stat. For tree optimization, will clear bit 0x400.
+     */
+    @VisibleForTesting
+    public static int fixFsckFsStat(String partition, int statOrg, String[] lines,
+            int startLineNumber, int endLineNumber) {
+        int stat = statOrg;
+        if ((stat & FS_STAT_FS_FIXED) != 0) {
+            // fs was fixed. should check if quota warning was caused by tree optimization.
+            // This is not a real fix but optimization, so should not be counted as a fs fix.
+            Pattern passPattern = Pattern.compile(FSCK_PASS_PATTERN);
+            Pattern treeOptPattern = Pattern.compile(FSCK_TREE_OPTIMIZATION_PATTERN);
+            String currentPass = "";
+            boolean foundTreeOptimization = false;
+            boolean foundQuotaFix = false;
+            boolean foundOtherFix = false;
+            String otherFixLine = null;
+            for (int i = startLineNumber; i < endLineNumber; i++) {
+                String line = lines[i];
+                if (line.contains(FSCK_FS_MODIFIED)) { // no need to parse above this
+                    break;
+                } else if (line.startsWith("Pass ")) {
+                    Matcher matcher = passPattern.matcher(line);
+                    if (matcher.find()) {
+                        currentPass = matcher.group(1);
+                    }
+                } else if (line.startsWith("Inode ")) {
+                    Matcher matcher = treeOptPattern.matcher(line);
+                    if (matcher.find() && currentPass.equals("1")) {
+                        foundTreeOptimization = true;
+                        Slog.i(TAG, "fs_stat, partition:" + partition + " found tree optimization:"
+                                + line);
+                    } else {
+                        foundOtherFix = true;
+                        otherFixLine = line;
+                        break;
+                    }
+                } else if (line.startsWith("[QUOTA WARNING]") && currentPass.equals("5")) {
+                    Slog.i(TAG, "fs_stat, partition:" + partition + " found quota warning:"
+                            + line);
+                    foundQuotaFix = true;
+                    if (!foundTreeOptimization) { // only quota warning, this is real fix.
+                        otherFixLine = line;
+                        break;
+                    }
+                } else if (line.startsWith("Update quota info") && currentPass.equals("5")) {
+                    // follows "[QUOTA WARNING]", ignore
+                } else {
+                    line = line.trim();
+                    // ignore empty msg or any msg before Pass 1
+                    if (!line.isEmpty() && !currentPass.isEmpty()) {
+                        foundOtherFix = true;
+                        otherFixLine = line;
+                        break;
+                    }
+                }
+            }
+            if (!foundOtherFix && foundTreeOptimization && foundQuotaFix) {
+                // not a real fix, so clear it.
+                Slog.i(TAG, "fs_stat, partition:" + partition +
+                        " quota fix due to tree optimization");
+                stat &= ~FS_STAT_FS_FIXED;
+            } else {
+                if (otherFixLine != null) {
+                    Slog.i(TAG, "fs_stat, partition:" + partition + " fix:" + otherFixLine);
+                }
+            }
+        }
+        return stat;
+    }
+
+    private static void handleFsckFsStat(Matcher match, String[] lines, int startLineNumber,
+            int endLineNumber) {
         String partition = match.group(1);
         int stat;
         try {
@@ -412,9 +504,9 @@
             Slog.w(TAG, "cannot parse fs_stat: partition:" + partition + " stat:" + match.group(2));
             return;
         }
-
+        stat = fixFsckFsStat(partition, stat, lines, startLineNumber, endLineNumber);
         MetricsLogger.histogram(null, "boot_fs_stat_" + partition, stat);
-        Slog.i(TAG, "fs_stat, partition:" + partition + " stat:" + match.group(2));
+        Slog.i(TAG, "fs_stat, partition:" + partition + " stat:0x" + Integer.toHexString(stat));
     }
 
     private static HashMap<String, Long> readTimestamps() {
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 1aed501..de67c50 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -193,10 +193,10 @@
     if (env->IsInstanceOf(excep, gErrorOffsets.mClass)) {
         /*
          * It's an Error: Reraise the exception and ask the runtime to abort.
-         * This will dump the pending exception as well as all thread traces
-         * to the log.
          */
         env->Throw(excep);
+        ALOGE("java.lang.Error thrown during binder transaction (stack trace follows) : ");
+        env->ExceptionDescribe();
         env->FatalError("java.lang.Error thrown during binder transaction.");
     }
 
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index a2f07d9..2042bf62 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -21,10 +21,14 @@
 
 import "frameworks/base/libs/incident/proto/android/privacy.proto";
 import "frameworks/base/core/proto/android/service/appwidget.proto";
+import "frameworks/base/core/proto/android/service/battery.proto";
 import "frameworks/base/core/proto/android/service/graphicsstats.proto";
 import "frameworks/base/core/proto/android/service/fingerprint.proto";
+import "frameworks/base/core/proto/android/service/diskstats.proto";
 import "frameworks/base/core/proto/android/service/netstats.proto";
 import "frameworks/base/core/proto/android/service/notification.proto";
+import "frameworks/base/core/proto/android/service/package.proto";
+import "frameworks/base/core/proto/android/service/power.proto";
 import "frameworks/base/core/proto/android/providers/settings.proto";
 
 package android.os;
@@ -57,6 +61,10 @@
     android.service.NetworkStatsServiceDumpProto netstats = 3001;
     android.providers.settings.SettingsServiceDumpProto settings = 3002;
     android.service.appwidget.AppWidgetServiceDumpProto appwidget = 3003;
+    android.service.battery.BatteryServiceDumpProto battery = 3006;
+    android.service.diskstats.DiskStatsServiceDumpProto diskstats = 3007;
     android.service.notification.NotificationServiceDumpProto notification = 3004;
+    android.service.pm.PackageServiceDumpProto package = 3008;
+    android.service.power.PowerServiceDumpProto power = 3009;
     android.service.GraphicsStatsServiceDumpProto graphicsstats = 3005;
 }
diff --git a/core/res/res/drawable/ic_corp_badge.xml b/core/res/res/drawable/ic_corp_badge.xml
deleted file mode 100644
index 8917431..0000000
--- a/core/res/res/drawable/ic_corp_badge.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="20.0dp"
-        android:height="20.0dp"
-        android:viewportWidth="20.0"
-        android:viewportHeight="20.0">
-    <path
-        android:pathData="M10.0,10.0m-10.0,0.0a10.0,10.0 0.0,1.0 1.0,20.0 0.0a10.0,10.0 0.0,1.0 1.0,-20.0 0.0"
-        android:fillColor="#FF5722"/>
-    <path
-        android:pathData="M15.2,6.2L4.8,6.2c-0.5,0.0 -0.9,0.4 -0.9,1.0L3.9,10.0c0.0,0.5 0.4,1.0 0.9,1.0l3.8,0.0l0.0,-1.0l2.9,0.0l0.0,1.0l3.8,0.0c0.5,0.0 1.0,-0.4 1.0,-1.0L16.3,7.1C16.2,6.6 15.8,6.2 15.2,6.2z"
-        android:fillColor="#FFFFFF"/>
-    <path
-        android:pathData="M8.6,12.9l0.0,-1.0L4.3,11.9l0.0,2.4c0.0,0.5 0.4,0.9 0.9,0.9l9.5,0.0c0.5,0.0 0.9,-0.4 0.9,-0.9l0.0,-2.4l-4.3,0.0l0.0,1.0L8.6,12.9z"
-        android:fillColor="#FFFFFF"/>
-    <path
-        android:pathData="M7.1,5.2l0.0,1.0 1.0,0.0 0.0,-1.0 3.799999,0.0 0.0,1.0 1.0,0.0 0.0,-1.0 -1.0,-0.9 -3.799999,0.0z"
-        android:fillColor="#FFFFFF"/>
-</vector>
diff --git a/core/res/res/drawable/ic_instant_icon_badge_bolt.xml b/core/res/res/drawable/ic_instant_icon_badge_bolt.xml
new file mode 100644
index 0000000..08512b7
--- /dev/null
+++ b/core/res/res/drawable/ic_instant_icon_badge_bolt.xml
@@ -0,0 +1,29 @@
+<!--
+Copyright (C) 2017 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="64.0dp"
+        android:height="64.0dp"
+        android:viewportWidth="64.0"
+        android:viewportHeight="64.0">
+
+    <!--
+     The path is similar to ic_corp_icon_badge_case, such that it positions on top of
+     ic_corp_icon_badge_shadow.
+    -->
+    <path
+        android:pathData="M43.9 50.9h4v8l6-12h-4v-8l-6 12z"
+        android:fillColor="#757575"/>
+</vector>
diff --git a/core/res/res/drawable/ic_picture_in_picture.xml b/core/res/res/drawable/ic_picture_in_picture.xml
new file mode 100644
index 0000000..e2dda33
--- /dev/null
+++ b/core/res/res/drawable/ic_picture_in_picture.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M19 11h-8v6h8v-6zm4 8V4.98C23 3.88 22.1 3 21 3H3c-1.1 0-2 .88-2 1.98V19c0 1.1 .9
+2 2 2h18c1.1 0 2-.9 2-2zm-2 .02H3V4.97h18v14.05z" />
+    <path
+        android:pathData="M0 0h24v24H0V0z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/layout/accessibility_button_chooser.xml b/core/res/res/layout/accessibility_button_chooser.xml
index 0ef785f..480defb 100644
--- a/core/res/res/layout/accessibility_button_chooser.xml
+++ b/core/res/res/layout/accessibility_button_chooser.xml
@@ -19,7 +19,7 @@
 <com.android.internal.widget.ResolverDrawerLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"
+    android:layout_height="wrap_content"
     android:maxWidth="@dimen/resolver_max_width"
     android:maxCollapsedHeight="256dp"
     android:maxCollapsedHeightSmall="56dp"
@@ -27,11 +27,14 @@
 
     <LinearLayout
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alwaysShow="true"
         android:orientation="vertical"
         android:background="?attr/colorBackground"
         android:paddingTop="8dp"
-        android:paddingBottom="8dp">
+        android:paddingBottom="8dp"
+        android:paddingStart="?attr/dialogPreferredPadding"
+        android:paddingEnd="?attr/dialogPreferredPadding">
 
         <TextView
             android:layout_width="match_parent"
@@ -41,8 +44,6 @@
             android:text="@string/accessibility_button_prompt_text"
             android:gravity="start|center_vertical"
             android:layout_alignParentStart="true"
-            android:paddingStart="?attr/dialogPreferredPadding"
-            android:paddingEnd="?attr/dialogPreferredPadding"
             android:paddingTop="8dp"
             android:paddingBottom="8dp"/>
 
@@ -55,20 +56,15 @@
             android:verticalSpacing="10dp"
             android:horizontalSpacing="10dp"
             android:stretchMode="columnWidth"
-            android:paddingStart="?attr/dialogPreferredPadding"
-            android:paddingEnd="?attr/dialogPreferredPadding"
             android:gravity="center"/>
 
         <TextView
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:id="@+id/accessibility_button_prompt"
-            android:layout_alwaysShow="true"
             android:textAppearance="?attr/textAppearanceMedium"
             android:text="@string/accessibility_button_instructional_text"
             android:gravity="start|center_vertical"
-            android:paddingStart="?attr/dialogPreferredPadding"
-            android:paddingEnd="?attr/dialogPreferredPadding"
             android:paddingTop="8dp"
             android:paddingBottom="8dp"
             android:visibility="gone"/>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 937fc6f..536906c 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -171,6 +171,9 @@
     <color name="profile_badge_2">#ff000000</color><!-- Black -->
     <color name="profile_badge_3">#ff22f033</color><!-- Green -->
 
+    <!-- Default instant app badge color -->
+    <color name="instant_app_badge">#FFFFFFFF</color><!-- White -->
+
     <!-- Multi-sim sim colors -->
     <color name="Teal_700">#ff00796b</color>
     <color name="Teal_800">#ff00695c</color>
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index e0cc5b5..8c37d4b 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -74,10 +74,10 @@
 
     <item name="disabled_alpha_material_light" format="float" type="dimen">0.26</item>
     <item name="disabled_alpha_material_dark" format="float" type="dimen">0.30</item>
-    <item name="primary_content_alpha_material_light" format="float" type="dimen">1</item>
-    <item name="primary_content_alpha_material_dark" format="float" type="dimen">0.87</item>
-    <item name="secondary_content_alpha_material_light" format="float" type="dimen">.7</item>
-    <item name="secondary_content_alpha_material_dark" format="float" type="dimen">0.54</item>
+    <item name="primary_content_alpha_material_dark" format="float" type="dimen">1</item>
+    <item name="primary_content_alpha_material_light" format="float" type="dimen">0.87</item>
+    <item name="secondary_content_alpha_material_dark" format="float" type="dimen">.7</item>
+    <item name="secondary_content_alpha_material_light" format="float" type="dimen">0.54</item>
 
     <item name="highlight_alpha_material_light" format="float" type="dimen">0.12</item>
     <item name="highlight_alpha_material_dark" format="float" type="dimen">0.20</item>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 45dccb6..e85f560 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -852,6 +852,8 @@
             1 - Go to sleep (doze)
             2 - Really go to sleep (don't doze)
             3 - Really go to sleep and go home (don't doze)
+            4 - Go to home
+            5 - Dismiss IME if shown. Otherwise go to home
     -->
     <integer name="config_shortPressOnPowerBehavior">1</integer>
 
@@ -2794,7 +2796,7 @@
     <string name="config_defaultCellBroadcastReceiverPkg" translatable="false">com.android.cellbroadcastreceiver</string>
 
     <!-- Specifies the path that is used by AdaptiveIconDrawable class to crop launcher icons. -->
-    <string name="config_icon_mask" translatable="false">"M50,0L92,0 A8,8,0,0 1 100,8 L100,92 A8,8,0,0 1 92,100 L8,100 A8,8,0,0 1 0,92 L 0,8 A8,8,0,0 1 8,0z"</string>
+    <string name="config_icon_mask" translatable="false">"M50,0L92,0C96.42,0 100,4.58 100 8L100,92C100, 96.42 96.42 100 92 100L8 100C4.58, 100 0 96.42 0 92L0 8 C 0 4.42 4.42 0 8 0L50 0Z"</string>
 
     <!-- The component name, flattened to a string, for the default accessibility service to be
          enabled by the accessibility shortcut. This service must be trusted, as it can be activated
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 9e98efd..89c912fd 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2834,6 +2834,7 @@
 
     <public-group type="drawable" first-id="0x010800b4">
         <public name="autofilled_highlight" />
+        <public name="ic_picture_in_picture" />
     </public-group>
 
     <public-group type="string" first-id="0x01040019">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f2da845..907294d 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1322,6 +1322,7 @@
   <java-symbol type="drawable" name="ic_corp_user_badge" />
   <java-symbol type="drawable" name="ic_corp_badge_no_background" />
   <java-symbol type="drawable" name="ic_corp_statusbar_icon" />
+  <java-symbol type="drawable" name="ic_instant_icon_badge_bolt" />
   <java-symbol type="drawable" name="emulator_circular_window_overlay" />
 
   <java-symbol type="drawable" name="sim_light_blue" />
@@ -1353,6 +1354,7 @@
   <java-symbol type="color" name="profile_badge_1" />
   <java-symbol type="color" name="profile_badge_2" />
   <java-symbol type="color" name="profile_badge_3" />
+  <java-symbol type="color" name="instant_app_badge" />
 
   <java-symbol type="layout" name="action_bar_home" />
   <java-symbol type="layout" name="action_bar_title_item" />
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index e0b4ec5..d80ff1d 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -794,6 +794,8 @@
 
     <!-- Theme used for the intent picker activity. -->
     <style name="Theme.DeviceDefault.Resolver" parent="Theme.Material.Light">
+        <item name="windowEnterTransition">@empty</item>
+        <item name="windowExitTransition">@empty</item>
         <item name="windowIsTranslucent">true</item>
         <item name="windowNoTitle">true</item>
         <item name="windowBackground">@color/transparent</item>
diff --git a/core/tests/coretests/src/android/metrics/LogMakerTest.java b/core/tests/coretests/src/android/metrics/LogMakerTest.java
index bab9f63..63c1f87 100644
--- a/core/tests/coretests/src/android/metrics/LogMakerTest.java
+++ b/core/tests/coretests/src/android/metrics/LogMakerTest.java
@@ -263,4 +263,32 @@
         assertFalse(a.isSubsetOf(b));
         assertFalse(b.isSubsetOf(a));
     }
+
+    public void testConstructFromNull() {
+        new LogMaker(null);
+        // no promises, just don't throw
+    }
+
+    public void testConstructFromNullKey() {
+        Object[] items = new Object[2];
+        items[0] = null;
+        items[1] = "foo";
+        new LogMaker(items);
+        // no promises, just don't throw
+    }
+
+    public void testConstructFromNullField() {
+        Object[] items = new Object[2];
+        items[0] = 10;
+        items[1] = null;
+        new LogMaker(items);
+        // no promises, just don't throw
+    }
+
+    public void testConstructFromTruncatedArray() {
+        Object[] items = new Object[1];
+        items[0] = 10;
+        new LogMaker(items);
+        // no promises, just don't throw
+    }
 }
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
new file mode 100644
index 0000000..39b999d
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2017 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.view.textclassifier;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+
+import android.os.LocaleList;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.Locale;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TextClassificationManagerTest {
+
+    private static final LocaleList LOCALES = LocaleList.forLanguageTags("en");
+
+    private TextClassificationManager mTcm;
+    private TextClassifier mClassifier;
+
+    @Before
+    public void setup() {
+        mTcm = InstrumentationRegistry.getTargetContext()
+                .getSystemService(TextClassificationManager.class);
+        mTcm.setTextClassifier(null);
+        mClassifier = mTcm.getTextClassifier();
+    }
+
+    @Test
+    public void testSmartSelection() {
+        if (isTextClassifierDisabled()) return;
+
+        String text = "Contact me at droid@android.com";
+        String selected = "droid";
+        String suggested = "droid@android.com";
+        int startIndex = text.indexOf(selected);
+        int endIndex = startIndex + selected.length();
+        int smartStartIndex = text.indexOf(suggested);
+        int smartEndIndex = smartStartIndex + suggested.length();
+
+        assertThat(mClassifier.suggestSelection(text, startIndex, endIndex, LOCALES),
+                isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_EMAIL));
+    }
+
+    @Test
+    public void testSmartSelection_nullLocaleList() {
+        if (isTextClassifierDisabled()) return;
+
+        String text = "Contact me at droid@android.com";
+        String selected = "droid";
+        String suggested = "droid@android.com";
+        int startIndex = text.indexOf(selected);
+        int endIndex = startIndex + selected.length();
+        int smartStartIndex = text.indexOf(suggested);
+        int smartEndIndex = smartStartIndex + suggested.length();
+        LocaleList nullLocales = null;
+
+        assertThat(mClassifier.suggestSelection(text, startIndex, endIndex, nullLocales),
+                isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_EMAIL));
+    }
+
+    @Test
+    public void testSmartSelection_url() {
+        if (isTextClassifierDisabled()) return;
+
+        String text = "Visit http://www.android.com for more information";
+        String selected = "http";
+        String suggested = "http://www.android.com";
+        int startIndex = text.indexOf(selected);
+        int endIndex = startIndex + selected.length();
+        int smartStartIndex = text.indexOf(suggested);
+        int smartEndIndex = smartStartIndex + suggested.length();
+
+        assertThat(mClassifier.suggestSelection(text, startIndex, endIndex, LOCALES),
+                isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_URL));
+    }
+
+    @Test
+    public void testTextClassificationResult() {
+        if (isTextClassifierDisabled()) return;
+
+        String text = "Contact me at droid@android.com";
+        String classifiedText = "droid@android.com";
+        int startIndex = text.indexOf(classifiedText);
+        int endIndex = startIndex + classifiedText.length();
+        assertThat(mClassifier.getTextClassificationResult(text, startIndex, endIndex, LOCALES),
+                isTextClassificationResult(classifiedText, TextClassifier.TYPE_EMAIL));
+    }
+
+    @Test
+    public void testTextClassificationResult_url() {
+        if (isTextClassifierDisabled()) return;
+
+        String text = "Visit http://www.android.com for more information";
+        String classifiedText = "http://www.android.com";
+        int startIndex = text.indexOf(classifiedText);
+        int endIndex = startIndex + classifiedText.length();
+        assertThat(mClassifier.getTextClassificationResult(text, startIndex, endIndex, LOCALES),
+                isTextClassificationResult(classifiedText, TextClassifier.TYPE_URL));
+    }
+
+    @Test
+    public void testTextClassificationResult_nullLocaleList() {
+        if (isTextClassifierDisabled()) return;
+
+        String text = "Contact me at droid@android.com";
+        String classifiedText = "droid@android.com";
+        int startIndex = text.indexOf(classifiedText);
+        int endIndex = startIndex + classifiedText.length();
+        LocaleList nullLocales = null;
+        assertThat(mClassifier.getTextClassificationResult(text, startIndex, endIndex, nullLocales),
+                isTextClassificationResult(classifiedText, TextClassifier.TYPE_EMAIL));
+    }
+
+    @Test
+    public void testLanguageDetection() {
+        if (isTextClassifierDisabled()) return;
+
+        String text = "This is a piece of English text";
+        assertThat(mTcm.detectLanguages(text), isDetectedLanguage("en"));
+
+        text = "Das ist ein deutscher Text";
+        assertThat(mTcm.detectLanguages(text), isDetectedLanguage("de"));
+
+        text = "これは日本語のテキストです";
+        assertThat(mTcm.detectLanguages(text), isDetectedLanguage("ja"));
+    }
+
+    @Test
+    public void testSetTextClassifier() {
+        TextClassifier classifier = mock(TextClassifier.class);
+        mTcm.setTextClassifier(classifier);
+        assertEquals(classifier, mTcm.getTextClassifier());
+    }
+
+    private boolean isTextClassifierDisabled() {
+        return mClassifier == TextClassifier.NO_OP;
+    }
+
+    private static Matcher<TextSelection> isTextSelection(
+            final int startIndex, final int endIndex, final String type) {
+        return new BaseMatcher<TextSelection>() {
+            @Override
+            public boolean matches(Object o) {
+                if (o instanceof TextSelection) {
+                    TextSelection selection = (TextSelection) o;
+                    return startIndex == selection.getSelectionStartIndex()
+                            && endIndex == selection.getSelectionEndIndex()
+                            && selection.getEntityCount() > 0
+                            && type.equals(selection.getEntity(0));
+                }
+                return false;
+            }
+
+            @Override
+            public void describeTo(Description description) {
+                description.appendValue(
+                        String.format("%d, %d, %s", startIndex, endIndex, type));
+            }
+        };
+    }
+
+    private static Matcher<TextClassificationResult> isTextClassificationResult(
+            final String text, final String type) {
+        return new BaseMatcher<TextClassificationResult>() {
+            @Override
+            public boolean matches(Object o) {
+                if (o instanceof TextClassificationResult) {
+                    TextClassificationResult result = (TextClassificationResult) o;
+                    return text.equals(result.getText())
+                            && result.getEntityCount() > 0
+                            && type.equals(result.getEntity(0));
+                    // TODO: Include other properties.
+                }
+                return false;
+            }
+
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("text=").appendValue(text)
+                        .appendText(", type=").appendValue(type);
+            }
+        };
+    }
+
+    private static Matcher<List<TextLanguage>> isDetectedLanguage(final String language) {
+        return new BaseMatcher<List<TextLanguage>>() {
+            @Override
+            public boolean matches(Object o) {
+                if (o instanceof List) {
+                    List languages = (List) o;
+                    if (!languages.isEmpty()) {
+                        Object o1 = languages.get(0);
+                        if (o1 instanceof TextLanguage) {
+                            TextLanguage lang = (TextLanguage) o1;
+                            return lang.getLanguageCount() > 0
+                                    && new Locale(language).getLanguage()
+                                            .equals(lang.getLanguage(0).getLanguage());
+                        }
+                    }
+                }
+                return false;
+            }
+
+            @Override
+            public void describeTo(Description description) {
+                description.appendValue(String.format("%s", language));
+            }
+        };
+    }
+}
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 3029134..2203b6a 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -28,6 +28,7 @@
 import static android.widget.espresso.TextViewActions.longPressOnTextAtIndex;
 import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex;
 import static android.widget.espresso.TextViewAssertions.hasSelection;
+import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarItemIndex;
 import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsDisplayed;
 import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsNotDisplayed;
 import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarContainsItem;
@@ -46,6 +47,11 @@
 import static org.hamcrest.Matchers.anyOf;
 import static org.hamcrest.Matchers.is;
 
+import android.view.ActionMode;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.textclassifier.TextClassificationManager;
+import android.view.textclassifier.TextClassifier;
 import android.widget.espresso.CustomViewActions.RelativeCoordinatesProvider;
 
 import android.support.test.espresso.action.EspressoKey;
@@ -71,7 +77,8 @@
     @Override
     public void setUp() throws Exception {
         super.setUp();
-        getActivity();
+        getActivity().getSystemService(TextClassificationManager.class)
+                .setTextClassifier(TextClassifier.NO_OP);
     }
 
     public void testTypedTextIsOnScreen() throws Exception {
@@ -676,4 +683,38 @@
         // hasTransientState should return false when selection is created by API.
         assertFalse(textView.hasTransientState());
     }
+
+    public void testAssistItemIsAtIndexZero() throws Exception {
+        getActivity().getSystemService(TextClassificationManager.class).setTextClassifier(null);
+        final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
+        textView.post(() -> textView.setCustomSelectionActionModeCallback(
+                new ActionMode.Callback() {
+                    @Override
+                    public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
+                        // Create another item at order position 0 to confirm that it will never be
+                        // placed before the textAssist item.
+                        menu.add(Menu.NONE, 0 /* id */, 0 /* order */, "Test");
+                        return true;
+                    }
+
+                    @Override
+                    public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
+                        return true;
+                    }
+
+                    @Override
+                    public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
+                        return false;
+                    }
+
+                    @Override
+                    public void onDestroyActionMode(ActionMode actionMode) {}
+                }));
+        final String text = "droid@android.com";
+
+        onView(withId(R.id.textview)).perform(replaceText(text));
+        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('@')));
+        sleepForFloatingToolbarPopup();
+        assertFloatingToolbarItemIndex(android.R.id.textAssist, 0);
+    }
 }
diff --git a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
index 838f4db..5206c9b 100644
--- a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
+++ b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
@@ -29,7 +29,13 @@
 import static org.hamcrest.Matchers.allOf;
 import static org.hamcrest.Matchers.is;
 
+import android.view.MenuItem;
+import android.view.ViewGroup;
+import java.util.ArrayList;
+import java.util.List;
+import org.hamcrest.Description;
 import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
 
 import android.support.test.espresso.NoMatchingRootException;
 import android.support.test.espresso.NoMatchingViewException;
@@ -123,6 +129,39 @@
     }
 
     /**
+     * Asserts that the floating toolbar contains a specified item at a specified index.
+     *
+     * @param menuItemId id of the menu item
+     * @param index expected index of the menu item in the floating toolbar
+     * @throws AssertionError if the assertion fails
+     */
+    public static void assertFloatingToolbarItemIndex(final int menuItemId, final int index) {
+        onFloatingToolBar().check(matches(new TypeSafeMatcher<View>() {
+            private List<Integer> menuItemIds = new ArrayList<>();
+
+            @Override
+            public boolean matchesSafely(View view) {
+                collectMenuItemIds(view);
+                return menuItemIds.size() > index && menuItemIds.get(index) == menuItemId;
+            }
+
+            @Override
+            public void describeTo(Description description) {}
+
+            private void collectMenuItemIds(View view) {
+                if (view.getTag() instanceof MenuItem) {
+                    menuItemIds.add(((MenuItem) view.getTag()).getItemId());
+                } else if (view instanceof ViewGroup) {
+                    ViewGroup viewGroup = (ViewGroup) view;
+                    for (int i = 0; i < viewGroup.getChildCount(); i++) {
+                        collectMenuItemIds(viewGroup.getChildAt(i));
+                    }
+                }
+            }
+        }));
+    }
+
+    /**
      * Asserts that the floating toolbar doesn't contain the specified item.
      *
      * @param itemLabel label of the item.
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 7a8e487..abdab39 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -916,17 +916,20 @@
      * @param hasAlpha If the bitmap is ARGB_8888 or RGBA_16F this flag can be used to
      *                 mark the bitmap as opaque. Doing so will clear the bitmap in black
      *                 instead of transparent.
-     * @param colorSpace The color space of the bitmap. If null or if the config is not
-     *                   {@link Config#ARGB_8888}, {@link ColorSpace.Named#SRGB sRGB} is assumed.
+     * @param colorSpace The color space of the bitmap. If the config is {@link Config#RGBA_F16},
+     *                   {@link ColorSpace.Named#EXTENDED_SRGB scRGB} is assumed, and if the
+     *                   config is not {@link Config#ARGB_8888}, {@link ColorSpace.Named#SRGB sRGB}
+     *                   is assumed.
      *
      * @throws IllegalArgumentException if the width or height are <= 0, if
      *         Config is Config.HARDWARE (because hardware bitmaps are always
      *         immutable), if the specified color space is not {@link ColorSpace.Model#RGB RGB},
-     *         or if the specified color space's transfer function is not an
-     *         {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}
+     *         if the specified color space's transfer function is not an
+     *         {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}, or if
+     *         the color space is null
      */
     public static Bitmap createBitmap(int width, int height, @NonNull Config config,
-            boolean hasAlpha, @Nullable ColorSpace colorSpace) {
+            boolean hasAlpha, @NonNull ColorSpace colorSpace) {
         return createBitmap(null, width, height, config, hasAlpha, colorSpace);
     }
 
@@ -950,7 +953,8 @@
      */
     public static Bitmap createBitmap(@Nullable DisplayMetrics display, int width, int height,
             @NonNull Config config, boolean hasAlpha) {
-        return createBitmap(display, width, height, config, hasAlpha, null);
+        return createBitmap(display, width, height, config, hasAlpha,
+                ColorSpace.get(ColorSpace.Named.SRGB));
     }
 
     /**
@@ -967,26 +971,34 @@
      * @param hasAlpha If the bitmap is ARGB_8888 or RGBA_16F this flag can be used to
      *                 mark the bitmap as opaque. Doing so will clear the bitmap in black
      *                 instead of transparent.
-     * @param colorSpace The color space of the bitmap. If null or if the config is not
-     *                   {@link Config#ARGB_8888}, {@link ColorSpace.Named#SRGB sRGB} is assumed.
+     * @param colorSpace The color space of the bitmap. If the config is {@link Config#RGBA_F16},
+     *                   {@link ColorSpace.Named#EXTENDED_SRGB scRGB} is assumed, and if the
+     *                   config is not {@link Config#ARGB_8888}, {@link ColorSpace.Named#SRGB sRGB}
+     *                   is assumed.
      *
      * @throws IllegalArgumentException if the width or height are <= 0, if
      *         Config is Config.HARDWARE (because hardware bitmaps are always
      *         immutable), if the specified color space is not {@link ColorSpace.Model#RGB RGB},
-     *         or if the specified color space's transfer function is not an
-     *         {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}
+     *         if the specified color space's transfer function is not an
+     *         {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}, or if
+     *         the color space is null
      */
     public static Bitmap createBitmap(@Nullable DisplayMetrics display, int width, int height,
-            @NonNull Config config, boolean hasAlpha, @Nullable ColorSpace colorSpace) {
+            @NonNull Config config, boolean hasAlpha, @NonNull ColorSpace colorSpace) {
         if (width <= 0 || height <= 0) {
             throw new IllegalArgumentException("width and height must be > 0");
         }
         if (config == Config.HARDWARE) {
             throw new IllegalArgumentException("can't create mutable bitmap with Config.HARDWARE");
         }
+        if (colorSpace == null) {
+            throw new IllegalArgumentException("can't create bitmap without a color space");
+        }
 
         Bitmap bm;
-        if (colorSpace == null || config != Config.ARGB_8888) {
+        // nullptr color spaces have a particular meaning in native and are interpreted as sRGB
+        // (we also avoid the unnecessary extra work of the else branch)
+        if (config != Config.ARGB_8888 || colorSpace == ColorSpace.get(ColorSpace.Named.SRGB)) {
             bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true, null, null);
         } else {
             if (!(colorSpace instanceof ColorSpace.Rgb)) {
diff --git a/graphics/java/android/graphics/BitmapShader.java b/graphics/java/android/graphics/BitmapShader.java
index 7e6756e..5577f53 100644
--- a/graphics/java/android/graphics/BitmapShader.java
+++ b/graphics/java/android/graphics/BitmapShader.java
@@ -41,35 +41,16 @@
      * @param tileY The tiling mode for y to draw the bitmap in.
      */
     public BitmapShader(@NonNull Bitmap bitmap, @NonNull TileMode tileX, @NonNull TileMode tileY) {
-        set(bitmap, tileX, tileY);
+        this(bitmap, tileX.nativeInt, tileY.nativeInt);
     }
 
     private BitmapShader(Bitmap bitmap, int tileX, int tileY) {
-        setInternal(bitmap, tileX, tileY);
-    }
-
-    /**
-     * Reinitialize the BitmapShader's Bitmap and tile modes.
-     *
-     * @param bitmap The bitmap to use inside the shader
-     * @param tileX The tiling mode for x to draw the bitmap in.
-     * @param tileY The tiling mode for y to draw the bitmap in.
-     */
-    public void set(@NonNull Bitmap bitmap, @NonNull TileMode tileX, @NonNull TileMode tileY) {
-        if (tileX == null || tileY == null) {
-            throw new IllegalArgumentException();
-        }
-        setInternal(bitmap, tileX.nativeInt, tileY.nativeInt);
-    }
-
-    private void setInternal(Bitmap bitmap, int tileX, int tileY) {
         if (bitmap == null) {
             throw new IllegalArgumentException("Bitmap must be non-null");
         }
         if (bitmap == mBitmap && tileX == mTileX && tileY == mTileY) {
             return;
         }
-        discardNativeInstance();
         mBitmap = bitmap;
         mTileX = tileX;
         mTileY = tileY;
diff --git a/graphics/java/android/graphics/ColorMatrixColorFilter.java b/graphics/java/android/graphics/ColorMatrixColorFilter.java
index 61f6cc5..9201a2e 100644
--- a/graphics/java/android/graphics/ColorMatrixColorFilter.java
+++ b/graphics/java/android/graphics/ColorMatrixColorFilter.java
@@ -73,6 +73,8 @@
      * @see #getColorMatrix(ColorMatrix)
      * @see #setColorMatrixArray(float[])
      * @see ColorMatrix#reset()
+     *
+     * @hide
      */
     public void setColorMatrix(@Nullable ColorMatrix matrix) {
         discardNativeInstance();
@@ -99,6 +101,8 @@
      *
      * @throws ArrayIndexOutOfBoundsException if the specified array's
      *         length is < 20
+     *
+     * @hide
      */
     public void setColorMatrixArray(@Nullable float[] array) {
         // called '...Array' so that passing null isn't ambiguous
diff --git a/graphics/java/android/graphics/ComposeShader.java b/graphics/java/android/graphics/ComposeShader.java
index 8438bf2..0b1141a 100644
--- a/graphics/java/android/graphics/ComposeShader.java
+++ b/graphics/java/android/graphics/ComposeShader.java
@@ -59,43 +59,10 @@
     }
 
     private ComposeShader(Shader shaderA, Shader shaderB, int nativeMode) {
-        setInternal(shaderA, shaderB, nativeMode);
-    }
-
-    /**
-     * Reinitialize the ComposeShader's component Shaders and blend mode.
-     *
-     * @param shaderA  The colors from this shader are seen as the "dst" by the mode
-     * @param shaderB  The colors from this shader are seen as the "src" by the mode
-     * @param mode     The PorterDuff mode that combines the colors from the two shaders.
-     */
-    public void set(@NonNull Shader shaderA, @NonNull Shader shaderB, @NonNull Xfermode mode) {
-        setInternal(shaderA, shaderB, mode.porterDuffMode);
-    }
-
-    /**
-     * Reinitialize the ComposeShader's component Shaders and blend mode.
-     *
-     * @param shaderA  The colors from this shader are seen as the "dst" by the mode
-     * @param shaderB  The colors from this shader are seen as the "src" by the mode
-     * @param mode     The PorterDuff mode that combines the colors from the two shaders.
-     */
-    public void set(@NonNull Shader shaderA, @NonNull Shader shaderB,
-            @NonNull PorterDuff.Mode mode) {
-        setInternal(shaderA, shaderB, mode.nativeInt);
-    }
-
-    private void setInternal(Shader shaderA, Shader shaderB, int nativeMode) {
         if (shaderA == null || shaderB == null) {
             throw new IllegalArgumentException("Shader parameters must not be null");
         }
 
-        if (shaderA == mShaderA && shaderB == mShaderB && mPorterDuffMode == nativeMode) {
-            // no work to do...
-            return;
-        }
-
-        discardNativeInstance();
         mShaderA = shaderA;
         mShaderB = shaderB;
         mPorterDuffMode = nativeMode;
diff --git a/graphics/java/android/graphics/LightingColorFilter.java b/graphics/java/android/graphics/LightingColorFilter.java
index b0c145b..1578ffb 100644
--- a/graphics/java/android/graphics/LightingColorFilter.java
+++ b/graphics/java/android/graphics/LightingColorFilter.java
@@ -57,8 +57,6 @@
     /**
      * Returns the RGB color used to multiply the source color when the
      * color filter is applied.
-     *
-     * @see #setColorMultiply(int)
      */
     @ColorInt
     public int getColorMultiply() {
@@ -71,6 +69,8 @@
      * The alpha channel of this color is ignored.
      *
      * @see #getColorMultiply()
+     *
+     * @hide
      */
     public void setColorMultiply(@ColorInt int mul) {
         if (mMul != mul) {
@@ -82,8 +82,6 @@
     /**
      * Returns the RGB color that will be added to the source color
      * when the color filter is applied.
-     *
-     * @see #setColorAdd(int)
      */
     @ColorInt
     public int getColorAdd() {
@@ -96,6 +94,8 @@
      * The alpha channel of this color is ignored.
      *
      * @see #getColorAdd()
+     *
+     * @hide
      */
     public void setColorAdd(@ColorInt int add) {
         if (mAdd != add) {
diff --git a/graphics/java/android/graphics/LinearGradient.java b/graphics/java/android/graphics/LinearGradient.java
index 0e4cd0a..7139efe 100644
--- a/graphics/java/android/graphics/LinearGradient.java
+++ b/graphics/java/android/graphics/LinearGradient.java
@@ -57,7 +57,20 @@
     */
     public LinearGradient(float x0, float y0, float x1, float y1, @NonNull @ColorInt int colors[],
             @Nullable float positions[], @NonNull TileMode tile) {
-        set(x0, y0, x1, y1, colors, positions, tile);
+        if (colors.length < 2) {
+            throw new IllegalArgumentException("needs >= 2 number of colors");
+        }
+        if (positions != null && colors.length != positions.length) {
+            throw new IllegalArgumentException("color and position arrays must be of equal length");
+        }
+        mType = TYPE_COLORS_AND_POSITIONS;
+        mX0 = x0;
+        mY0 = y0;
+        mX1 = x1;
+        mY1 = y1;
+        mColors = colors.clone();
+        mPositions = positions != null ? positions.clone() : null;
+        mTileMode = tile;
     }
 
     /**
@@ -74,56 +87,6 @@
     public LinearGradient(float x0, float y0, float x1, float y1,
             @ColorInt int color0, @ColorInt int color1,
             @NonNull TileMode tile) {
-        set(x0, y0, x1, y1, color0, color1, tile);
-    }
-
-    /**
-     * Reinitialize the shader.
-     *
-     * @param x0           The x-coordinate for the start of the gradient line
-     * @param y0           The y-coordinate for the start of the gradient line
-     * @param x1           The x-coordinate for the end of the gradient line
-     * @param y1           The y-coordinate for the end of the gradient line
-     * @param colors       The colors to be distributed along the gradient line
-     * @param positions    May be null. The relative positions [0..1] of
-     *                     each corresponding color in the colors array. If this is null,
-     *                     the the colors are distributed evenly along the gradient line.
-     * @param  tile        The Shader tiling mode
-     */
-    public void set(float x0, float y0, float x1, float y1, @NonNull @ColorInt int colors[],
-            @Nullable float positions[], @NonNull TileMode tile) {
-        if (colors.length < 2) {
-            throw new IllegalArgumentException("needs >= 2 number of colors");
-        }
-        if (positions != null && colors.length != positions.length) {
-            throw new IllegalArgumentException("color and position arrays must be of equal length");
-        }
-        discardNativeInstance();
-        mType = TYPE_COLORS_AND_POSITIONS;
-        mX0 = x0;
-        mY0 = y0;
-        mX1 = x1;
-        mY1 = y1;
-        mColors = colors.clone();
-        mPositions = positions != null ? positions.clone() : null;
-        mTileMode = tile;
-    }
-
-    /**
-     * Reinitialize the shader.
-     *
-     * @param x0       The x-coordinate for the start of the gradient line
-     * @param y0       The y-coordinate for the start of the gradient line
-     * @param x1       The x-coordinate for the end of the gradient line
-     * @param y1       The y-coordinate for the end of the gradient line
-     * @param color0   The color at the start of the gradient line.
-     * @param color1   The color at the end of the gradient line.
-     * @param tile     The Shader tiling mode
-    */
-    public void set(float x0, float y0, float x1, float y1,
-            @ColorInt int color0, @ColorInt int color1,
-            @NonNull TileMode tile) {
-        discardNativeInstance();
         mType = TYPE_COLOR_START_AND_COLOR_END;
         mX0 = x0;
         mY0 = y0;
diff --git a/graphics/java/android/graphics/PorterDuffColorFilter.java b/graphics/java/android/graphics/PorterDuffColorFilter.java
index ccc6ead..01d5825 100644
--- a/graphics/java/android/graphics/PorterDuffColorFilter.java
+++ b/graphics/java/android/graphics/PorterDuffColorFilter.java
@@ -49,6 +49,8 @@
      *
      * @see Color
      * @see #setColor(int)
+     *
+     * @hide
      */
     @ColorInt
     public int getColor() {
@@ -64,6 +66,8 @@
      * @see Color
      * @see #getColor()
      * @see #getMode()
+     *
+     * @hide
      */
     public void setColor(@ColorInt int color) {
         if (mColor != color) {
@@ -78,6 +82,8 @@
      *
      * @see PorterDuff
      * @see #setMode(android.graphics.PorterDuff.Mode)
+     *
+     * @hide
      */
     public PorterDuff.Mode getMode() {
         return mMode;
@@ -90,6 +96,8 @@
      * @see PorterDuff
      * @see #getMode()
      * @see #getColor()
+     *
+     * @hide
      */
     public void setMode(@NonNull PorterDuff.Mode mode) {
         if (mode == null) {
diff --git a/graphics/java/android/graphics/RadialGradient.java b/graphics/java/android/graphics/RadialGradient.java
index ae8f7da..f4b1191 100644
--- a/graphics/java/android/graphics/RadialGradient.java
+++ b/graphics/java/android/graphics/RadialGradient.java
@@ -57,7 +57,22 @@
     public RadialGradient(float centerX, float centerY, float radius,
             @NonNull @ColorInt int colors[], @Nullable float stops[],
             @NonNull TileMode tileMode) {
-        set(centerX, centerY, radius, colors, stops, tileMode);
+        if (radius <= 0) {
+            throw new IllegalArgumentException("radius must be > 0");
+        }
+        if (colors.length < 2) {
+            throw new IllegalArgumentException("needs >= 2 number of colors");
+        }
+        if (stops != null && colors.length != stops.length) {
+            throw new IllegalArgumentException("color and position arrays must be of equal length");
+        }
+        mType = TYPE_COLORS_AND_POSITIONS;
+        mX = centerX;
+        mY = centerY;
+        mRadius = radius;
+        mColors = colors.clone();
+        mPositions = stops != null ? stops.clone() : null;
+        mTileMode = tileMode;
     }
 
     /**
@@ -72,59 +87,9 @@
      */
     public RadialGradient(float centerX, float centerY, float radius,
             @ColorInt int centerColor, @ColorInt int edgeColor, @NonNull TileMode tileMode) {
-        set(centerX, centerY, radius, centerColor, edgeColor, tileMode);
-    }
-
-    /**
-     * Reinitialize the shader.
-     *
-     * @param centerX  The x-coordinate of the center of the radius
-     * @param centerY  The y-coordinate of the center of the radius
-     * @param radius   Must be positive. The radius of the circle for this gradient.
-     * @param colors   The colors to be distributed between the center and edge of the circle
-     * @param stops    May be <code>null</code>. Valid values are between <code>0.0f</code> and
-     *                 <code>1.0f</code>. The relative position of each corresponding color in
-     *                 the colors array. If <code>null</code>, colors are distributed evenly
-     *                 between the center and edge of the circle.
-     * @param tileMode The Shader tiling mode
-     */
-    public void set(float centerX, float centerY, float radius,
-            @NonNull @ColorInt int colors[], @Nullable float stops[], @NonNull TileMode tileMode) {
         if (radius <= 0) {
             throw new IllegalArgumentException("radius must be > 0");
         }
-        if (colors.length < 2) {
-            throw new IllegalArgumentException("needs >= 2 number of colors");
-        }
-        if (stops != null && colors.length != stops.length) {
-            throw new IllegalArgumentException("color and position arrays must be of equal length");
-        }
-        discardNativeInstance();
-        mType = TYPE_COLORS_AND_POSITIONS;
-        mX = centerX;
-        mY = centerY;
-        mRadius = radius;
-        mColors = colors.clone();
-        mPositions = stops != null ? stops.clone() : null;
-        mTileMode = tileMode;
-    }
-
-    /**
-     * Reinitialize the shader.
-     *
-     * @param centerX     The x-coordinate of the center of the radius
-     * @param centerY     The y-coordinate of the center of the radius
-     * @param radius      Must be positive. The radius of the circle for this gradient
-     * @param centerColor The color at the center of the circle.
-     * @param edgeColor   The color at the edge of the circle.
-     * @param tileMode    The Shader tiling mode
-     */
-    public void set(float centerX, float centerY, float radius,
-            @ColorInt int centerColor, @ColorInt int edgeColor, @NonNull TileMode tileMode) {
-        if (radius <= 0) {
-            throw new IllegalArgumentException("radius must be > 0");
-        }
-        discardNativeInstance();
         mType = TYPE_COLOR_CENTER_AND_COLOR_EDGE;
         mX = centerX;
         mY = centerY;
diff --git a/graphics/java/android/graphics/SweepGradient.java b/graphics/java/android/graphics/SweepGradient.java
index 0a1aef6..b6b80b4 100644
--- a/graphics/java/android/graphics/SweepGradient.java
+++ b/graphics/java/android/graphics/SweepGradient.java
@@ -54,7 +54,18 @@
      */
     public SweepGradient(float cx, float cy,
             @NonNull @ColorInt int colors[], @Nullable float positions[]) {
-        set(cx, cy, colors, positions);
+        if (colors.length < 2) {
+            throw new IllegalArgumentException("needs >= 2 number of colors");
+        }
+        if (positions != null && colors.length != positions.length) {
+            throw new IllegalArgumentException(
+                    "color and position arrays must be of equal length");
+        }
+        mType = TYPE_COLORS_AND_POSITIONS;
+        mCx = cx;
+        mCy = cy;
+        mColors = colors.clone();
+        mPositions = positions != null ? positions.clone() : null;
     }
 
     /**
@@ -66,50 +77,6 @@
      * @param color1   The color to use at the end of the sweep
      */
     public SweepGradient(float cx, float cy, @ColorInt int color0, @ColorInt int color1) {
-        set(cx, cy, color0, color1);
-    }
-
-    /**
-     * Reinitialize the shader.
-     *
-     * @param cx       The x-coordinate of the center
-     * @param cy       The y-coordinate of the center
-     * @param colors   The colors to be distributed between around the center.
-     *                 There must be at least 2 colors in the array.
-     * @param positions May be NULL. The relative position of
-     *                 each corresponding color in the colors array, beginning
-     *                 with 0 and ending with 1.0. If the values are not
-     *                 monotonic, the drawing may produce unexpected results.
-     *                 If positions is NULL, then the colors are automatically
-     *                 spaced evenly.
-     */
-    public void set(float cx, float cy,
-            @NonNull @ColorInt int colors[], @Nullable float positions[]) {
-        if (colors.length < 2) {
-            throw new IllegalArgumentException("needs >= 2 number of colors");
-        }
-        if (positions != null && colors.length != positions.length) {
-            throw new IllegalArgumentException(
-                    "color and position arrays must be of equal length");
-        }
-        discardNativeInstance();
-        mType = TYPE_COLORS_AND_POSITIONS;
-        mCx = cx;
-        mCy = cy;
-        mColors = colors.clone();
-        mPositions = positions != null ? positions.clone() : null;
-    }
-
-    /**
-     * Reinitialize the shader.
-     *
-     * @param cx       The x-coordinate of the center
-     * @param cy       The y-coordinate of the center
-     * @param color0   The color to use at the start of the sweep
-     * @param color1   The color to use at the end of the sweep
-     */
-    public void set(float cx, float cy, @ColorInt int color0, @ColorInt int color1) {
-        discardNativeInstance();
         mType = TYPE_COLOR_START_AND_COLOR_END;
         mCx = cx;
         mCy = cy;
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 97d3e5e..18dc0dc 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -563,6 +563,9 @@
         /**
          * Constructs a builder with a file descriptor.
          *
+         * Caller is responsible for closing the passed file descriptor after {@link #build} is
+         * called.
+         *
          * @param fd The file descriptor. The passed fd must be mmap-able.
          */
         public Builder(@NonNull FileDescriptor fd) {
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index d95acff..3e5e3bf 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -390,14 +390,8 @@
 }
 
 bool SkiaCanvas::clipPath(const SkPath* path, SkClipOp op) {
-    SkRRect roundRect;
-    if (path->isRRect(&roundRect)) {
-        this->recordClip(roundRect, op);
-        mCanvas->clipRRect(roundRect, op);
-    } else {
-        this->recordClip(*path, op);
-        mCanvas->clipPath(*path, op);
-    }
+    this->recordClip(*path, op);
+    mCanvas->clipPath(*path, op);
     return !mCanvas->isClipEmpty();
 }
 
diff --git a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
index 7ae58a6..dafa074 100644
--- a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
+++ b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
@@ -20,6 +20,7 @@
 #include <SkColorMatrixFilter.h>
 #include <SkColorSpace.h>
 #include <SkImagePriv.h>
+#include <SkPathOps.h>
 #include <SkShader.h>
 
 using namespace android;
@@ -90,6 +91,16 @@
     ASSERT_EQ(expected, paint.getBlendMode());
 }
 
+TEST(SkiaBehavior, pathIntersection) {
+    SkPath p0, p1, result;
+    p0.addRect(SkRect::MakeXYWH(-5.0f, 0.0f, 1080.0f, 242.0f));
+    p1.addRect(SkRect::MakeXYWH(0.0f, 0.0f, 1080.0f, 242.0f));
+    Op(p0, p1, kIntersect_SkPathOp, &result);
+    SkRect resultRect;
+    ASSERT_TRUE(result.isRect(&resultRect));
+    ASSERT_EQ(SkRect::MakeXYWH(0.0f, 0.0f, 1075.0f, 242.0f), resultRect);
+}
+
 TEST(SkiaBehavior, srgbColorSpaceIsSingleton) {
     sk_sp<SkColorSpace> sRGB1 = SkColorSpace::MakeSRGB();
     sk_sp<SkColorSpace> sRGB2 = SkColorSpace::MakeSRGB();
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index b222a6d..68f46ad 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -646,7 +646,10 @@
      *
      * <p>Following this call {@link #hasAltitude} will return false,
      * and {@link #getAltitude} will return 0.0.
+     *
+     * @deprecated use a new Location object for location updates.
      */
+    @Deprecated
     public void removeAltitude() {
         mAltitude = 0.0f;
         mFieldsMask &= ~HAS_ALTITUDE_MASK;
@@ -683,7 +686,10 @@
      *
      * <p>Following this call {@link #hasSpeed} will return false,
      * and {@link #getSpeed} will return 0.0.
+     *
+     * @deprecated use a new Location object for location updates.
      */
+    @Deprecated
     public void removeSpeed() {
         mSpeed = 0.0f;
         mFieldsMask &= ~HAS_SPEED_MASK;
@@ -733,7 +739,10 @@
      *
      * <p>Following this call {@link #hasBearing} will return false,
      * and {@link #getBearing} will return 0.0.
+     *
+     * @deprecated use a new Location object for location updates.
      */
+    @Deprecated
     public void removeBearing() {
         mBearing = 0.0f;
         mFieldsMask &= ~HAS_BEARING_MASK;
@@ -790,7 +799,10 @@
      *
      * <p>Following this call {@link #hasAccuracy} will return false, and
      * {@link #getAccuracy} will return 0.0.
+     *
+     * @deprecated use a new Location object for location updates.
      */
+    @Deprecated
     public void removeAccuracy() {
         mHorizontalAccuracyMeters = 0.0f;
         mFieldsMask &= ~HAS_HORIZONTAL_ACCURACY_MASK;
@@ -839,7 +851,11 @@
      *
      * <p>Following this call {@link #hasVerticalAccuracy} will return false, and
      * {@link #getVerticalAccuracyMeters} will return 0.0.
+     *
+     * @deprecated use a new Location object for location updates.
+     * @removed
      */
+    @Deprecated
     public void removeVerticalAccuracy() {
         mVerticalAccuracyMeters = 0.0f;
         mFieldsMask &= ~HAS_VERTICAL_ACCURACY_MASK;
@@ -883,7 +899,11 @@
      *
      * <p>Following this call {@link #hasSpeedAccuracy} will return false, and
      * {@link #getSpeedAccuracyMetersPerSecond} will return 0.0.
+     *
+     * @deprecated use a new Location object for location updates.
+     * @removed
      */
+    @Deprecated
     public void removeSpeedAccuracy() {
         mSpeedAccuracyMetersPerSecond = 0.0f;
         mFieldsMask &= ~HAS_SPEED_ACCURACY_MASK;
@@ -927,7 +947,11 @@
      *
      * <p>Following this call {@link #hasBearingAccuracy} will return false, and
      * {@link #getBearingAccuracyDegrees} will return 0.0.
+     *
+     * @deprecated use a new Location object for location updates.
+     * @removed
      */
+    @Deprecated
     public void removeBearingAccuracy() {
         mBearingAccuracyDegrees = 0.0f;
         mFieldsMask &= ~HAS_BEARING_ACCURACY_MASK;
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 9386246..d5efc97 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1007,13 +1007,14 @@
      * @param context the Context to use when resolving the Uri
      * @param uri the Content URI of the data you want to play
      * @param headers the headers to be sent together with the request for the data
-     *                Note that the cross domain redirection is allowed by default, but that can be
-     *                changed with key/value pairs through the headers parameter with
-     *                "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value
-     *                to disallow or allow cross domain redirection.
      *                The headers must not include cookies. Instead, use the cookies param.
      * @param cookies the cookies to be sent together with the request
      * @throws IllegalStateException if it is called in an invalid state
+     *
+     * <p><strong>Note</strong> that the cross domain redirection is allowed by default,
+     * but that can be changed with key/value pairs through the headers parameter with
+     * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value to
+     * disallow or allow cross domain redirection.
      */
     public void setDataSource(@NonNull Context context, @NonNull Uri uri,
             @Nullable Map<String, String> headers, @Nullable List<HttpCookie> cookies)
@@ -1056,11 +1057,12 @@
      * @param context the Context to use when resolving the Uri
      * @param uri the Content URI of the data you want to play
      * @param headers the headers to be sent together with the request for the data
-     *                Note that the cross domain redirection is allowed by default, but that can be
-     *                changed with key/value pairs through the headers parameter with
-     *                "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value
-     *                to disallow or allow cross domain redirection.
      * @throws IllegalStateException if it is called in an invalid state
+     *
+     * <p><strong>Note</strong> that the cross domain redirection is allowed by default,
+     * but that can be changed with key/value pairs through the headers parameter with
+     * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value to
+     * disallow or allow cross domain redirection.
      */
     public void setDataSource(@NonNull Context context, @NonNull Uri uri,
             @Nullable Map<String, String> headers)
@@ -1981,7 +1983,7 @@
         mOnSubtitleDataListener = null;
 
         // Modular DRM clean up
-        mOnDrmConfigListener = null;
+        mOnDrmConfigHelper = null;
         mOnDrmInfoHandlerDelegate = null;
         mOnDrmPreparedHandlerDelegate = null;
         resetDrmState();
@@ -3905,11 +3907,11 @@
      * 'securityLevel', which has to be set after DRM scheme creation but
      * before the DRM session is opened.
      *
-     * The only allowed DRM calls in this listener are getDrmPropertyString
-     * and setDrmPropertyString.
+     * The only allowed DRM calls in this listener are {@code getDrmPropertyString}
+     * and {@code setDrmPropertyString}.
      *
      */
-    public interface OnDrmConfigListener
+    public interface OnDrmConfigHelper
     {
         /**
          * Called to give the app the opportunity to configure DRM before the session is created
@@ -3922,19 +3924,19 @@
     /**
      * Register a callback to be invoked for configuration of the DRM object before
      * the session is created.
-     * The callback will be invoked synchronously half-way into the execution
+     * The callback will be invoked synchronously during the execution
      * of {@link #prepareDrm(UUID uuid)}.
      *
      * @param listener the callback that will be run
      */
-    public void setOnDrmConfigListener(OnDrmConfigListener listener)
+    public void setOnDrmConfigHelper(OnDrmConfigHelper listener)
     {
         synchronized (mDrmLock) {
-            mOnDrmConfigListener = listener;
+            mOnDrmConfigHelper = listener;
         } // synchronized
     }
 
-    private OnDrmConfigListener mOnDrmConfigListener;
+    private OnDrmConfigHelper mOnDrmConfigHelper;
 
     /**
      * Interface definition of a callback to be invoked when the
@@ -3946,7 +3948,7 @@
          * Called to indicate DRM info is available
          *
          * @param mp the {@code MediaPlayer} associated with this callback
-         * @param drmInfo DRM info of the source including PSSH, mimes, and subset
+         * @param drmInfo DRM info of the source including PSSH, and subset
          *                of crypto schemes supported by this device
          */
         public void onDrmInfo(MediaPlayer mp, DrmInfo drmInfo);
@@ -3982,6 +3984,41 @@
 
     private OnDrmInfoHandlerDelegate mOnDrmInfoHandlerDelegate;
 
+
+    /**
+     * The status codes for {@link OnDrmPreparedListener#onDrmPrepared} listener.
+     * <p>
+     *
+     * DRM preparation has succeeded.
+     */
+    public static final int PREPARE_DRM_STATUS_SUCCESS = 0;
+
+    /**
+     * The device required DRM provisioning but couldn't reach the provisioning server.
+     */
+    public static final int PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR = 1;
+
+    /**
+     * The device required DRM provisioning but the provisioning server denied the request.
+     */
+    public static final int PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR = 2;
+
+    /**
+     * The DRM preparation has failed .
+     */
+    public static final int PREPARE_DRM_STATUS_PREPARATION_ERROR = 3;
+
+
+    /** @hide */
+    @IntDef({
+        PREPARE_DRM_STATUS_SUCCESS,
+        PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR,
+        PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR,
+        PREPARE_DRM_STATUS_PREPARATION_ERROR,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PrepareDrmStatusCode {}
+
     /**
      * Interface definition of a callback to notify the app when the
      * DRM is ready for key request/response
@@ -3992,9 +4029,13 @@
          * Called to notify the app that prepareDrm is finished and ready for key request/response
          *
          * @param mp the {@code MediaPlayer} associated with this callback
-         * @param success the result of DRM preparation
+         * @param status the result of DRM preparation which can be
+         * {@link #PREPARE_DRM_STATUS_SUCCESS},
+         * {@link #PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR},
+         * {@link #PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR}, or
+         * {@link #PREPARE_DRM_STATUS_PREPARATION_ERROR}.
          */
-        public void onDrmPrepared(MediaPlayer mp, boolean success);
+        public void onDrmPrepared(MediaPlayer mp, @PrepareDrmStatusCode int status);
     }
 
     /**
@@ -4038,30 +4079,28 @@
             mOnDrmInfoListener = listener;
 
             // find the looper for our new event handler
-            Looper looper = null;
             if (handler != null) {
-                looper = handler.getLooper();
-            }
-
-            // construct the event handler with this looper
-            if (looper != null) {
-                // implement the event handler delegate
-                mHandler = new Handler(looper) {
-                    public void handleMessage(Message msg) {
-                        DrmInfo drmInfo = (DrmInfo)msg.obj;
-                        mOnDrmInfoListener.onDrmInfo(mMediaPlayer, drmInfo);
-                    }
-                };
+                mHandler = handler;
+            } else {
+                // handler == null
+                // Will let OnDrmInfoListener be called in mEventHandler similar to other
+                // legacy notifications. This is because MEDIA_DRM_INFO's notification has to be
+                // sent before MEDIA_PREPARED's (i.e., in the same order they are issued by
+                // mediaserver). As a result, the callback has to be called directly by
+                // EventHandler.handleMessage similar to onPrepared.
             }
         }
 
         void notifyClient(DrmInfo drmInfo) {
-            if ( mHandler != null ) {
-                Message msg = new Message();  // no message type needed
-                msg.obj = drmInfo;
-                mHandler.sendMessage(msg);
+            if (mHandler != null) {
+                mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                       mOnDrmInfoListener.onDrmInfo(mMediaPlayer, drmInfo);
+                    }
+                });
             }
-            else {  // no handler: direct call
+            else {  // no handler: direct call by mEventHandler
                 mOnDrmInfoListener.onDrmInfo(mMediaPlayer, drmInfo);
             }
         }
@@ -4078,31 +4117,26 @@
             mOnDrmPreparedListener = listener;
 
             // find the looper for our new event handler
-            Looper looper = null;
             if (handler != null) {
-                looper = handler.getLooper();
-            }
-
-            // construct the event handler with this looper
-            if (looper != null) {
-                // implement the event handler delegate
-                mHandler = new Handler(looper) {
-                    public void handleMessage(Message msg) {
-                        boolean success = (msg.arg1 == 0) ? false : true;
-                        mOnDrmPreparedListener.onDrmPrepared(mMediaPlayer, success);
-                    }
-                };
+                mHandler = handler;
+            } else if (mEventHandler != null) {
+                // Otherwise, use mEventHandler
+                mHandler = mEventHandler;
+            } else {
+                Log.e(TAG, "OnDrmPreparedHandlerDelegate: Unexpected null mEventHandler");
             }
         }
 
-        void notifyClient(boolean success) {
-            if ( mHandler != null ) {
-                Message msg = new Message();  // no message type needed
-                msg.arg1 = success ? 1 : 0;
-                mHandler.sendMessage(msg);
-            }
-            else {  // no handler: direct call
-                mOnDrmPreparedListener.onDrmPrepared(mMediaPlayer, success);
+        void notifyClient(int status) {
+            if (mHandler != null) {
+                mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        mOnDrmPreparedListener.onDrmPrepared(mMediaPlayer, status);
+                    }
+                });
+            } else {
+                Log.e(TAG, "OnDrmPreparedHandlerDelegate:notifyClient: Unexpected null mHandler");
             }
         }
     }
@@ -4137,7 +4171,7 @@
     /**
      * Prepares the DRM for the current source
      * <p>
-     * If {@code OnDrmConfigListener} is registered, it will be called half-way into
+     * If {@code OnDrmConfigHelper} is registered, it will be called during
      * preparation to allow configuration of the DRM properties before opening the
      * DRM session. Note that the callback is called synchronously in the thread that called
      * {@code prepareDrm}. It should be used only for a series of {@code getDrmPropertyString}
@@ -4148,9 +4182,9 @@
      * complete depending on the network connectivity.
      * If {@code OnDrmPreparedListener} is registered, prepareDrm() runs in non-blocking
      * mode by launching the provisioning in the background and returning. The listener
-     * will be called when provisioning and preperation has finished. If a
+     * will be called when provisioning and preparation has finished. If a
      * {@code OnDrmPreparedListener} is not registered, prepareDrm() waits till provisioning
-     * and preperation has finished, i.e., runs in blocking mode.
+     * and preparation has finished, i.e., runs in blocking mode.
      * <p>
      * If {@code OnDrmPreparedListener} is registered, it is called to indicate the DRM
      * session being ready. The application should not make any assumption about its call
@@ -4158,18 +4192,23 @@
      * execute the listener (unless the listener is registered with a handler thread).
      * <p>
      *
-     * @param uuid The UUID of the crypto scheme.
+     * @param uuid The UUID of the crypto scheme. If not known beforehand, it can be retrieved
+     * from the source through {@code getDrmInfo} or registering a {@code onDrmInfoListener}.
      *
-     * @throws IllegalStateException       if called before prepare(), or there exists a Drm already
-     * @throws UnsupportedSchemeException  if the crypto scheme is not supported
-     * @throws ResourceBusyException       if required DRM resources are in use
-     * @throws ProvisioningErrorException  if provisioning is required but an attempt failed
+     * @throws IllegalStateException              if called before prepare(), or the DRM was
+     *                                            prepared already
+     * @throws UnsupportedSchemeException         if the crypto scheme is not supported
+     * @throws ResourceBusyException              if required DRM resources are in use
+     * @throws ProvisioningNetworkErrorException  if provisioning is required but failed due to a
+     *                                            network error
+     * @throws ProvisioningServerErrorException   if provisioning is required but failed due to
+     *                                            the request denied by the provisioning server
      */
     public void prepareDrm(@NonNull UUID uuid)
-            throws UnsupportedSchemeException,
-                   ResourceBusyException, ProvisioningErrorException
+            throws UnsupportedSchemeException, ResourceBusyException,
+                   ProvisioningNetworkErrorException, ProvisioningServerErrorException
     {
-        Log.v(TAG, "prepareDrm: uuid: " + uuid + " mOnDrmConfigListener: " + mOnDrmConfigListener);
+        Log.v(TAG, "prepareDrm: uuid: " + uuid + " mOnDrmConfigHelper: " + mOnDrmConfigHelper);
 
         boolean allDoneWithoutProvisioning = false;
         // get a snapshot as we'll use them outside the lock
@@ -4177,7 +4216,7 @@
 
         synchronized (mDrmLock) {
 
-            // only allowing if tied to a protected source; might releax for releasing offline keys
+            // only allowing if tied to a protected source; might relax for releasing offline keys
             if (mDrmInfo == null) {
                 final String msg = "prepareDrm(): Wrong usage: The player must be prepared and " +
                         "DRM info be retrieved before this call.";
@@ -4226,8 +4265,8 @@
 
 
         // call the callback outside the lock
-        if (mOnDrmConfigListener != null)  {
-            mOnDrmConfigListener.onDrmConfig(this);
+        if (mOnDrmConfigHelper != null)  {
+            mOnDrmConfigHelper.onDrmConfig(this);
         }
 
         synchronized (mDrmLock) {
@@ -4251,15 +4290,33 @@
                 Log.w(TAG, "prepareDrm: NotProvisionedException");
 
                 // handle provisioning internally; it'll reset mPrepareDrmInProgress
-                boolean result = HandleProvisioninig(uuid);
+                int result = HandleProvisioninig(uuid);
 
                 // if blocking mode, we're already done;
                 // if non-blocking mode, we attempted to launch background provisioning
-                if (result == false) {
-                    final String msg = "prepareDrm: Provisioning was required but failed.";
-                    Log.e(TAG, msg);
+                if (result != PREPARE_DRM_STATUS_SUCCESS) {
                     earlyExit = true;
-                    throw new ProvisioningErrorException(msg);
+                    String msg;
+
+                    switch (result) {
+                    case PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR:
+                        msg = "prepareDrm: Provisioning was required but failed " +
+                                "due to a network error.";
+                        Log.e(TAG, msg);
+                        throw new ProvisioningNetworkErrorException(msg);
+
+                    case PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR:
+                        msg = "prepareDrm: Provisioning was required but the request " +
+                                "was denied by the server.";
+                        Log.e(TAG, msg);
+                        throw new ProvisioningServerErrorException(msg);
+
+                    case PREPARE_DRM_STATUS_PREPARATION_ERROR:
+                    default: // default for safeguard
+                        msg = "prepareDrm: Post-provisioning preparation failed.";
+                        Log.e(TAG, msg);
+                        throw new IllegalStateException(msg);
+                    }
                 }
                 // nothing else to do;
                 // if blocking or non-blocking, HandleProvisioninig does the re-attempt & cleanup
@@ -4281,7 +4338,7 @@
         // if finished successfully without provisioning, call the callback outside the lock
         if (allDoneWithoutProvisioning) {
             if (onDrmPreparedHandlerDelegate != null)
-                onDrmPreparedHandlerDelegate.notifyClient(true /*success*/);
+                onDrmPreparedHandlerDelegate.notifyClient(PREPARE_DRM_STATUS_SUCCESS);
         }
 
     }
@@ -4291,6 +4348,10 @@
 
     /**
      * Releases the DRM session
+     * <p>
+     * The player has to have an active DRM session and be in stopped, or prepared
+     * state before this call is made.
+     * A {@code reset()} call will release the DRM session implicitly.
      *
      * @throws NoDrmSchemeException if there is no active DRM session to release
      */
@@ -4307,7 +4368,7 @@
 
             try {
                 // we don't have the player's state in this layer. The below call raises
-                // exception if we're in a non-stopped/idle state.
+                // exception if we're in a non-stopped/prepared state.
 
                 // for cleaning native/mediaserver crypto object
                 _releaseDrm();
@@ -4316,9 +4377,11 @@
                 cleanDrmObj();
 
                 mActiveDrmScheme = false;
-            } catch (Exception e) {
+            } catch (IllegalStateException e) {
                 Log.w(TAG, "releaseDrm: Exception ", e);
-                throw e;
+                throw new IllegalStateException("releaseDrm: The player is not in a valid state.");
+            } catch (Exception e) {
+                Log.e(TAG, "releaseDrm: Exception ", e);
             }
         }   // synchronized
     }
@@ -4337,21 +4400,23 @@
      * it should deliver to the response to the DRM engine plugin using the method
      * {@link #provideKeyResponse}.
      *
-     * @param scope may be a container-specific initialization data or a keySetId,
-     * depending on the specified keyType.
-     * When the keyType is KEY_TYPE_STREAMING or KEY_TYPE_OFFLINE, scope should be set to
-     * the container-specific initialization data. Its meaning is interpreted based on the
-     * mime type provided in the mimeType parameter.  It could contain, for example,
-     * the content ID, key ID or other data obtained from the content metadata that is
-     * required in generating the key request.
-     * When the keyType is KEY_TYPE_RELEASE, scope should be set to the keySetId of
-     * the keys being released.
+     * @param keySetId is the key-set identifier of the offline keys being released when keyType is
+     * {@link MediaDrm#KEY_TYPE_RELEASE}. It should be set to null for other key requests, when
+     * keyType is {@link MediaDrm#KEY_TYPE_STREAMING} or {@link MediaDrm#KEY_TYPE_OFFLINE}.
+     *
+     * @param initData is the container-specific initialization data when the keyType is
+     * {@link MediaDrm#KEY_TYPE_STREAMING} or {@link MediaDrm#KEY_TYPE_OFFLINE}. Its meaning is
+     * interpreted based on the mime type provided in the mimeType parameter.  It could
+     * contain, for example, the content ID, key ID or other data obtained from the content
+     * metadata that is required in generating the key request.
+     * When the keyType is {@link MediaDrm#KEY_TYPE_RELEASE}, it should be set to null.
      *
      * @param mimeType identifies the mime type of the content
      *
-     * @param keyType specifes the type of the request. The request may be to acquire
-     * keys for streaming or offline content, or to release previously acquired
-     * keys, which are identified by a keySetId.
+     * @param keyType specifies the type of the request. The request may be to acquire
+     * keys for streaming, {@link MediaDrm#KEY_TYPE_STREAMING}, or for offline content
+     * {@link MediaDrm#KEY_TYPE_OFFLINE}, or to release previously acquired
+     * keys ({@link MediaDrm#KEY_TYPE_RELEASE}), which are identified by a keySetId.
      *
      * @param optionalParameters are included in the key request message to
      * allow a client application to provide additional message parameters to the server.
@@ -4360,12 +4425,13 @@
      * @throws NoDrmSchemeException if there is no active DRM session
      */
     @NonNull
-    public MediaDrm.KeyRequest getKeyRequest(@NonNull byte[] scope, @Nullable String mimeType,
-            @MediaDrm.KeyType int keyType, @Nullable Map<String, String> optionalParameters)
+    public MediaDrm.KeyRequest getKeyRequest(@Nullable byte[] keySetId, @Nullable byte[] initData,
+            @Nullable String mimeType, @MediaDrm.KeyType int keyType,
+            @Nullable Map<String, String> optionalParameters)
             throws NoDrmSchemeException
     {
         Log.v(TAG, "getKeyRequest: " +
-                " scope: " + scope + " mimeType: " + mimeType +
+                " keySetId: " + keySetId + " initData:" + initData + " mimeType: " + mimeType +
                 " keyType: " + keyType + " optionalParameters: " + optionalParameters);
 
         synchronized (mDrmLock) {
@@ -4375,20 +4441,16 @@
             }
 
             try {
-                byte[] scopeOut = (keyType != MediaDrm.KEY_TYPE_RELEASE) ?
-                                  mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE
-                                  scope;          // keySetId for KEY_TYPE_RELEASE
-
-                byte[] initData = (keyType != MediaDrm.KEY_TYPE_RELEASE) ?
-                                  scope :         // initData for KEY_TYPE_STREAMING/OFFLINE
-                                  null;           // not used for KEY_TYPE_RELEASE
+                byte[] scope = (keyType != MediaDrm.KEY_TYPE_RELEASE) ?
+                        mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE
+                        keySetId;       // keySetId for KEY_TYPE_RELEASE
 
                 HashMap<String, String> hmapOptionalParameters =
                                                 (optionalParameters != null) ?
                                                 new HashMap<String, String>(optionalParameters) :
                                                 null;
 
-                MediaDrm.KeyRequest request = mDrmObj.getKeyRequest(scopeOut, initData, mimeType,
+                MediaDrm.KeyRequest request = mDrmObj.getKeyRequest(scope, initData, mimeType,
                                                               keyType, hmapOptionalParameters);
                 Log.v(TAG, "getKeyRequest:   --> request: " + request);
 
@@ -4499,8 +4561,8 @@
      * @param propertyName the property name
      *
      * Standard fields names are:
-     * {link #PROPERTY_VENDOR}, {link #PROPERTY_VERSION},
-     * {link #PROPERTY_DESCRIPTION}, {link #PROPERTY_ALGORITHMS}
+     * {@link MediaDrm#PROPERTY_VENDOR}, {@link MediaDrm#PROPERTY_VERSION},
+     * {@link MediaDrm#PROPERTY_DESCRIPTION}, {@link MediaDrm#PROPERTY_ALGORITHMS}
      */
     @NonNull
     public String getDrmPropertyString(@NonNull @MediaDrm.StringProperty String propertyName)
@@ -4537,8 +4599,8 @@
      * @param value the property value
      *
      * Standard fields names are:
-     * {link #PROPERTY_VENDOR}, {link #PROPERTY_VERSION},
-     * {link #PROPERTY_DESCRIPTION}, {link #PROPERTY_ALGORITHMS}
+     * {@link MediaDrm#PROPERTY_VENDOR}, {@link MediaDrm#PROPERTY_VERSION},
+     * {@link MediaDrm#PROPERTY_DESCRIPTION}, {@link MediaDrm#PROPERTY_ALGORITHMS}
      */
     public void setDrmPropertyString(@NonNull @MediaDrm.StringProperty String propertyName,
                                      @NonNull String value)
@@ -4565,8 +4627,6 @@
     public static final class DrmInfo {
         private Map<UUID, byte[]> mapPssh;
         private UUID[] supportedSchemes;
-        // TODO: Won't need this in final release. Only keeping it for the existing test app.
-        private String[] mimes;
 
         public Map<UUID, byte[]> getPssh() {
             return mapPssh;
@@ -4574,15 +4634,10 @@
         public UUID[] getSupportedSchemes() {
             return supportedSchemes;
         }
-        // TODO: Won't need this in final release. Only keeping it for the existing test app.
-        public String[] getMimes() {
-            return mimes;
-        }
 
-        private DrmInfo(Map<UUID, byte[]> Pssh, UUID[] SupportedSchemes, String[] Mimes) {
+        private DrmInfo(Map<UUID, byte[]> Pssh, UUID[] SupportedSchemes) {
             mapPssh = Pssh;
             supportedSchemes = SupportedSchemes;
-            mimes = Mimes;
         }
 
         private DrmInfo(Parcel parcel) {
@@ -4608,18 +4663,12 @@
                       supportedSchemes[i]);
             }
 
-            // TODO: Won't need this in final release. Only keeping it for the test app.
-            mimes = parcel.readStringArray();
-            int mimeCount = mimes.length;
-            Log.v(TAG, "DrmInfo() mime: " + Arrays.toString(mimes));
-
             Log.v(TAG, "DrmInfo() Parcel psshsize: " + psshsize +
-                  " supportedDRMsCount: " + supportedDRMsCount +
-                  " mimeCount: " + mimeCount);
+                  " supportedDRMsCount: " + supportedDRMsCount);
         }
 
         private DrmInfo makeCopy() {
-            return new DrmInfo(this.mapPssh, this.supportedSchemes, this.mimes);
+            return new DrmInfo(this.mapPssh, this.supportedSchemes);
         }
 
         private String arrToHex(byte[] bytes) {
@@ -4714,11 +4763,22 @@
 
     /**
      * Thrown when the device requires DRM provisioning but the provisioning attempt has
-     * failed (for example: network timeout, provisioning server error).
+     * failed due to a network error (Internet reachability, timeout, etc.).
      * Extends MediaDrm.MediaDrmException
      */
-    public static final class ProvisioningErrorException extends MediaDrmException {
-        public ProvisioningErrorException(String detailMessage) {
+    public static final class ProvisioningNetworkErrorException extends MediaDrmException {
+        public ProvisioningNetworkErrorException(String detailMessage) {
+            super(detailMessage);
+        }
+    }
+
+    /**
+     * Thrown when the device requires DRM provisioning but the provisioning attempt has
+     * failed due to the provisioning server denying the request.
+     * Extends MediaDrm.MediaDrmException
+     */
+    public static final class ProvisioningServerErrorException extends MediaDrmException {
+        public ProvisioningServerErrorException(String detailMessage) {
             super(detailMessage);
         }
     }
@@ -4770,14 +4830,13 @@
 
         private UUID uuid;
         private String urlStr;
-        private byte[] response;
         private Object drmLock;
         private OnDrmPreparedHandlerDelegate onDrmPreparedHandlerDelegate;
         private MediaPlayer mediaPlayer;
-        private boolean succeeded;
+        private int status;
         private boolean finished;
-        public  boolean succeeded() {
-            return succeeded;
+        public  int status() {
+            return status;
         }
 
         public ProvisioningThread initialize(MediaDrm.ProvisionRequest request,
@@ -4790,12 +4849,15 @@
             urlStr = request.getDefaultUrl() + "&signedRequest=" + new String(request.getData());
             this.uuid = uuid;
 
+            status = PREPARE_DRM_STATUS_PREPARATION_ERROR;
+
             Log.v(TAG, "HandleProvisioninig: Thread is initialised url: " + urlStr);
             return this;
         }
 
         public void run() {
 
+            byte[] response = null;
             boolean provisioningSucceeded = false;
             try {
                 URL url = new URL(urlStr);
@@ -4813,11 +4875,13 @@
                     Log.v(TAG, "HandleProvisioninig: Thread run: response " +
                             response.length + " " + response);
                 } catch (Exception e) {
+                    status = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR;
                     Log.w(TAG, "HandleProvisioninig: Thread run: connect " + e + " url: " + url);
                 } finally {
                     connection.disconnect();
                 }
             } catch (Exception e)   {
+                status = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR;
                 Log.w(TAG, "HandleProvisioninig: Thread run: openConnection " + e);
             }
 
@@ -4828,12 +4892,15 @@
                             "provideProvisionResponse SUCCEEDED!");
 
                     provisioningSucceeded = true;
-                } catch (Exception e)   {
+                } catch (Exception e) {
+                    status = PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR;
                     Log.w(TAG, "HandleProvisioninig: Thread run: " +
                             "provideProvisionResponse " + e);
                 }
             }
 
+            boolean succeeded = false;
+
             // non-blocking mode needs the lock
             if (onDrmPreparedHandlerDelegate != null) {
 
@@ -4841,6 +4908,9 @@
                     // continuing with prepareDrm
                     if (provisioningSucceeded) {
                         succeeded = mediaPlayer.resumePrepareDrm(uuid);
+                        status = (succeeded) ?
+                                PREPARE_DRM_STATUS_SUCCESS :
+                                PREPARE_DRM_STATUS_PREPARATION_ERROR;
                     }
                     mediaPlayer.mDrmProvisioningInProgress = false;
                     mediaPlayer.mPrepareDrmInProgress = false;
@@ -4850,12 +4920,15 @@
                 } // synchronized
 
                 // calling the callback outside the lock
-                onDrmPreparedHandlerDelegate.notifyClient(succeeded);
+                onDrmPreparedHandlerDelegate.notifyClient(status);
             } else {   // blocking mode already has the lock
 
                 // continuing with prepareDrm
                 if (provisioningSucceeded) {
                     succeeded = mediaPlayer.resumePrepareDrm(uuid);
+                    status = (succeeded) ?
+                            PREPARE_DRM_STATUS_SUCCESS :
+                            PREPARE_DRM_STATUS_PREPARATION_ERROR;
                 }
                 mediaPlayer.mDrmProvisioningInProgress = false;
                 mediaPlayer.mPrepareDrmInProgress = false;
@@ -4869,19 +4942,19 @@
 
     }   // ProvisioningThread
 
-    private boolean HandleProvisioninig(UUID uuid)
+    private int HandleProvisioninig(UUID uuid)
     {
         // the lock is already held by the caller
 
         if (mDrmProvisioningInProgress) {
             Log.e(TAG, "HandleProvisioninig: Unexpected mDrmProvisioningInProgress");
-            return false;
+            return PREPARE_DRM_STATUS_PREPARATION_ERROR;
         }
 
         MediaDrm.ProvisionRequest provReq = mDrmObj.getProvisionRequest();
         if (provReq == null) {
             Log.e(TAG, "HandleProvisioninig: getProvisionRequest returned null.");
-            return false;
+            return PREPARE_DRM_STATUS_PREPARATION_ERROR;
         }
 
         Log.v(TAG, "HandleProvisioninig provReq " +
@@ -4893,11 +4966,11 @@
         mDrmProvisioningThread = new ProvisioningThread().initialize(provReq, uuid, this);
         mDrmProvisioningThread.start();
 
-        boolean result = false;
+        int result;
 
-        // non-blocking
+        // non-blocking: this is not the final result
         if (mOnDrmPreparedHandlerDelegate != null) {
-            result = true;
+            result = PREPARE_DRM_STATUS_SUCCESS;
         } else {
             // if blocking mode, wait till provisioning is done
             try {
@@ -4905,7 +4978,7 @@
             } catch (Exception e) {
                 Log.w(TAG, "HandleProvisioninig: Thread.join Exception " + e);
             }
-            result = mDrmProvisioningThread.succeeded();
+            result = mDrmProvisioningThread.status();
             // no longer need the thread
             mDrmProvisioningThread = null;
         }
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
index 246bd2b..5beb412 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
@@ -314,8 +314,8 @@
     }
 
     class DevicesAdapter extends ArrayAdapter<DeviceFilterPair> {
-        //TODO wifi icon
         private Drawable BLUETOOTH_ICON = icon(android.R.drawable.stat_sys_data_bluetooth);
+        private Drawable WIFI_ICON = icon(com.android.internal.R.drawable.ic_wifi_signal_3);
 
         private Drawable icon(int drawableRes) {
             Drawable icon = getResources().getDrawable(drawableRes, null);
@@ -345,6 +345,11 @@
                     device.equals(mSelectedDevice)
                             ? Color.GRAY
                             : Color.TRANSPARENT);
+            textView.setCompoundDrawablesWithIntrinsicBounds(
+                    device.device instanceof android.net.wifi.ScanResult
+                        ? WIFI_ICON
+                        : BLUETOOTH_ICON,
+                    null, null, null);
             textView.setOnClickListener((view) -> {
                 mSelectedDevice = device;
                 notifyDataSetChanged();
@@ -357,8 +362,6 @@
             textView.setTextColor(Color.BLACK);
             final int padding = DeviceChooserActivity.getPadding(getResources());
             textView.setPadding(padding, padding, padding, padding);
-            textView.setCompoundDrawablesWithIntrinsicBounds(
-                    BLUETOOTH_ICON, null, null, null);
             textView.setCompoundDrawablePadding(padding);
             return textView;
         }
diff --git a/packages/SettingsLib/res/layout/usage_view.xml b/packages/SettingsLib/res/layout/usage_view.xml
index 1d56668..151d1ee 100644
--- a/packages/SettingsLib/res/layout/usage_view.xml
+++ b/packages/SettingsLib/res/layout/usage_view.xml
@@ -76,17 +76,23 @@
             android:id="@+id/bottom_label_space"
             android:layout_width="@dimen/usage_graph_labels_width"
             android:layout_height="wrap_content"/>
-        <include android:id="@+id/label_start"
-            layout="@layout/usage_side_label" />
-
-        <Space
+        <LinearLayout
             android:layout_width="0dp"
             android:layout_height="wrap_content"
-            android:layout_weight="1" />
+            android:layout_weight="1"
+            android:orientation="horizontal"
+            android:layoutDirection="ltr">
+            <include android:id="@+id/label_start"
+                     layout="@layout/usage_side_label" />
 
-        <include android:id="@+id/label_end"
-            layout="@layout/usage_side_label" />
+            <Space
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1" />
 
+            <include android:id="@+id/label_end"
+                     layout="@layout/usage_side_label" />
+        </LinearLayout>
     </LinearLayout>
 
 </LinearLayout>
diff --git a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityButtonHelper.java b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityButtonHelper.java
new file mode 100644
index 0000000..972ea34
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityButtonHelper.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 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.accessibility;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.content.Context;
+import android.content.res.Resources;
+import android.provider.Settings;
+import android.view.accessibility.AccessibilityManager;
+
+import java.util.List;
+
+/**
+ * A helper class to assist determining the state of the accessibility button that can appear
+ * within the software-rendered navigation area.
+ */
+public class AccessibilityButtonHelper {
+    public static boolean isRequestedByMagnification(Context ctx) {
+        return Settings.Secure.getInt(ctx.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, 0) == 1;
+    }
+
+    public static boolean isRequestedByAccessibilityService(Context ctx) {
+        final AccessibilityManager accessibilityManager = ctx.getSystemService(
+                AccessibilityManager.class);
+        List<AccessibilityServiceInfo> services =
+                accessibilityManager.getEnabledAccessibilityServiceList(
+                        AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
+        if (services != null) {
+            for (int i = 0, size = services.size(); i < size; i++) {
+                if ((services.get(i).flags
+                        & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON)
+                        != 0) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    public static boolean isRequested(Context ctx) {
+        return isRequestedByMagnification(ctx) || isRequestedByAccessibilityService(ctx);
+    }
+
+    public static boolean isDeviceSupported(Resources res) {
+        return res.getBoolean(com.android.internal.R.bool.config_showNavigationBar);
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index 10f3b63..c109704 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -46,6 +46,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.text.format.Formatter;
+import android.util.IconDrawableFactory;
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -95,6 +96,7 @@
 
     final Context mContext;
     final PackageManager mPm;
+    final IconDrawableFactory mDrawableFactory;
     final IPackageManager mIpm;
     final UserManager mUm;
     final StorageStatsManager mStats;
@@ -132,6 +134,7 @@
     private ApplicationsState(Application app) {
         mContext = app;
         mPm = mContext.getPackageManager();
+        mDrawableFactory = IconDrawableFactory.newInstance(mContext);
         mIpm = AppGlobals.getPackageManager();
         mUm = mContext.getSystemService(UserManager.class);
         mStats = mContext.getSystemService(StorageStatsManager.class);
@@ -327,7 +330,7 @@
             return;
         }
         synchronized (entry) {
-            entry.ensureIconLocked(mContext, mPm);
+            entry.ensureIconLocked(mContext, mDrawableFactory);
         }
     }
 
@@ -943,7 +946,7 @@
                             AppEntry entry = mAppEntries.get(i);
                             if (entry.icon == null || !entry.mounted) {
                                 synchronized (entry) {
-                                    if (entry.ensureIconLocked(mContext, mPm)) {
+                                    if (entry.ensureIconLocked(mContext, mDrawableFactory)) {
                                         if (!mRunning) {
                                             mRunning = true;
                                             Message m = mMainHandler.obtainMessage(
@@ -1266,10 +1269,10 @@
             }
         }
 
-        boolean ensureIconLocked(Context context, PackageManager pm) {
+        boolean ensureIconLocked(Context context, IconDrawableFactory drawableFactory) {
             if (this.icon == null) {
                 if (this.apkFile.exists()) {
-                    this.icon = getBadgedIcon(pm);
+                    this.icon = drawableFactory.getBadgedIcon(info);
                     return true;
                 } else {
                     this.mounted = false;
@@ -1281,19 +1284,13 @@
                 // its icon.
                 if (this.apkFile.exists()) {
                     this.mounted = true;
-                    this.icon = getBadgedIcon(pm);
+                    this.icon = drawableFactory.getBadgedIcon(info);
                     return true;
                 }
             }
             return false;
         }
 
-        private Drawable getBadgedIcon(PackageManager pm) {
-            // Do badging ourself so that it comes from the user of the app not the current user.
-            return pm.getUserBadgedIcon(pm.loadUnbadgedItemIcon(info, info),
-                    new UserHandle(UserHandle.getUserId(info.uid)));
-        }
-
         public String getVersion(Context context) {
             try {
                 return context.getPackageManager().getPackageInfo(info.packageName, 0).versionName;
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java
index 231fc69..3f826cc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java
+++ b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java
@@ -27,6 +27,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.util.IconDrawableFactory;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -48,10 +49,12 @@
 
     private final PackageManager mPackageManager;
     private final Context mContext;
+    private final IconDrawableFactory mDrawableFactory;
 
     public RecentLocationApps(Context context) {
         mContext = context;
         mPackageManager = context.getPackageManager();
+        mDrawableFactory = IconDrawableFactory.newInstance(context);
     }
 
     /**
@@ -146,8 +149,7 @@
             }
 
             final UserHandle userHandle = new UserHandle(userId);
-            Drawable appIcon = mPackageManager.getApplicationIcon(appInfo);
-            Drawable icon = mPackageManager.getUserBadgedIcon(appIcon, userHandle);
+            Drawable icon = mDrawableFactory.getBadgedIcon(appInfo, userId);
             CharSequence appLabel = mPackageManager.getApplicationLabel(appInfo);
             CharSequence badgedAppLabel = mPackageManager.getUserBadgedLabel(appLabel, userHandle);
             if (appLabel.toString().contentEquals(badgedAppLabel)) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 169a034..48429e8 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1109,6 +1109,33 @@
         // Retrieve the ssaid from the table if present.
         final Setting ssaid = mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_SSAID, owningUserId,
                 name);
+        // If the app is an Instant App use its stored SSAID instead of our own.
+        final String instantSsaid;
+        final long token = Binder.clearCallingIdentity();
+        try {
+            instantSsaid = mPackageManager.getInstantAppAndroidId(callingPkg.packageName,
+                    owningUserId);
+        } catch (RemoteException e) {
+            Slog.e(LOG_TAG, "Failed to get Instant App Android ID", e);
+            return null;
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+        if (instantSsaid != null) {
+            // Use the stored value if it is still valid.
+            if (ssaid != null && instantSsaid.equals(ssaid.getValue())) {
+                return ssaid;
+            }
+            // The value has changed, update the stored value.
+            final SettingsState ssaidSettings = mSettingsRegistry.getSettingsLocked(
+                    SETTINGS_TYPE_SSAID, owningUserId);
+            final boolean success = ssaidSettings.insertSettingLocked(name, instantSsaid, null,
+                    true, callingPkg.packageName);
+            if (!success) {
+                throw new IllegalStateException("Failed to update instant app android id");
+            }
+            return mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_SSAID, owningUserId, name);
+        }
 
         // Lazy initialize ssaid if not yet present in ssaid table.
         if (ssaid == null || ssaid.isNull() || ssaid.getValue() == null) {
diff --git a/packages/SystemUI/res/drawable/ic_info_outline.xml b/packages/SystemUI/res/drawable/ic_info_outline.xml
new file mode 100644
index 0000000..a4a3e9a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_info_outline.xml
@@ -0,0 +1,25 @@
+<!--
+  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
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="32dp"
+        android:height="32dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_pause_white_24dp.xml b/packages/SystemUI/res/drawable/ic_pause_white.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/ic_pause_white_24dp.xml
rename to packages/SystemUI/res/drawable/ic_pause_white.xml
diff --git a/packages/SystemUI/res/drawable/ic_play_arrow_white_24dp.xml b/packages/SystemUI/res/drawable/ic_play_arrow_white.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/ic_play_arrow_white_24dp.xml
rename to packages/SystemUI/res/drawable/ic_play_arrow_white.xml
diff --git a/packages/SystemUI/res/drawable/ic_skip_next_white.xml b/packages/SystemUI/res/drawable/ic_skip_next_white.xml
new file mode 100644
index 0000000..040c7e6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_skip_next_white.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2017 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z" />
+    <path
+        android:pathData="M0 0h24v24H0z" />
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_skip_previous_white.xml b/packages/SystemUI/res/drawable/ic_skip_previous_white.xml
new file mode 100644
index 0000000..b9b94b7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_skip_previous_white.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright (C) 2017 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M6 6h2v12H6zm3.5 6l8.5 6V6z" />
+    <path
+        android:pathData="M0 0h24v24H0z" />
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/notification_snooze.xml b/packages/SystemUI/res/layout/notification_snooze.xml
index 5bd64de..b70f24b 100644
--- a/packages/SystemUI/res/layout/notification_snooze.xml
+++ b/packages/SystemUI/res/layout/notification_snooze.xml
@@ -19,47 +19,56 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:minHeight="@dimen/snooze_snackbar_min_height"
-    android:id="@+id/notification_snooze"
-    android:clickable="true"
-    android:gravity="center_vertical"
-    android:orientation="horizontal"
-    android:paddingStart="24dp"
-    android:paddingEnd="24dp"
-    android:background="@color/snooze_snackbar_bg">
-    
-    <TextView
-        android:id="@+id/snooze_option_default"
-        style="@style/TextAppearance.SnoozeSnackBar"
-        android:layout_width="wrap_content"
-       	android:layout_height="match_parent"
-       	android:gravity="center_vertical"
-      	android:drawableTint="@android:color/white"
-       	android:drawableEnd="@drawable/notification_expand_more"/>
-    
-    <android.widget.Space
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        android:layout_weight="1"
-        />
-    
-    <TextView
-        android:id="@+id/undo"
-        style="@style/TextAppearance.SnoozeSnackBar.Button"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:layout_marginEnd="8dp"
-        android:layout_marginStart="8dp"
-        android:background="@drawable/btn_borderless_rect"
-        android:layout_gravity="end"
-        android:text="@string/snooze_undo" />
-    
+    android:orientation="vertical"
+    android:background="@color/notification_guts_bg_color"
+    android:theme="@*android:style/Theme.DeviceDefault.Light">
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:id="@+id/notification_snooze"
+        android:layout_height="@dimen/snooze_snackbar_min_height">
+
+        <TextView
+            android:id="@+id/snooze_option_default"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentStart="true"
+            android:layout_centerVertical="true"
+            android:paddingStart="16dp"
+            android:textColor="#DD000000"
+            android:paddingEnd="4dp"/>
+
+        <ImageView
+            android:id="@+id/expand_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_toEndOf="@+id/snooze_option_default"
+            android:layout_centerVertical="true"
+            android:paddingTop="1dp"
+            android:tint="#9E9E9E" />
+
+        <TextView
+            android:id="@+id/undo"
+            style="@style/TextAppearance.NotificationInfo.Button"
+            android:layout_width="wrap_content"
+            android:layout_height="36dp"
+            android:layout_marginEnd="8dp"
+            android:layout_alignParentEnd="true"
+            android:layout_centerVertical="true"
+            android:text="@string/snooze_undo" />
+    </RelativeLayout>
+
+    <View
+        android:id="@+id/divider"
+        android:layout_width="match_parent"
+        android:layout_height="0.5dp"
+        android:background="#9E9E9E" />
+
     <LinearLayout
         android:id="@+id/snooze_options"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:paddingTop="8dp"
         android:paddingBottom="8dp"
-        android:orientation="vertical"/>
-    
+        android:orientation="vertical" />
+
 </com.android.systemui.statusbar.NotificationSnooze>
diff --git a/packages/SystemUI/res/layout/notification_snooze_option.xml b/packages/SystemUI/res/layout/notification_snooze_option.xml
new file mode 100644
index 0000000..aaf45f3
--- /dev/null
+++ b/packages/SystemUI/res/layout/notification_snooze_option.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright 2017, 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.
+-->
+<TextView
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="48dp"
+        android:layout_marginStart="@*android:dimen/notification_content_margin_start"
+        android:layout_marginEnd="@*android:dimen/notification_content_margin_end"
+        android:gravity="center_vertical"
+        android:textSize="14sp"
+        android:textColor="#DD000000"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/quick_settings_footer.xml b/packages/SystemUI/res/layout/quick_settings_footer.xml
index 8667a5a..bb1b288 100644
--- a/packages/SystemUI/res/layout/quick_settings_footer.xml
+++ b/packages/SystemUI/res/layout/quick_settings_footer.xml
@@ -14,41 +14,31 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:clickable="true"
     android:paddingBottom="@dimen/qs_tile_padding_top"
-    android:paddingTop="@dimen/qs_tile_padding_top" >
+    android:paddingTop="@dimen/qs_tile_padding_top"
+    android:paddingStart="@dimen/qs_footer_padding_start"
+    android:paddingEnd="@dimen/qs_footer_padding_end"
+    android:gravity="center_vertical"
+    android:background="?android:attr/colorPrimaryDark" >
 
     <TextView
         android:id="@+id/footer_text"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_centerHorizontal="true"
-        android:textSize="@dimen/qs_tile_text_size" />
+        android:gravity="start"
+        android:layout_weight="1"
+        android:textAppearance="@style/TextAppearance.QS.TileLabel"
+        android:textColor="?android:attr/textColorSecondary"/>
 
     <ImageView
         android:id="@+id/footer_icon"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_centerVertical="true"
-        android:layout_marginEnd="8dp"
-        android:layout_toStartOf="@id/footer_text"
+        android:layout_width="@dimen/qs_footer_icon_size"
+        android:layout_height="@dimen/qs_footer_icon_size"
         android:contentDescription="@null"
-        android:src="@drawable/ic_qs_vpn"
-        android:visibility="invisible" />
+        android:src="@drawable/ic_info_outline" />
 
-    <!-- Only shown if both images are visible -->
-    <ImageView
-        android:id="@+id/footer_icon2"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_centerVertical="true"
-        android:layout_marginEnd="8dp"
-        android:layout_toStartOf="@id/footer_icon"
-        android:contentDescription="@null"
-        android:src="@drawable/ic_qs_network_logging"
-        android:visibility="invisible" />
-
-</RelativeLayout>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/tv_pip_controls.xml b/packages/SystemUI/res/layout/tv_pip_controls.xml
index c6bcd32..61ac6f6 100644
--- a/packages/SystemUI/res/layout/tv_pip_controls.xml
+++ b/packages/SystemUI/res/layout/tv_pip_controls.xml
@@ -40,7 +40,7 @@
         android:layout_width="100dp"
         android:layout_height="wrap_content"
         android:layout_marginStart="-50dp"
-        android:src="@drawable/ic_pause_white_24dp"
+        android:src="@drawable/ic_pause_white"
         android:text="@string/pip_pause"
         android:visibility="gone" />
 </merge>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index f461bb3..83a0b7b 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -96,10 +96,6 @@
     <!-- The "inside" of a notification, reached via longpress -->
     <color name="notification_guts_bg_color">#eeeeee</color>
 
-    <!-- Colors of the snooze menu reached via snooze icon behind a notification -->
-    <color name="snooze_snackbar_bg">#FF4A4A4A</color>
-	<color name="snooze_snackbar_text">#FFA6BAFF</color>
-
     <color name="assist_orb_color">#ffffff</color>
 
     <color name="keyguard_user_switcher_background_gradient_color">#77000000</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 9c7a6a0..4edadea 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -119,7 +119,7 @@
     <dimen name="notification_menu_icon_padding">20dp</dimen>
 
     <!-- The minimum height for the snackbar shown after the snooze option has been chosen. -->
-    <dimen name="snooze_snackbar_min_height">48dp</dimen>
+    <dimen name="snooze_snackbar_min_height">56dp</dimen>
 
     <!-- The text size of options in the snooze menu. -->
     <dimen name="snooze_option_text_size">14sp</dimen>
@@ -263,6 +263,9 @@
     <dimen name="qs_detail_item_icon_size">24dp</dimen>
     <dimen name="qs_detail_item_icon_marginStart">0dp</dimen>
     <dimen name="qs_detail_item_icon_marginEnd">20dp</dimen>
+    <dimen name="qs_footer_padding_start">16dp</dimen>
+    <dimen name="qs_footer_padding_end">24dp</dimen>
+    <dimen name="qs_footer_icon_size">16dp</dimen>
 
     <!-- Desired qs icon overlay size. -->
     <dimen name="qs_detail_icon_overlay_size">24dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 33b1dd4..c9da889 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -376,13 +376,13 @@
     <string name="accessibility_no_sim">No SIM.</string>
 
     <!-- Content description of the cell data. [CHAR LIMIT=NONE] -->
-    <string name="accessibility_cell_data">Cellular Data</string>
+    <string name="accessibility_cell_data">Mobile Data</string>
 
     <!-- Content description of the cell data being enabled. [CHAR LIMIT=NONE] -->
-    <string name="accessibility_cell_data_on">Cellular Data On</string>
+    <string name="accessibility_cell_data_on">Mobile Data On</string>
 
     <!-- Content description of the cell data being disabled. [CHAR LIMIT=NONE] -->
-    <string name="accessibility_cell_data_off">Cellular Data Off</string>
+    <string name="accessibility_cell_data_off">Mobile Data Off</string>
 
     <!-- Content description of the bluetooth tethering icon for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_bluetooth_tether">Bluetooth tethering.</string>
@@ -574,11 +574,11 @@
     <!-- Title of dialog shown when 4G data usage has exceeded limit and has been disabled. [CHAR LIMIT=48] -->
     <string name="data_usage_disabled_dialog_4g_title">4G data is paused</string>
     <!-- Title of dialog shown when mobile data usage has exceeded limit and has been disabled. [CHAR LIMIT=48] -->
-    <string name="data_usage_disabled_dialog_mobile_title">Cellular data is paused</string>
+    <string name="data_usage_disabled_dialog_mobile_title">Mobile data is paused</string>
     <!-- Title of dialog shown when data usage has exceeded limit and has been disabled. [CHAR LIMIT=48] -->
     <string name="data_usage_disabled_dialog_title">Data is paused</string>
     <!-- Body of dialog shown when data usage has exceeded limit and has been disabled. [CHAR LIMIT=NONE] -->
-    <string name="data_usage_disabled_dialog">The data limit you set has been reached. You are no longer using cellular data.\n\nIf you resume, charges may apply for data usage.</string>
+    <string name="data_usage_disabled_dialog">The data limit you set has been reached. You are no longer using mobile data.\n\nIf you resume, charges may apply for data usage.</string>
     <!-- Dialog button indicating that data connection should be re-enabled. [CHAR LIMIT=28] -->
     <string name="data_usage_disabled_dialog_enable">Resume</string>
 
@@ -749,7 +749,7 @@
     <!-- QuickSettings: Flashlight [CHAR LIMIT=NONE] -->
     <string name="quick_settings_flashlight_label">Flashlight</string>
     <!-- QuickSettings: Cellular detail panel title [CHAR LIMIT=NONE] -->
-    <string name="quick_settings_cellular_detail_title">Cellular data</string>
+    <string name="quick_settings_cellular_detail_title">Mobile data</string>
     <!-- QuickSettings: Cellular detail panel, data usage title [CHAR LIMIT=NONE] -->
     <string name="quick_settings_cellular_detail_data_usage">Data usage</string>
     <!-- QuickSettings: Cellular detail panel, remaining data title [CHAR LIMIT=NONE] -->
@@ -1502,8 +1502,6 @@
     <string name="snooze_option_30_min">30 minutes</string>
     <!-- Notification: Menu row: Snooze options: 1 hour option. [CHAR LIMIT=50]-->
     <string name="snooze_option_1_hour">1 hour</string>
-    <!-- Notification: Menu row: Snooze options: cancel snoozing option. [CHAR LIMIT=50] -->
-    <string name="snooze_option_dont_snooze">Cancel</string>
     <!-- Notification: Menu row: Snooze undo button label. [CHAR LIMIT=50]-->
     <string name="snooze_undo">UNDO</string>
 
@@ -1897,6 +1895,18 @@
     <!-- PiP minimize description. [CHAR LIMIT=NONE] -->
     <string name="pip_minimize_description" translatable="false">Drag or fling the PIP to the edges of the screen to minimize it.</string>
 
+    <!-- Button to play the current media on picture-in-picture (PIP) [CHAR LIMIT=30] -->
+    <string name="pip_play">Play</string>
+
+    <!-- Button to pause the current media on picture-in-picture (PIP) [CHAR LIMIT=30] -->
+    <string name="pip_pause">Pause</string>
+
+    <!-- Button to skip to the next media on picture-in-picture (PIP) [CHAR LIMIT=30] -->
+    <string name="pip_skip_to_next">Skip to next</string>
+
+    <!-- Button to skip to the prev media on picture-in-picture (PIP) [CHAR LIMIT=30] -->
+    <string name="pip_skip_to_prev">Skip to previous</string>
+
     <!-- Tuner string -->
     <string name="change_theme_reboot" translatable="false">Changing the theme requires a restart.</string>
     <!-- Tuner string -->
@@ -1904,11 +1914,11 @@
     <!-- Tuner string -->
     <string name="default_theme" translatable="false">Default</string>
 
-    <!-- Title for notification & dialog that the user's phone last shut down because it got too hot. [CHAR LIMIT=30] -->
+    <!-- Title for notification & dialog that the user's phone last shut down because it got too hot. [CHAR LIMIT=40] -->
     <string name="thermal_shutdown_title">Phone turned off due to heat</string>
     <!-- Message body for notification that user's phone last shut down because it got too hot. [CHAR LIMIT=100] -->
     <string name="thermal_shutdown_message">Your phone is now running normally</string>
-    <!-- Text body for dialog alerting user that their phone last shut down bewcause it got too hot. [CHAR LIMIT=300] -->
+    <!-- Text body for dialog alerting user that their phone last shut down because it got too hot. [CHAR LIMIT=450] -->
     <string name="thermal_shutdown_dialog_message">Your phone was too hot, so it turned off to cool down. Your phone is now running normally.\n\nYour phone may get too hot if you:\n\t&#8226; Use resource-intensive apps (such as gaming, video, or navigation apps)\n\t&#8226; Download or upload large files\n\t&#8226; Use your phone in high temperatures</string>
 
     <!-- Title for notification (and dialog) that user's phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=30] -->
diff --git a/packages/SystemUI/res/values/strings_tv.xml b/packages/SystemUI/res/values/strings_tv.xml
index 41626fc..e578068 100644
--- a/packages/SystemUI/res/values/strings_tv.xml
+++ b/packages/SystemUI/res/values/strings_tv.xml
@@ -23,8 +23,4 @@
     <string name="pip_close">Close PIP</string>
     <!-- Button to move picture-in-picture (PIP) screen to the fullscreen in PIP menu [CHAR LIMIT=30] -->
     <string name="pip_fullscreen">Full screen</string>
-    <!-- Button to play the current media on picture-in-picture (PIP) [CHAR LIMIT=30] -->
-    <string name="pip_play">Play</string>
-    <!-- Button to pause the current media on picture-in-picture (PIP) [CHAR LIMIT=30] -->
-    <string name="pip_pause">Pause</string>
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index e5f04c6..dbdbd1e 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -386,20 +386,6 @@
         <item name="android:paddingEnd">8dp</item>
     </style>
 
-    <style name="TextAppearance.SnoozeSnackBar">
-        <item name="android:textSize">14sp</item>
-        <item name="android:fontFamily">sans-serif</item>
-        <item name="android:textColor">@android:color/white</item>
-    </style>
-
-    <style name="TextAppearance.SnoozeSnackBar.Button">
-        <item name="android:textSize">14sp</item>
-        <item name="android:textAllCaps">true</item>
-        <item name="android:fontFamily">sans-serif-medium</item>
-        <item name="android:gravity">center</item>
-        <item name="android:textColor">@color/snooze_snackbar_text</item>
-    </style>
-
     <style name="edit_theme" parent="@*android:style/Theme.DeviceDefault.QuickSettings">
         <item name="android:colorBackground">?android:attr/colorSecondary</item>
     </style>
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 5a04108d..8c4159a 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -462,11 +462,20 @@
         int duration = SNAP_ANIM_LEN;
         anim.setDuration(duration);
         anim.addListener(new AnimatorListenerAdapter() {
+            boolean wasCancelled = false;
+
+            @Override
+            public void onAnimationCancel(Animator animator) {
+                wasCancelled = true;
+            }
+
             @Override
             public void onAnimationEnd(Animator animator) {
                 mSnappingChild = false;
-                updateSwipeProgressFromOffset(animView, canBeDismissed);
-                mCallback.onChildSnappedBack(animView, targetLeft);
+                if (!wasCancelled) {
+                    updateSwipeProgressFromOffset(animView, canBeDismissed);
+                    mCallback.onChildSnappedBack(animView, targetLeft);
+                }
             }
         });
         prepareSnapBackAnimation(animView, anim);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
index 86bb0de..4b3cdfb 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
@@ -39,7 +39,8 @@
     // This delay controls how long to wait before we show the target when the user first moves
     // the PIP, to prevent the target from animating if the user just wants to fling the PIP
     private static final int SHOW_TARGET_DELAY = 100;
-    private static final int SHOW_TARGET_DURATION = 200;
+    private static final int SHOW_TARGET_DURATION = 350;
+    private static final int HIDE_TARGET_DURATION = 225;
 
     private Context mContext;
     private WindowManager mWindowManager;
@@ -96,7 +97,7 @@
     public void showDismissTarget() {
         mDismissView.animate()
                 .alpha(1f)
-                .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
+                .setInterpolator(Interpolators.LINEAR)
                 .setStartDelay(SHOW_TARGET_DELAY)
                 .setDuration(SHOW_TARGET_DURATION)
                 .start();
@@ -109,9 +110,9 @@
         if (mDismissView != null) {
             mDismissView.animate()
                     .alpha(0f)
-                    .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN)
+                    .setInterpolator(Interpolators.LINEAR)
                     .setStartDelay(0)
-                    .setDuration(SHOW_TARGET_DURATION)
+                    .setDuration(HIDE_TARGET_DURATION)
                     .withEndAction(new Runnable() {
                         @Override
                         public void run() {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java
index 3a4caa9..62ec09b 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java
@@ -48,6 +48,8 @@
 
     private static final String ACTION_PLAY = "com.android.systemui.pip.phone.PLAY";
     private static final String ACTION_PAUSE = "com.android.systemui.pip.phone.PAUSE";
+    private static final String ACTION_NEXT = "com.android.systemui.pip.phone.NEXT";
+    private static final String ACTION_PREV = "com.android.systemui.pip.phone.PREV";
 
     /**
      * A listener interface to receive notification on changes to the media actions.
@@ -67,6 +69,8 @@
 
     private RemoteAction mPauseAction;
     private RemoteAction mPlayAction;
+    private RemoteAction mNextAction;
+    private RemoteAction mPrevAction;
 
     private BroadcastReceiver mPlayPauseActionReceiver = new BroadcastReceiver() {
         @Override
@@ -76,6 +80,10 @@
                 mMediaController.getTransportControls().play();
             } else if (action.equals(ACTION_PAUSE)) {
                 mMediaController.getTransportControls().pause();
+            } else if (action.equals(ACTION_NEXT)) {
+                mMediaController.getTransportControls().skipToNext();
+            } else if (action.equals(ACTION_PREV)) {
+                mMediaController.getTransportControls().skipToPrevious();
             }
         }
     };
@@ -95,6 +103,8 @@
         IntentFilter mediaControlFilter = new IntentFilter();
         mediaControlFilter.addAction(ACTION_PLAY);
         mediaControlFilter.addAction(ACTION_PAUSE);
+        mediaControlFilter.addAction(ACTION_NEXT);
+        mediaControlFilter.addAction(ACTION_PREV);
         mContext.registerReceiver(mPlayPauseActionReceiver, mediaControlFilter);
 
         createMediaActions();
@@ -143,11 +153,21 @@
         int state = mMediaController.getPlaybackState().getState();
         boolean isPlaying = MediaSession.isActiveState(state);
         long actions = mMediaController.getPlaybackState().getActions();
+
+        // Prev action
+        mPrevAction.setEnabled((actions & PlaybackState.ACTION_SKIP_TO_PREVIOUS) != 0);
+        mediaActions.add(mPrevAction);
+
+        // Play/pause action
         if (!isPlaying && ((actions & PlaybackState.ACTION_PLAY) != 0)) {
             mediaActions.add(mPlayAction);
         } else if (isPlaying && ((actions & PlaybackState.ACTION_PAUSE) != 0)) {
             mediaActions.add(mPauseAction);
         }
+
+        // Next action
+        mNextAction.setEnabled((actions & PlaybackState.ACTION_SKIP_TO_NEXT) != 0);
+        mediaActions.add(mNextAction);
         return mediaActions;
     }
 
@@ -157,15 +177,27 @@
     private void createMediaActions() {
         String pauseDescription = mContext.getString(R.string.pip_pause);
         mPauseAction = new RemoteAction(Icon.createWithResource(mContext,
-                R.drawable.ic_pause_white_24dp), pauseDescription, pauseDescription,
+                R.drawable.ic_pause_white), pauseDescription, pauseDescription,
                         PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_PAUSE),
                                 FLAG_UPDATE_CURRENT));
 
         String playDescription = mContext.getString(R.string.pip_play);
         mPlayAction = new RemoteAction(Icon.createWithResource(mContext,
-                R.drawable.ic_play_arrow_white_24dp), playDescription, playDescription,
+                R.drawable.ic_play_arrow_white), playDescription, playDescription,
                         PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_PLAY),
                                 FLAG_UPDATE_CURRENT));
+
+        String nextDescription = mContext.getString(R.string.pip_skip_to_next);
+        mNextAction = new RemoteAction(Icon.createWithResource(mContext,
+                R.drawable.ic_skip_next_white), nextDescription, nextDescription,
+                        PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_NEXT),
+                                FLAG_UPDATE_CURRENT));
+
+        String prevDescription = mContext.getString(R.string.pip_skip_to_prev);
+        mPrevAction = new RemoteAction(Icon.createWithResource(mContext,
+                R.drawable.ic_skip_previous_white), prevDescription, prevDescription,
+                        PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_PREV),
+                                FLAG_UPDATE_CURRENT));
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index 982b808..79ac816 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -412,16 +412,26 @@
         } else {
             actionsContainer.setVisibility(View.VISIBLE);
             if (mActionsGroup != null) {
-                mActionsGroup.removeAllViews();
+                // Hide extra views
+                for (int i = mActions.size(); i < mActionsGroup.getChildCount(); i++) {
+                    mActionsGroup.getChildAt(i).setVisibility(View.GONE);
+                }
+                // Add needed views
+                final LayoutInflater inflater = LayoutInflater.from(this);
+                while (mActionsGroup.getChildCount() < mActions.size()) {
+                    final ImageView actionView = (ImageView) inflater.inflate(
+                            R.layout.pip_menu_action, mActionsGroup, false);
+                    mActionsGroup.addView(actionView);
+                }
 
                 // Recreate the layout
                 final boolean isLandscapePip = stackBounds != null &&
                         (stackBounds.width() > stackBounds.height());
-                final LayoutInflater inflater = LayoutInflater.from(this);
                 for (int i = 0; i < mActions.size(); i++) {
                     final RemoteAction action = mActions.get(i);
-                    final ImageView actionView = (ImageView) inflater.inflate(
-                            R.layout.pip_menu_action, mActionsGroup, false);
+                    final ImageView actionView = (ImageView) mActionsGroup.getChildAt(i);
+
+                    // TODO: Check if the action drawable has changed before we reload it
                     action.getIcon().loadDrawableAsync(this, d -> {
                         d.setTint(Color.WHITE);
                         actionView.setImageDrawable(d);
@@ -439,12 +449,11 @@
                         actionView.setAlpha(DISABLED_ACTION_ALPHA);
                         actionView.setEnabled(false);
                     }
-                    if (isLandscapePip && i > 0) {
-                        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
-                                actionView.getLayoutParams();
-                        lp.leftMargin = mBetweenActionPaddingLand;
-                    }
-                    mActionsGroup.addView(actionView);
+
+                    // Update the margin between actions
+                    LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
+                            actionView.getLayoutParams();
+                    lp.leftMargin = (isLandscapePip && i > 0) ? mBetweenActionPaddingLand : 0;
                 }
             }
 
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index a60ecf7..3223f9f 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -63,7 +63,7 @@
     private static final int METRIC_VALUE_DISMISSED_BY_TAP = 0;
     private static final int METRIC_VALUE_DISMISSED_BY_DRAG = 1;
 
-    private static final int SHOW_DISMISS_AFFORDANCE_DELAY = 200;
+    private static final int SHOW_DISMISS_AFFORDANCE_DELAY = 225;
 
     // Allow dragging the PIP to a location to close it
     private static final boolean ENABLE_DISMISS_DRAG_TO_EDGE = true;
@@ -78,6 +78,7 @@
     private final PipDismissViewController mDismissViewController;
     private final PipSnapAlgorithm mSnapAlgorithm;
     private final AccessibilityManager mAccessibilityManager;
+    private boolean mShowPipMenuOnAnimationEnd = false;
 
     // The current movement bounds
     private Rect mMovementBounds = new Rect();
@@ -221,13 +222,18 @@
             setMinimizedStateInternal(false);
         }
         mDismissViewController.destroyDismissTarget();
-        mMenuController.showMenu(MENU_STATE_CLOSE, mMotionHelper.getBounds(),
-                mMovementBounds, true /* allowMenuTimeout */);
+        mShowPipMenuOnAnimationEnd = true;
     }
 
     public void onPinnedStackAnimationEnded() {
         // Always synchronize the motion helper bounds once PiP animations finish
         mMotionHelper.synchronizePinnedStackBounds();
+
+        if (mShowPipMenuOnAnimationEnd) {
+            mMenuController.showMenu(MENU_STATE_CLOSE, mMotionHelper.getBounds(),
+                    mMovementBounds, true /* allowMenuTimeout */);
+            mShowPipMenuOnAnimationEnd = false;
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java
index 4c81907..acea3b6 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java
@@ -186,10 +186,10 @@
         } else {
             mPlayPauseButtonView.setVisibility(View.VISIBLE);
             if (state == PipManager.PLAYBACK_STATE_PLAYING) {
-                mPlayPauseButtonView.setImageResource(R.drawable.ic_pause_white_24dp);
+                mPlayPauseButtonView.setImageResource(R.drawable.ic_pause_white);
                 mPlayPauseButtonView.setText(R.string.pip_pause);
             } else {
-                mPlayPauseButtonView.setImageResource(R.drawable.ic_play_arrow_white_24dp);
+                mPlayPauseButtonView.setImageResource(R.drawable.ic_play_arrow_white);
                 mPlayPauseButtonView.setText(R.string.pip_play);
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 38485c7..f5e096eb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -98,9 +98,6 @@
 
         setupTileLayout();
 
-        mFooter = new QSSecurityFooter(this, context);
-        addView(mFooter.getView());
-
         mPageIndicator = LayoutInflater.from(context).inflate(
                 R.layout.qs_page_indicator, this, false);
         addView(mPageIndicator);
@@ -110,6 +107,9 @@
 
         addDivider();
 
+        mFooter = new QSSecurityFooter(this, context);
+        addView(mFooter.getView());
+
         updateResources();
 
         mBrightnessController = new BrightnessController(getContext(),
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index 5b9d95d..51c6ad8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -1,4 +1,3 @@
-
 /*
  * Copyright (C) 2014 The Android Open Source Project
  *
@@ -51,24 +50,21 @@
     private final View mRootView;
     private final TextView mFooterText;
     private final ImageView mFooterIcon;
-    private final ImageView mFooterIcon2;
     private final Context mContext;
     private final Callback mCallback = new Callback();
     private final SecurityController mSecurityController;
     private final ActivityStarter mActivityStarter;
     private final Handler mMainHandler;
+    private final View mDivider;
 
     private AlertDialog mDialog;
     private QSTileHost mHost;
     protected H mHandler;
 
     private boolean mIsVisible;
-    private boolean mIsIconVisible;
-    private boolean mIsIcon2Visible;
     private CharSequence mFooterTextContent = null;
     private int mFooterTextId;
     private int mFooterIconId;
-    private int mFooterIcon2Id;
 
     public QSSecurityFooter(QSPanel qsPanel, Context context) {
         mRootView = LayoutInflater.from(context)
@@ -76,14 +72,13 @@
         mRootView.setOnClickListener(this);
         mFooterText = (TextView) mRootView.findViewById(R.id.footer_text);
         mFooterIcon = (ImageView) mRootView.findViewById(R.id.footer_icon);
-        mFooterIcon2 = (ImageView) mRootView.findViewById(R.id.footer_icon2);
-        mFooterIconId = R.drawable.ic_qs_vpn;
-        mFooterIcon2Id = R.drawable.ic_qs_network_logging;
+        mFooterIconId = R.drawable.ic_info_outline;
         mContext = context;
         mMainHandler = new Handler(Looper.getMainLooper());
         mActivityStarter = Dependency.get(ActivityStarter.class);
         mSecurityController = Dependency.get(SecurityController.class);
         mHandler = new H(Dependency.get(Dependency.BG_LOOPER));
+        mDivider = qsPanel == null ? null : qsPanel.getDivider();
     }
 
     public void setHostEnvironment(QSTileHost host) {
@@ -130,45 +125,102 @@
     }
 
     private void handleRefreshState() {
-        boolean isVpnEnabled = mSecurityController.isVpnEnabled();
-        boolean isNetworkLoggingEnabled = mSecurityController.isNetworkLoggingEnabled();
-        mIsIconVisible = isVpnEnabled || isNetworkLoggingEnabled;
-        mIsIcon2Visible = isVpnEnabled && isNetworkLoggingEnabled;
-        if (mSecurityController.isDeviceManaged()) {
-            final CharSequence organizationName =
-                    mSecurityController.getDeviceOwnerOrganizationName();
-            if (organizationName != null) {
-                mFooterTextContent = mContext.getResources().getString(
-                        R.string.do_disclosure_with_name, organizationName);
-            } else {
-                mFooterTextContent =
-                        mContext.getResources().getString(R.string.do_disclosure_generic);
-            }
-            mIsVisible = true;
-            int footerIconId = isVpnEnabled
-                    ? R.drawable.ic_qs_vpn
-                    : R.drawable.ic_qs_network_logging;
-            if (mFooterIconId != footerIconId) {
-                mFooterIconId = footerIconId;
-                mMainHandler.post(mUpdateIcon);
-            }
-        } else {
-            boolean isBranded = mSecurityController.isVpnBranded();
-            mFooterTextContent = mContext.getResources().getText(
-                    isBranded ? R.string.branded_vpn_footer : R.string.vpn_footer);
-            // Update the VPN footer icon, if needed.
-            int footerIconId = isVpnEnabled
-                    ? (isBranded ? R.drawable.ic_qs_branded_vpn : R.drawable.ic_qs_vpn)
-                    : R.drawable.ic_qs_network_logging;
-            if (mFooterIconId != footerIconId) {
-                mFooterIconId = footerIconId;
-                mMainHandler.post(mUpdateIcon);
-            }
-            mIsVisible = mIsIconVisible;
+        final boolean isDeviceManaged = mSecurityController.isDeviceManaged();
+        final boolean hasWorkProfile = mSecurityController.hasWorkProfile();
+        final boolean hasCACerts = mSecurityController.hasCACertInCurrentUser();
+        final boolean hasCACertsInWorkProfile = mSecurityController.hasCACertInWorkProfile();
+        final boolean isNetworkLoggingEnabled = mSecurityController.isNetworkLoggingEnabled();
+        final String vpnName = mSecurityController.getPrimaryVpnName();
+        final String vpnNameWorkProfile = mSecurityController.getWorkProfileVpnName();
+        final CharSequence organizationName = mSecurityController.getDeviceOwnerOrganizationName();
+        final CharSequence workProfileName = mSecurityController.getWorkProfileOrganizationName();
+        // Update visibility of footer
+        mIsVisible = isDeviceManaged || hasCACerts || hasCACertsInWorkProfile ||
+            vpnName != null || vpnNameWorkProfile != null;
+        // Update the string
+        mFooterTextContent = getFooterText(isDeviceManaged, hasWorkProfile,
+                hasCACerts, hasCACertsInWorkProfile, isNetworkLoggingEnabled, vpnName,
+                vpnNameWorkProfile, organizationName, workProfileName);
+        // Update the icon
+        int footerIconId = vpnName != null || vpnNameWorkProfile != null
+                ? R.drawable.ic_qs_vpn
+                : R.drawable.ic_info_outline;
+        if (mFooterIconId != footerIconId) {
+            mFooterIconId = footerIconId;
+            mMainHandler.post(mUpdateIcon);
         }
         mMainHandler.post(mUpdateDisplayState);
     }
 
+    protected CharSequence getFooterText(boolean isDeviceManaged, boolean hasWorkProfile,
+            boolean hasCACerts, boolean hasCACertsInWorkProfile, boolean isNetworkLoggingEnabled,
+            String vpnName, String vpnNameWorkProfile, CharSequence organizationName,
+            CharSequence workProfileName) {
+        if (isDeviceManaged) {
+            if (hasCACerts || hasCACertsInWorkProfile || isNetworkLoggingEnabled) {
+                if (organizationName == null) {
+                    return mContext.getString(
+                            R.string.quick_settings_disclosure_management_monitoring);
+                }
+                return mContext.getString(
+                        R.string.quick_settings_disclosure_named_management_monitoring,
+                        organizationName);
+            }
+            if (vpnName != null && vpnNameWorkProfile != null) {
+                if (organizationName == null) {
+                    return mContext.getString(R.string.quick_settings_disclosure_management_vpns);
+                }
+                return mContext.getString(R.string.quick_settings_disclosure_named_management_vpns,
+                        organizationName);
+            }
+            if (vpnName != null || vpnNameWorkProfile != null) {
+                if (organizationName == null) {
+                    return mContext.getString(
+                            R.string.quick_settings_disclosure_management_named_vpn,
+                            vpnName != null ? vpnName : vpnNameWorkProfile);
+                }
+                return mContext.getString(
+                        R.string.quick_settings_disclosure_named_management_named_vpn,
+                        organizationName,
+                        vpnName != null ? vpnName : vpnNameWorkProfile);
+            }
+            if (organizationName == null) {
+                return mContext.getString(R.string.quick_settings_disclosure_management);
+            }
+            return mContext.getString(R.string.quick_settings_disclosure_named_management,
+                    organizationName);
+        } // end if(isDeviceManaged)
+        if (hasCACertsInWorkProfile) {
+            if (workProfileName == null) {
+                return mContext.getString(
+                        R.string.quick_settings_disclosure_managed_profile_monitoring);
+            }
+            return mContext.getString(
+                    R.string.quick_settings_disclosure_named_managed_profile_monitoring,
+                    workProfileName);
+        }
+        if (hasCACerts) {
+            return mContext.getString(R.string.quick_settings_disclosure_monitoring);
+        }
+        if (vpnName != null && vpnNameWorkProfile != null) {
+            return mContext.getString(R.string.quick_settings_disclosure_vpns);
+        }
+        if (vpnNameWorkProfile != null) {
+            return mContext.getString(R.string.quick_settings_disclosure_managed_profile_named_vpn,
+                    vpnNameWorkProfile);
+        }
+        if (vpnName != null) {
+            if (hasWorkProfile) {
+                return mContext.getString(
+                        R.string.quick_settings_disclosure_personal_profile_named_vpn,
+                        vpnName);
+            }
+            return mContext.getString(R.string.quick_settings_disclosure_named_vpn,
+                    vpnName);
+        }
+        return null;
+    }
+
     @Override
     public void onClick(DialogInterface dialog, int which) {
         if (which == DialogInterface.BUTTON_NEGATIVE) {
@@ -182,18 +234,15 @@
         final String profileOwnerPackage = mSecurityController.getProfileOwnerName();
         final boolean isNetworkLoggingEnabled = mSecurityController.isNetworkLoggingEnabled();
         final String primaryVpn = mSecurityController.getPrimaryVpnName();
-        final String profileVpn = mSecurityController.getProfileVpnName();
+        final String profileVpn = mSecurityController.getWorkProfileVpnName();
         final CharSequence deviceOwnerOrganization =
                 mSecurityController.getDeviceOwnerOrganizationName();
         boolean hasProfileOwner = mSecurityController.hasProfileOwner();
-        boolean isBranded = deviceOwnerPackage == null && mSecurityController.isVpnBranded();
 
         mDialog = new SystemUIDialog(mContext);
-        if (!isBranded) {
-            mDialog.setTitle(getTitle(deviceOwnerPackage));
-        }
+        mDialog.setTitle(getTitle(deviceOwnerPackage));
         CharSequence msg = getMessage(deviceOwnerPackage, profileOwnerPackage, primaryVpn,
-                profileVpn, deviceOwnerOrganization, hasProfileOwner, isBranded);
+                profileVpn, deviceOwnerOrganization, hasProfileOwner);
         if (deviceOwnerPackage == null) {
             mDialog.setMessage(msg);
             if (mSecurityController.isVpnEnabled() && !mSecurityController.isVpnRestricted()) {
@@ -235,7 +284,7 @@
             }
         }
 
-        mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(isBranded), this);
+        mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(), this);
         mDialog.show();
         mDialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
     }
@@ -244,13 +293,13 @@
         return mContext.getString(R.string.status_bar_settings_settings_button);
     }
 
-    private String getPositiveButton(boolean isBranded) {
-        return mContext.getString(isBranded ? android.R.string.ok : R.string.quick_settings_done);
+    private String getPositiveButton() {
+        return mContext.getString(R.string.quick_settings_done);
     }
 
     protected CharSequence getMessage(String deviceOwnerPackage, String profileOwnerPackage,
             String primaryVpn, String profileVpn, CharSequence deviceOwnerOrganization,
-            boolean hasProfileOwner, boolean isBranded) {
+            boolean hasProfileOwner) {
         if (deviceOwnerPackage != null) {
             final SpannableStringBuilder message = new SpannableStringBuilder();
             if (deviceOwnerOrganization != null) {
@@ -273,13 +322,8 @@
                 return mContext.getString(R.string.monitoring_description_app_personal_work,
                         profileOwnerPackage, profileVpn, primaryVpn);
             } else {
-                if (isBranded) {
-                    return mContext.getString(R.string.branded_monitoring_description_app_personal,
-                            primaryVpn);
-                } else {
-                    return mContext.getString(R.string.monitoring_description_app_personal,
-                            primaryVpn);
-                }
+                return mContext.getString(R.string.monitoring_description_app_personal,
+                        primaryVpn);
             }
         } else if (profileVpn != null) {
             return mContext.getString(R.string.monitoring_description_app_work,
@@ -305,7 +349,6 @@
         @Override
         public void run() {
             mFooterIcon.setImageResource(mFooterIconId);
-            mFooterIcon2.setImageResource(mFooterIcon2Id);
         }
     };
 
@@ -316,8 +359,7 @@
                 mFooterText.setText(mFooterTextContent);
             }
             mRootView.setVisibility(mIsVisible ? View.VISIBLE : View.GONE);
-            mFooterIcon.setVisibility(mIsIconVisible ? View.VISIBLE : View.INVISIBLE);
-            mFooterIcon2.setVisibility(mIsIcon2Visible ? View.VISIBLE : View.INVISIBLE);
+            if (mDivider != null) mDivider.setVisibility(mIsVisible ? View.GONE : View.VISIBLE);
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 6781c16..976efb2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -340,12 +340,12 @@
         switch (state) {
             case Tile.STATE_UNAVAILABLE:
                 return Utils.getDisabled(context,
-                        Utils.getColorAttr(context, android.R.attr.textColorTertiary));
+                        Utils.getColorAttr(context, android.R.attr.textColorPrimary));
             case Tile.STATE_INACTIVE:
                 return Utils.getDisabled(context,
-                        Utils.getColorAttr(context, android.R.attr.textColorSecondary));
+                        Utils.getColorAttr(context, android.R.attr.colorForeground));
             case Tile.STATE_ACTIVE:
-                return Utils.getColorAttr(context, attr.textColorSecondary);
+                return Utils.getColorAttr(context, android.R.attr.textColorPrimary);
             default:
                 Log.e("QSTile", "Invalid state " + state);
                 return 0;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index d27bb85..9b20a7a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -259,22 +259,27 @@
             ArrayList<Item> items = new ArrayList<Item>();
             final Collection<CachedBluetoothDevice> devices = mController.getDevices();
             if (devices != null) {
+                int connectedDevices = 0;
                 for (CachedBluetoothDevice device : devices) {
                     if (device.getBondState() == BluetoothDevice.BOND_NONE) continue;
                     final Item item = new Item();
                     item.icon = R.drawable.ic_qs_bluetooth_on;
                     item.line1 = device.getName();
+                    item.tag = device;
                     int state = device.getMaxConnectionState();
                     if (state == BluetoothProfile.STATE_CONNECTED) {
                         item.icon = R.drawable.ic_qs_bluetooth_connected;
                         item.line2 = mContext.getString(R.string.quick_settings_connected);
                         item.canDisconnect = true;
+                        items.add(connectedDevices, item);
+                        connectedDevices++;
                     } else if (state == BluetoothProfile.STATE_CONNECTING) {
                         item.icon = R.drawable.ic_qs_bluetooth_connecting;
                         item.line2 = mContext.getString(R.string.quick_settings_connecting);
+                        items.add(connectedDevices, item);
+                    } else {
+                        items.add(item);
                     }
-                    item.tag = device;
-                    items.add(item);
                 }
             }
             mItems.setItems(items.toArray(new Item[items.size()]));
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 ccebd7c..429ace6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -65,7 +65,7 @@
 import android.os.UserManager;
 import android.provider.Settings;
 import android.util.ArraySet;
-import android.util.LauncherIcons;
+import android.util.IconDrawableFactory;
 import android.util.Log;
 import android.util.MutableBoolean;
 import android.view.Display;
@@ -121,6 +121,7 @@
     ActivityManager mAm;
     IActivityManager mIam;
     PackageManager mPm;
+    IconDrawableFactory mDrawableFactory;
     IPackageManager mIpm;
     AssistUtils mAssistUtils;
     WindowManager mWm;
@@ -139,7 +140,6 @@
     int mDummyThumbnailHeight;
     Paint mBgProtectionPaint;
     Canvas mBgProtectionCanvas;
-    LauncherIcons mLauncherIcons;
 
     private final Handler mHandler = new H();
 
@@ -258,6 +258,7 @@
         mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
         mIam = ActivityManager.getService();
         mPm = context.getPackageManager();
+        mDrawableFactory = IconDrawableFactory.newInstance(context);
         mIpm = AppGlobals.getPackageManager();
         mAssistUtils = new AssistUtils(context);
         mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
@@ -296,8 +297,6 @@
 
         Collections.addAll(sRecentsBlacklist,
                 res.getStringArray(R.array.recents_blacklist_array));
-
-        mLauncherIcons = new LauncherIcons(context);
     }
 
     /**
@@ -808,13 +807,19 @@
      * Returns the content description for a given task, badging it if necessary.  The content
      * description joins the app and activity labels.
      */
-    public String getBadgedContentDescription(ActivityInfo info, int userId, Resources res) {
+    public String getBadgedContentDescription(ActivityInfo info, int userId,
+            ActivityManager.TaskDescription td, Resources res) {
         // If we are mocking, then return a mock label
         if (RecentsDebugFlags.Static.EnableMockTasks) {
             return "Recent Task Content Description: " + userId;
         }
 
-        String activityLabel = info.loadLabel(mPm).toString();
+        String activityLabel;
+        if (td != null && td.getLabel() != null) {
+            activityLabel = td.getLabel();
+        } else {
+            activityLabel = info.loadLabel(mPm).toString();
+        }
         String applicationLabel = info.applicationInfo.loadLabel(mPm).toString();
         String badgedApplicationLabel = getBadgedLabel(applicationLabel, userId);
         return applicationLabel.equals(activityLabel) ? badgedApplicationLabel
@@ -834,8 +839,7 @@
             return new ColorDrawable(0xFF666666);
         }
 
-        Drawable icon = mLauncherIcons.wrapIconDrawableWithShadow(info.loadIcon(mPm));
-        return getBadgedIcon(icon, userId);
+        return mDrawableFactory.getBadgedIcon(info, info.applicationInfo, userId);
     }
 
     /**
@@ -850,8 +854,7 @@
             return new ColorDrawable(0xFF666666);
         }
 
-        Drawable icon = mLauncherIcons.wrapIconDrawableWithShadow(appInfo.loadIcon(mPm));
-        return getBadgedIcon(icon, userId);
+        return mDrawableFactory.getBadgedIcon(appInfo, userId);
     }
 
     /**
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 5c25bfd..78c71a1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -182,7 +182,8 @@
             // Load the title, icon, and color
             ActivityInfo info = loader.getAndUpdateActivityInfo(taskKey);
             String title = loader.getAndUpdateActivityTitle(taskKey, t.taskDescription);
-            String titleDescription = loader.getAndUpdateContentDescription(taskKey, res);
+            String titleDescription = loader.getAndUpdateContentDescription(taskKey,
+                    t.taskDescription, res);
             String dismissDescription = String.format(dismissDescFormat, titleDescription);
             String appInfoDescription = String.format(appInfoDescFormat, titleDescription);
             Drawable icon = isStackTask
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 e8ffb91..5e78b61 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -449,7 +449,8 @@
      * Returns the cached task content description if the task key is not expired, updating the
      * cache if it is.
      */
-    String getAndUpdateContentDescription(Task.TaskKey taskKey, Resources res) {
+    String getAndUpdateContentDescription(Task.TaskKey taskKey, ActivityManager.TaskDescription td,
+            Resources res) {
         SystemServicesProxy ssp = Recents.getSystemServices();
 
         // Return the cached content description if it exists
@@ -461,8 +462,15 @@
         // All short paths failed, load the label from the activity info and cache it
         ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey);
         if (activityInfo != null) {
-            label = ssp.getBadgedContentDescription(activityInfo, taskKey.userId, res);
-            mContentDescriptionCache.put(taskKey, label);
+            label = ssp.getBadgedContentDescription(activityInfo, taskKey.userId, td, res);
+            if (td == null) {
+                // Only add to the cache if the task description is null, otherwise, it is possible
+                // for the task description to change between calls without the last active time
+                // changing (ie. between preloading and Overview starting) which would lead to stale
+                // content descriptions
+                // TODO: Investigate improving this
+                mContentDescriptionCache.put(taskKey, label);
+            }
             return label;
         }
         // If the content description does not exist, return an empty label for now, but do not
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index cc91753..dff09bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -1494,7 +1494,7 @@
             return getActualHeight();
         }
         if (mGuts != null && mGuts.isExposed()) {
-            return mGuts.getHeight();
+            return mGuts.getIntrinsicHeight();
         } else if ((isChildInGroup() && !isGroupExpanded())) {
             return mPrivateLayout.getMinHeight();
         } else if (mShowAmbient) {
@@ -1835,7 +1835,9 @@
 
     @Override
     public int getMinHeight() {
-        if (!mOnKeyguard && mIsHeadsUp && mHeadsUpManager.isTrackingHeadsUp()) {
+        if (mGuts != null && mGuts.isExposed()) {
+            return mGuts.getIntrinsicHeight();
+        } else if (!mOnKeyguard && mIsHeadsUp && mHeadsUpManager.isTrackingHeadsUp()) {
                 return getPinnedHeadsUpHeight(false /* atLeastMinHeight */);
         } else if (mIsSummaryWithChildren && !isGroupExpanded() && !mShowingPublic) {
             return mChildrenContainer.getMinHeight();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index 0a99ee6..1375310 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -74,7 +74,8 @@
     private Handler mHandler;
     private Runnable mFalsingCheck;
     private boolean mNeedsFalsingProtection;
-    private OnGutsClosedListener mListener;
+    private OnGutsClosedListener mClosedListener;
+    private OnHeightChangedListener mHeightListener;
 
     private GutsContent mGutsContent;
 
@@ -88,6 +89,11 @@
         public View getContentView();
 
         /**
+         * @return the actual height of the content.
+         */
+        public int getActualHeight();
+
+        /**
          * Called when the guts view have been told to close, typically after an outside
          * interaction. Returning {@code true} here will prevent the guts view to close.
          */
@@ -103,6 +109,10 @@
         public void onGutsClosed(NotificationGuts guts);
     }
 
+    public interface OnHeightChangedListener {
+        public void onHeightChanged(NotificationGuts guts);
+    }
+
     interface OnSettingsClickListener {
         void onClick(View v, int appUid);
     }
@@ -126,7 +136,6 @@
 
     public NotificationGuts(Context context) {
         this(context, null);
-
     }
 
     public void setGutsContent(GutsContent content) {
@@ -190,8 +199,8 @@
 
     public void closeControls(int x, int y, boolean save) {
         if (getWindowToken() == null) {
-            if (mListener != null) {
-                mListener.onGutsClosed(this);
+            if (mClosedListener != null) {
+                mClosedListener.onGutsClosed(this);
             }
             return;
         }
@@ -199,8 +208,8 @@
             animateClose(x, y);
         }
         setExposed(false, mNeedsFalsingProtection);
-        if (mListener != null) {
-            mListener.onGutsClosed(this);
+        if (mClosedListener != null) {
+            mClosedListener.onGutsClosed(this);
         }
     }
 
@@ -235,6 +244,10 @@
         return mActualHeight;
     }
 
+    public int getIntrinsicHeight() {
+        return mGutsContent != null && mExposed ? mGutsContent.getActualHeight() : getHeight();
+    }
+
     public void setClipTopAmount(int clipTopAmount) {
         mClipTopAmount = clipTopAmount;
         invalidate();
@@ -252,7 +265,17 @@
     }
 
     public void setClosedListener(OnGutsClosedListener listener) {
-        mListener = listener;
+        mClosedListener = listener;
+    }
+
+    public void setHeightChangedListener(OnHeightChangedListener listener) {
+        mHeightListener = listener;
+    }
+
+    protected void onHeightChanged() {
+        if (mHeightListener != null) {
+            mHeightListener.onHeightChanged(this);
+        }
     }
 
     public void setExposed(boolean exposed, boolean needsFalsingProtection) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
index 8925189..f3c7b84 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
@@ -403,4 +403,9 @@
         }
         return false;
     }
+
+    @Override
+    public int getActualHeight() {
+        return getHeight();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java
index 802925a..7563fd1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java
@@ -317,6 +317,10 @@
     }
 
     private void dismiss(View animView, float velocity) {
+        if (mFadeAnimator != null) {
+            mFadeAnimator.cancel();
+        }
+        mHandler.removeCallbacks(mCheckForDrag);
         mMenuSnappedTo = false;
         mDismissing = true;
         mSwipeHelper.dismiss(animView, velocity);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java
index 4a3f112..ccc99c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java
@@ -21,35 +21,47 @@
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
 
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.content.res.Resources;
-import android.graphics.Color;
+import android.graphics.Typeface;
 import android.service.notification.SnoozeCriterion;
 import android.service.notification.StatusBarNotification;
+import android.text.SpannableString;
+import android.text.style.StyleSpan;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.util.TypedValue;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
+
+import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 
 public class NotificationSnooze extends LinearLayout
         implements NotificationGuts.GutsContent, View.OnClickListener {
 
-    private static final int MAX_ASSISTANT_SUGGESTIONS = 2;
+    private static final int MAX_ASSISTANT_SUGGESTIONS = 1;
     private NotificationGuts mGutsContainer;
     private NotificationSwipeActionHelper mSnoozeListener;
     private StatusBarNotification mSbn;
 
     private TextView mSelectedOptionText;
     private TextView mUndoButton;
-    private ViewGroup mSnoozeOptionView;
+    private ImageView mExpandButton;
+    private View mDivider;
+    private ViewGroup mSnoozeOptionContainer;
     private List<SnoozeOption> mSnoozeOptions;
-    private boolean mSnoozing;
+    private int mCollapsedHeight;
 
     private SnoozeOption mSelectedOption;
+    private boolean mSnoozing;
+    private boolean mExpanded;
+    private AnimatorSet mExpandAnimation;
 
     public NotificationSnooze(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -58,16 +70,21 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
+        mCollapsedHeight = getResources().getDimensionPixelSize(R.dimen.snooze_snackbar_min_height);
+        findViewById(R.id.notification_snooze).setOnClickListener(this);
+        mSelectedOptionText = (TextView) findViewById(R.id.snooze_option_default);
+        mUndoButton = (TextView) findViewById(R.id.undo);
+        mUndoButton.setOnClickListener(this);
+        mExpandButton = (ImageView) findViewById(R.id.expand_button);
+        mDivider = findViewById(R.id.divider);
+        mDivider.setAlpha(0f);
+        mSnoozeOptionContainer = (ViewGroup) findViewById(R.id.snooze_options);
+        mSnoozeOptionContainer.setAlpha(0f);
+
         // Create the different options based on list
         mSnoozeOptions = getDefaultSnoozeOptions();
         createOptionViews();
 
-        // Snackbar
-        mSelectedOptionText = findViewById(R.id.snooze_option_default);
-        mSelectedOptionText.setOnClickListener(this);
-        mUndoButton = findViewById(R.id.undo);
-        mUndoButton.setOnClickListener(this);
-
         // Default to first option in list
         setSelected(mSnoozeOptions.get(0));
     }
@@ -96,52 +113,68 @@
 
     private SnoozeOption createOption(int descriptionResId, int minutes) {
         Resources res = getResources();
-        String resultText = String.format(
-                res.getString(R.string.snoozed_for_time), res.getString(descriptionResId));
-        return new SnoozeOption(null, minutes, res.getString(descriptionResId), resultText);
+        final String description = res.getString(descriptionResId);
+        String resultText = String.format(res.getString(R.string.snoozed_for_time), description);
+        SpannableString string = new SpannableString(resultText);
+        string.setSpan(new StyleSpan(Typeface.BOLD),
+                resultText.length() - description.length(), resultText.length(), 0 /* flags */);
+        return new SnoozeOption(null, minutes, res.getString(descriptionResId), string);
     }
 
     private void createOptionViews() {
-        mSnoozeOptionView = findViewById(R.id.snooze_options);
-        mSnoozeOptionView.removeAllViews();
-        mSnoozeOptionView.setVisibility(View.GONE);
-        final Resources res = getResources();
-        final int textSize = res.getDimensionPixelSize(R.dimen.snooze_option_text_size);
-        final int p = res.getDimensionPixelSize(R.dimen.snooze_option_padding);
-
-        // Add all the options
+        mSnoozeOptionContainer.removeAllViews();
+        LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
+                Context.LAYOUT_INFLATER_SERVICE);
         for (int i = 0; i < mSnoozeOptions.size(); i++) {
             SnoozeOption option = mSnoozeOptions.get(i);
-            TextView tv = new TextView(getContext());
-            tv.setTextColor(Color.WHITE);
-            tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
-            tv.setPadding(p, p, p, p);
-            mSnoozeOptionView.addView(tv);
+            TextView tv = (TextView) inflater.inflate(R.layout.notification_snooze_option,
+                    mSnoozeOptionContainer, false);
+            mSnoozeOptionContainer.addView(tv);
             tv.setText(option.description);
             tv.setTag(option);
             tv.setOnClickListener(this);
         }
+    }
 
-        // Add the undo option as final item
-        TextView tv = new TextView(getContext());
-        tv.setTextColor(Color.WHITE);
-        tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
-        tv.setPadding(p, p, p, p);
-        mSnoozeOptionView.addView(tv);
-        tv.setText(R.string.snooze_option_dont_snooze);
-        tv.setOnClickListener(this);
+    private void hideSelectedOption() {
+        final int childCount = mSnoozeOptionContainer.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View child = mSnoozeOptionContainer.getChildAt(i);
+            child.setVisibility(child.getTag() == mSelectedOption ? View.GONE : View.VISIBLE);
+        }
     }
 
     private void showSnoozeOptions(boolean show) {
-        mSelectedOptionText.setVisibility(show ? View.GONE : View.VISIBLE);
-        mUndoButton.setVisibility(show ? View.GONE : View.VISIBLE);
-        mSnoozeOptionView.setVisibility(show ? View.VISIBLE : View.GONE);
+        mExpanded = show;
+        animateSnoozeOptions(show);
+        int drawableId = show ? com.android.internal.R.drawable.ic_collapse_notification
+                : com.android.internal.R.drawable.ic_expand_notification;
+        mExpandButton.setImageResource(drawableId);
+        if (mGutsContainer != null) {
+            mGutsContainer.onHeightChanged();
+        }
+    }
+
+    private void animateSnoozeOptions(boolean show) {
+        if (mExpandAnimation != null) {
+            mExpandAnimation.cancel();
+        }
+        ObjectAnimator dividerAnim = ObjectAnimator.ofFloat(mDivider, View.ALPHA,
+                mDivider.getAlpha(), show ? 1f : 0f);
+        ObjectAnimator optionAnim = ObjectAnimator.ofFloat(mSnoozeOptionContainer, View.ALPHA,
+                mSnoozeOptionContainer.getAlpha(), show ? 1f : 0f);
+        mExpandAnimation = new AnimatorSet();
+        mExpandAnimation.playTogether(dividerAnim, optionAnim);
+        mExpandAnimation.setDuration(150);
+        mExpandAnimation.setInterpolator(show ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT);
+        mExpandAnimation.start();
     }
 
     private void setSelected(SnoozeOption option) {
         mSelectedOption = option;
         mSelectedOptionText.setText(option.confirmation);
         showSnoozeOptions(false);
+        hideSelectedOption();
     }
 
     @Override
@@ -153,17 +186,28 @@
         final SnoozeOption tag = (SnoozeOption) v.getTag();
         if (tag != null) {
             setSelected(tag);
-        } else if (id == R.id.snooze_option_default) {
-            // Show more snooze options
-            showSnoozeOptions(true);
+        } else if (id == R.id.notification_snooze) {
+            // Toggle snooze options
+            showSnoozeOptions(!mExpanded);
         } else {
-            undoSnooze();
+            // Undo snooze was selected
+            mSelectedOption = null;
+            int[] parentLoc = new int[2];
+            int[] targetLoc = new int[2];
+            mGutsContainer.getLocationOnScreen(parentLoc);
+            v.getLocationOnScreen(targetLoc);
+            final int centerX = v.getWidth() / 2;
+            final int centerY = v.getHeight() / 2;
+            final int x = targetLoc[0] - parentLoc[0] + centerX;
+            final int y = targetLoc[1] - parentLoc[1] + centerY;
+            showSnoozeOptions(false);
+            mGutsContainer.closeControls(x, y, false /* save */);
         }
     }
 
-    private void undoSnooze() {
-        mSelectedOption = null;
-        mGutsContainer.closeControls(-1 /* x */, -1 /* y */, true /* notify */);
+    @Override
+    public int getActualHeight() {
+        return mExpanded ? getHeight() : mCollapsedHeight;
     }
 
     @Override
@@ -173,6 +217,8 @@
 
     @Override
     public View getContentView() {
+        // Reset the view before use
+        setSelected(mSnoozeOptions.get(0));
         return this;
     }
 
@@ -197,11 +243,8 @@
             mSnoozing = true;
             mSnoozeListener.snooze(mSbn, mSelectedOption);
             return true;
-        } else {
-            // Reset the view once it's closed
-            setSelected(mSnoozeOptions.get(0));
-            showSnoozeOptions(false);
         }
+        // The view should be closed
         return false;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 79191f3..46d6415 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -5774,6 +5774,9 @@
             snoozeGuts.setSnoozeListener(mStackScroller.getSwipeActionHelper());
             snoozeGuts.setStatusBarNotification(sbn);
             snoozeGuts.setSnoozeOptions(row.getEntry().snoozeCriteria);
+            guts.setHeightChangedListener((NotificationGuts g) -> {
+                mStackScroller.onHeightChanged(row, row.isShown() /* needsAnimation */);
+            });
         }
 
         if (gutsView instanceof NotificationInfo) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
index 3f8e41a..1fb9b69 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
@@ -23,16 +23,20 @@
     /** Whether the device has device owner, even if not on this user. */
     boolean isDeviceManaged();
     boolean hasProfileOwner();
+    boolean hasWorkProfile();
     String getDeviceOwnerName();
     String getProfileOwnerName();
     CharSequence getDeviceOwnerOrganizationName();
+    CharSequence getWorkProfileOrganizationName();
     boolean isNetworkLoggingEnabled();
     boolean isVpnEnabled();
     boolean isVpnRestricted();
     /** Whether the VPN app should use branded VPN iconography.  */
     boolean isVpnBranded();
     String getPrimaryVpnName();
-    String getProfileVpnName();
+    String getWorkProfileVpnName();
+    boolean hasCACertInCurrentUser();
+    boolean hasCACertInWorkProfile();
     void onUserSwitched(int newUserId);
 
     public interface SecurityControllerCallback {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 19ced23..fcb7289 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -138,6 +138,13 @@
     }
 
     @Override
+    public CharSequence getWorkProfileOrganizationName() {
+        final int profileId = getWorkProfileUserId(mCurrentUserId);
+        if (profileId == UserHandle.USER_NULL) return null;
+        return mDevicePolicyManager.getOrganizationNameForUser(profileId);
+    }
+
+    @Override
     public String getPrimaryVpnName() {
         VpnConfig cfg = mCurrentVpns.get(mVpnUserId);
         if (cfg != null) {
@@ -147,16 +154,27 @@
         }
     }
 
+    private int getWorkProfileUserId(int userId) {
+        for (final UserInfo userInfo : mUserManager.getProfiles(userId)) {
+            if (userInfo.isManagedProfile()) {
+                return userInfo.id;
+            }
+        }
+        return UserHandle.USER_NULL;
+    }
+
     @Override
-    public String getProfileVpnName() {
-        for (int profileId : mUserManager.getProfileIdsWithDisabled(mVpnUserId)) {
-            if (profileId == mVpnUserId) {
-                continue;
-            }
-            VpnConfig cfg = mCurrentVpns.get(profileId);
-            if (cfg != null) {
-                return getNameForVpnConfig(cfg, UserHandle.of(profileId));
-            }
+    public boolean hasWorkProfile() {
+        return getWorkProfileUserId(mCurrentUserId) != UserHandle.USER_NULL;
+    }
+
+    @Override
+    public String getWorkProfileVpnName() {
+        final int profileId = getWorkProfileUserId(mVpnUserId);
+        if (profileId == UserHandle.USER_NULL) return null;
+        VpnConfig cfg = mCurrentVpns.get(profileId);
+        if (cfg != null) {
+            return getNameForVpnConfig(cfg, UserHandle.of(profileId));
         }
         return null;
     }
@@ -199,6 +217,18 @@
     }
 
     @Override
+    public boolean hasCACertInCurrentUser() {
+        //TODO: implement
+        return false;
+    }
+
+    @Override
+    public boolean hasCACertInWorkProfile() {
+        //TODO: implement
+        return false;
+    }
+
+    @Override
     public void removeCallback(SecurityControllerCallback callback) {
         synchronized (mCallbacks) {
             if (callback == null) return;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 697cac9..b8b046b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -433,7 +433,7 @@
                 return false;
             }
         });
-        row.icon = (ImageButton) row.view.findViewById(R.id.volume_row_icon);
+        row.icon = row.view.findViewById(R.id.volume_row_icon);
         row.icon.setImageResource(iconRes);
         if (row.stream != AudioSystem.STREAM_ACCESSIBILITY) {
             row.icon.setOnClickListener(new OnClickListener() {
@@ -465,6 +465,8 @@
                     row.userAttempt = 0;  // reset the grace period, slider updates immediately
                 }
             });
+        } else {
+            row.icon.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
index 1ff373c..ff644d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
@@ -40,6 +40,15 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+/*
+ * Compile and run the whole SystemUI test suite:
+   runtest --path frameworks/base/packages/SystemUI/tests
+ *
+ * Compile and run just this class:
+   runtest --path \
+   frameworks/base/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
+*/
+
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class QSSecurityFooterTest extends SysuiTestCase {
@@ -47,11 +56,11 @@
     private final String MANAGING_ORGANIZATION = "organization";
     private final String DEVICE_OWNER_PACKAGE = "TestDPC";
     private final String VPN_PACKAGE = "TestVPN";
+    private final String VPN_PACKAGE_2 = "TestVPN 2";
 
     private ViewGroup mRootView;
     private TextView mFooterText;
     private TestableImageView mFooterIcon;
-    private TestableImageView mFooterIcon2;
     private QSSecurityFooter mFooter;
     private SecurityController mSecurityController = mock(SecurityController.class);
 
@@ -69,15 +78,12 @@
         mRootView = (ViewGroup) mFooter.getView();
         mFooterText = (TextView) mRootView.findViewById(R.id.footer_text);
         mFooterIcon = (TestableImageView) mRootView.findViewById(R.id.footer_icon);
-        mFooterIcon2 = (TestableImageView) mRootView.findViewById(R.id.footer_icon2);
         mFooter.setHostEnvironment(null);
     }
 
     @Test
     public void testUnmanaged() {
         when(mSecurityController.isDeviceManaged()).thenReturn(false);
-        when(mSecurityController.isVpnEnabled()).thenReturn(false);
-        when(mSecurityController.isVpnBranded()).thenReturn(false);
         mFooter.refreshState();
 
         waitForIdleSync(mFooter.mHandler);
@@ -91,8 +97,12 @@
         mFooter.refreshState();
 
         waitForIdleSync(mFooter.mHandler);
-        assertEquals(mContext.getString(R.string.do_disclosure_generic), mFooterText.getText());
+        assertEquals(mContext.getString(R.string.quick_settings_disclosure_management),
+                     mFooterText.getText());
         assertEquals(View.VISIBLE, mRootView.getVisibility());
+        assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
+        // -1 == never set.
+        assertEquals(-1, mFooterIcon.getLastImageResource());
     }
 
     @Test
@@ -103,37 +113,100 @@
         mFooter.refreshState();
 
         waitForIdleSync(mFooter.mHandler);
-        assertEquals(mContext.getString(R.string.do_disclosure_with_name, MANAGING_ORGANIZATION),
+        assertEquals(mContext.getString(R.string.quick_settings_disclosure_named_management,
+                                        MANAGING_ORGANIZATION),
                 mFooterText.getText());
         assertEquals(View.VISIBLE, mRootView.getVisibility());
+        assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
+        // -1 == never set.
+        assertEquals(-1, mFooterIcon.getLastImageResource());
     }
 
     @Test
     public void testNetworkLoggingEnabled() {
         when(mSecurityController.isDeviceManaged()).thenReturn(true);
         when(mSecurityController.isNetworkLoggingEnabled()).thenReturn(true);
-        when(mSecurityController.isVpnEnabled()).thenReturn(false);
         mFooter.refreshState();
 
         waitForIdleSync(mFooter.mHandler);
-        assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
-        assertEquals(R.drawable.ic_qs_network_logging, mFooterIcon.getLastImageResource());
-        assertEquals(View.INVISIBLE, mFooterIcon2.getVisibility());
-    }
-
-    @Test
-    public void testVpnEnabled() {
-        when(mSecurityController.isDeviceManaged()).thenReturn(true);
-        when(mSecurityController.isNetworkLoggingEnabled()).thenReturn(false);
-        when(mSecurityController.isVpnEnabled()).thenReturn(true);
-        when(mSecurityController.isVpnBranded()).thenReturn(false);
-        mFooter.refreshState();
-
-        waitForIdleSync(mFooter.mHandler);
+        assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_monitoring),
+                mFooterText.getText());
         assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
         // -1 == never set.
         assertEquals(-1, mFooterIcon.getLastImageResource());
-        assertEquals(View.INVISIBLE, mFooterIcon2.getVisibility());
+
+        // Same situation, but with organization name set
+        when(mSecurityController.getDeviceOwnerOrganizationName())
+                .thenReturn(MANAGING_ORGANIZATION);
+        mFooter.refreshState();
+
+        waitForIdleSync(mFooter.mHandler);
+        assertEquals(mContext.getString(
+                             R.string.quick_settings_disclosure_named_management_monitoring,
+                             MANAGING_ORGANIZATION),
+                     mFooterText.getText());
+    }
+
+    @Test
+    public void testManagedCACertsInstalled() {
+        when(mSecurityController.isDeviceManaged()).thenReturn(true);
+        when(mSecurityController.hasCACertInCurrentUser()).thenReturn(true);
+        mFooter.refreshState();
+
+        waitForIdleSync(mFooter.mHandler);
+        assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_monitoring),
+                mFooterText.getText());
+    }
+
+    @Test
+    public void testManagedOneVpnEnabled() {
+        when(mSecurityController.isDeviceManaged()).thenReturn(true);
+        when(mSecurityController.isVpnEnabled()).thenReturn(true);
+        when(mSecurityController.getPrimaryVpnName()).thenReturn(VPN_PACKAGE);
+        mFooter.refreshState();
+
+        waitForIdleSync(mFooter.mHandler);
+        assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_named_vpn,
+                                        VPN_PACKAGE),
+                     mFooterText.getText());
+        assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
+        assertEquals(R.drawable.ic_qs_vpn, mFooterIcon.getLastImageResource());
+
+        // Same situation, but with organization name set
+        when(mSecurityController.getDeviceOwnerOrganizationName())
+                .thenReturn(MANAGING_ORGANIZATION);
+        mFooter.refreshState();
+
+        waitForIdleSync(mFooter.mHandler);
+        assertEquals(mContext.getString(
+                              R.string.quick_settings_disclosure_named_management_named_vpn,
+                              MANAGING_ORGANIZATION, VPN_PACKAGE),
+                     mFooterText.getText());
+    }
+
+    @Test
+    public void testManagedTwoVpnsEnabled() {
+        when(mSecurityController.isDeviceManaged()).thenReturn(true);
+        when(mSecurityController.isVpnEnabled()).thenReturn(true);
+        when(mSecurityController.getPrimaryVpnName()).thenReturn(VPN_PACKAGE);
+        when(mSecurityController.getWorkProfileVpnName()).thenReturn(VPN_PACKAGE_2);
+        mFooter.refreshState();
+
+        waitForIdleSync(mFooter.mHandler);
+        assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_vpns),
+                     mFooterText.getText());
+        assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
+        assertEquals(R.drawable.ic_qs_vpn, mFooterIcon.getLastImageResource());
+
+        // Same situation, but with organization name set
+        when(mSecurityController.getDeviceOwnerOrganizationName())
+                .thenReturn(MANAGING_ORGANIZATION);
+        mFooter.refreshState();
+
+        waitForIdleSync(mFooter.mHandler);
+        assertEquals(mContext.getString(R.string.quick_settings_disclosure_named_management_vpns,
+                                        MANAGING_ORGANIZATION),
+                     mFooterText.getText());
     }
 
     @Test
@@ -141,15 +214,101 @@
         when(mSecurityController.isDeviceManaged()).thenReturn(true);
         when(mSecurityController.isNetworkLoggingEnabled()).thenReturn(true);
         when(mSecurityController.isVpnEnabled()).thenReturn(true);
-        when(mSecurityController.isVpnBranded()).thenReturn(false);
+        when(mSecurityController.getPrimaryVpnName()).thenReturn("VPN Test App");
         mFooter.refreshState();
 
         waitForIdleSync(mFooter.mHandler);
         assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
-        assertEquals(View.VISIBLE, mFooterIcon2.getVisibility());
+        assertEquals(R.drawable.ic_qs_vpn, mFooterIcon.getLastImageResource());
+        assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_monitoring),
+                mFooterText.getText());
+    }
+
+    @Test
+    public void testWorkProfileCACertsInstalled() {
+        when(mSecurityController.isDeviceManaged()).thenReturn(false);
+        when(mSecurityController.hasCACertInWorkProfile()).thenReturn(true);
+        mFooter.refreshState();
+
+        waitForIdleSync(mFooter.mHandler);
         // -1 == never set.
         assertEquals(-1, mFooterIcon.getLastImageResource());
-        assertEquals(-1, mFooterIcon2.getLastImageResource());
+        assertEquals(mContext.getString(
+                             R.string.quick_settings_disclosure_managed_profile_monitoring),
+                     mFooterText.getText());
+
+        // Same situation, but with organization name set
+        when(mSecurityController.getWorkProfileOrganizationName())
+                .thenReturn(MANAGING_ORGANIZATION);
+        mFooter.refreshState();
+
+        waitForIdleSync(mFooter.mHandler);
+        assertEquals(mContext.getString(
+                             R.string.quick_settings_disclosure_named_managed_profile_monitoring,
+                             MANAGING_ORGANIZATION),
+                     mFooterText.getText());
+    }
+
+    @Test
+    public void testCACertsInstalled() {
+        when(mSecurityController.isDeviceManaged()).thenReturn(false);
+        when(mSecurityController.hasCACertInCurrentUser()).thenReturn(true);
+        mFooter.refreshState();
+
+        waitForIdleSync(mFooter.mHandler);
+        // -1 == never set.
+        assertEquals(-1, mFooterIcon.getLastImageResource());
+        assertEquals(mContext.getString(R.string.quick_settings_disclosure_monitoring),
+                     mFooterText.getText());
+    }
+
+    @Test
+    public void testTwoVpnsEnabled() {
+        when(mSecurityController.isVpnEnabled()).thenReturn(true);
+        when(mSecurityController.getPrimaryVpnName()).thenReturn(VPN_PACKAGE);
+        when(mSecurityController.getWorkProfileVpnName()).thenReturn(VPN_PACKAGE_2);
+        mFooter.refreshState();
+
+        waitForIdleSync(mFooter.mHandler);
+        assertEquals(R.drawable.ic_qs_vpn, mFooterIcon.getLastImageResource());
+        assertEquals(mContext.getString(R.string.quick_settings_disclosure_vpns),
+                     mFooterText.getText());
+    }
+
+    @Test
+    public void testWorkProfileVpnEnabled() {
+        when(mSecurityController.isVpnEnabled()).thenReturn(true);
+        when(mSecurityController.getWorkProfileVpnName()).thenReturn(VPN_PACKAGE_2);
+        mFooter.refreshState();
+
+        waitForIdleSync(mFooter.mHandler);
+        assertEquals(R.drawable.ic_qs_vpn, mFooterIcon.getLastImageResource());
+        assertEquals(mContext.getString(
+                             R.string.quick_settings_disclosure_managed_profile_named_vpn,
+                             VPN_PACKAGE_2),
+                     mFooterText.getText());
+    }
+
+    @Test
+    public void testVpnEnabled() {
+        when(mSecurityController.isVpnEnabled()).thenReturn(true);
+        when(mSecurityController.getPrimaryVpnName()).thenReturn(VPN_PACKAGE);
+        mFooter.refreshState();
+
+        waitForIdleSync(mFooter.mHandler);
+        assertEquals(R.drawable.ic_qs_vpn, mFooterIcon.getLastImageResource());
+        assertEquals(mContext.getString(R.string.quick_settings_disclosure_named_vpn,
+                                        VPN_PACKAGE),
+                     mFooterText.getText());
+
+        when(mSecurityController.hasWorkProfile()).thenReturn(true);
+        mFooter.refreshState();
+
+        waitForIdleSync(mFooter.mHandler);
+        assertEquals(mContext.getString(
+                             R.string.quick_settings_disclosure_personal_profile_named_vpn,
+                             VPN_PACKAGE),
+                     mFooterText.getText());
     }
 
     @Test
@@ -160,8 +319,7 @@
                         null /* primaryVpn */,
                         null /* profileVpn */,
                         null /* deviceOwnerOrganization */,
-                        false /* hasProfileOwner */,
-                        false /* isBranded */));
+                        false /* hasProfileOwner */));
     }
 
     @Test
@@ -172,8 +330,7 @@
                         VPN_PACKAGE,
                         null /* profileVpn */,
                         null /* deviceOwnerOrganization */,
-                        false /* hasProfileOwner */,
-                        false /* isBranded */));
+                        false /* hasProfileOwner */));
     }
 
     @Test
@@ -184,8 +341,7 @@
                         null /* primaryVpn */,
                         null /* profileVpn */,
                         MANAGING_ORGANIZATION,
-                        false /* hasProfileOwner */,
-                        false /* isBranded */));
+                        false /* hasProfileOwner */));
     }
 
     @Test
@@ -196,8 +352,7 @@
                         VPN_PACKAGE,
                         null /* profileVpn */,
                         MANAGING_ORGANIZATION,
-                        false /* hasProfileOwner */,
-                        false /* isBranded */));
+                        false /* hasProfileOwner */));
     }
 
     private CharSequence getExpectedMessage(boolean hasDeviceOwnerOrganization, boolean hasVPN) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java
index 157b8a0..fee5e32 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java
@@ -36,6 +36,11 @@
     }
 
     @Override
+    public boolean hasWorkProfile() {
+        return false;
+    }
+
+    @Override
     public String getDeviceOwnerName() {
         return null;
     }
@@ -51,6 +56,11 @@
     }
 
     @Override
+    public CharSequence getWorkProfileOrganizationName() {
+        return null;
+    }
+
+    @Override
     public boolean isNetworkLoggingEnabled() {
         return false;
     }
@@ -76,11 +86,21 @@
     }
 
     @Override
-    public String getProfileVpnName() {
+    public String getWorkProfileVpnName() {
         return null;
     }
 
     @Override
+    public boolean hasCACertInCurrentUser() {
+        return false;
+    }
+
+    @Override
+    public boolean hasCACertInWorkProfile() {
+        return false;
+    }
+
+    @Override
     public void onUserSwitched(int newUserId) {
 
     }
diff --git a/services/Android.mk b/services/Android.mk
index a4c891b..5c863b0 100644
--- a/services/Android.mk
+++ b/services/Android.mk
@@ -27,6 +27,7 @@
     appwidget \
     autofill \
     backup \
+    companion \
     coverage\
     devicepolicy \
     midi \
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index d6f5256..1b5b2c6 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -48,6 +48,7 @@
 import android.os.UserManager;
 import android.os.UserManagerInternal;
 import android.provider.Settings;
+import android.service.autofill.FillEventHistory;
 import android.util.LocalLog;
 import android.util.Log;
 import android.util.Slog;
@@ -184,7 +185,7 @@
     }
 
     @Override
-    public void onStopUser(int userId) {
+    public void onCleanupUser(int userId) {
         synchronized (mLock) {
             removeCachedServiceLocked(userId);
         }
@@ -211,7 +212,7 @@
     /**
      * Peeks the service instance for a user.
      *
-     * @return service instance or null if not already present
+     * @return service instance or {@code null} if not already present
      */
     @Nullable
     AutofillManagerServiceImpl peekServiceForUserLocked(int userId) {
@@ -398,6 +399,21 @@
         }
 
         @Override
+        public FillEventHistory getFillEventHistory() throws RemoteException {
+            UserHandle user = getCallingUserHandle();
+            int uid = getCallingUid();
+
+            synchronized (mLock) {
+                AutofillManagerServiceImpl service = peekServiceForUserLocked(user.getIdentifier());
+                if (service != null) {
+                    return service.getFillEventHistory(uid);
+                }
+            }
+
+            return null;
+        }
+
+        @Override
         public boolean restoreSession(int sessionId, IBinder activityToken, IBinder appCallback)
                 throws RemoteException {
             activityToken = Preconditions.checkNotNull(activityToken, "activityToken");
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 57ef6d0..e274e18 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -48,7 +48,9 @@
 import android.provider.Settings;
 import android.service.autofill.AutofillService;
 import android.service.autofill.AutofillServiceInfo;
+import android.service.autofill.FillEventHistory;
 import android.service.autofill.FillRequest;
+import android.service.autofill.FillResponse;
 import android.service.autofill.IAutoFillService;
 import android.text.TextUtils;
 import android.util.LocalLog;
@@ -122,6 +124,10 @@
     @GuardedBy("mLock")
     private final SparseArray<Session> mSessions = new SparseArray<>();
 
+    /** The last selection */
+    @GuardedBy("mLock")
+    private FillEventHistory mEventHistory;
+
     /**
      * Receiver of assist data from the app's {@link Activity}.
      */
@@ -500,6 +506,72 @@
         return mInfo.getServiceInfo().loadLabel(mContext.getPackageManager());
     }
 
+    /**
+     * Initializes the last fill selection after an autofill service returned a new
+     * {@link FillResponse}.
+     */
+    void setLastResponse(int serviceUid, @NonNull FillResponse response) {
+        synchronized (mLock) {
+            mEventHistory = new FillEventHistory(serviceUid, response.getClientState());
+        }
+    }
+
+    /**
+     * Updates the last fill selection when an authentication was selected.
+     */
+    void setAuthenticationSelected() {
+        synchronized (mLock) {
+            mEventHistory.addEvent(
+                    new FillEventHistory.Event(FillEventHistory.Event.TYPE_AUTHENTICATION_SELECTED, null));
+        }
+    }
+
+    /**
+     * Updates the last fill selection when an dataset authentication was selected.
+     */
+    void setDatasetAuthenticationSelected(@Nullable String selectedDataset) {
+        synchronized (mLock) {
+            mEventHistory.addEvent(new FillEventHistory.Event(
+                    FillEventHistory.Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset));
+        }
+    }
+
+    /**
+     * Updates the last fill selection when an save Ui is shown.
+     */
+    void setSaveShown() {
+        synchronized (mLock) {
+            mEventHistory.addEvent(new FillEventHistory.Event(FillEventHistory.Event.TYPE_SAVE_SHOWN, null));
+        }
+    }
+
+    /**
+     * Updates the last fill response when a dataset was selected.
+     */
+    void setDatasetSelected(@Nullable String selectedDataset) {
+        synchronized (mLock) {
+            mEventHistory.addEvent(
+                    new FillEventHistory.Event(FillEventHistory.Event.TYPE_DATASET_SELECTED, selectedDataset));
+        }
+    }
+
+    /**
+     * Gets the fill event history.
+     *
+     * @param callingUid The calling uid
+     *
+     * @return The history or {@code null} if there is none.
+     */
+    FillEventHistory getFillEventHistory(int callingUid) {
+        synchronized (mLock) {
+            if (mEventHistory != null && mEventHistory.getServiceUid() == callingUid) {
+                return mEventHistory;
+            }
+        }
+
+        return null;
+    }
+
     void dumpLocked(String prefix, PrintWriter pw) {
         final String prefix2 = prefix + "  ";
 
@@ -528,6 +600,20 @@
                 mSessions.valueAt(i).dumpLocked(prefix2, pw);
             }
         }
+
+        if (mEventHistory == null || mEventHistory.getEvents().size() == 0) {
+            pw.print(prefix); pw.println("No event on last fill response");
+        } else {
+            pw.print(prefix); pw.println("Events of last fill response:");
+            pw.print(prefix);
+
+            int numEvents = mEventHistory.getEvents().size();
+            for (int i = 0; i < numEvents; i++) {
+                FillEventHistory.Event event = mEventHistory.getEvents().get(i);
+                pw.println("  " + i + ": eventType=" + event.getType() + " datasetId="
+                        + event.getDatasetId());
+            }
+        }
     }
 
     void destroySessionsLocked() {
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index 3badcfc..4d0f380 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -87,7 +87,7 @@
     private PendingRequest mPendingRequest;
 
     public interface FillServiceCallbacks {
-        void onFillRequestSuccess(@Nullable FillResponse response,
+        void onFillRequestSuccess(@Nullable FillResponse response, int serviceUid,
                 @NonNull String servicePackageName, int requestId);
         void onFillRequestFailure(@Nullable CharSequence message,
                 @NonNull String servicePackageName);
@@ -252,11 +252,11 @@
     }
 
     private void dispatchOnFillRequestSuccess(PendingRequest pendingRequest,
-            FillResponse response, int requestId) {
+            int callingUid, FillResponse response, int requestId) {
         mHandler.getHandler().post(() -> {
             if (handleResponseCallbackCommon(pendingRequest)) {
-                mCallbacks.onFillRequestSuccess(response, mComponentName.getPackageName(),
-                        requestId);
+                mCallbacks.onFillRequestSuccess(response, callingUid,
+                        mComponentName.getPackageName(), requestId);
             }
         });
     }
@@ -424,7 +424,7 @@
                     RemoteFillService remoteService = mWeakService.get();
                     if (remoteService != null) {
                         remoteService.dispatchOnFillRequestSuccess(
-                                PendingFillRequest.this, response, requestId);
+                                PendingFillRequest.this, getCallingUid(), response, requestId);
                     }
                 }
 
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 7c3f324..2b99614 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -229,7 +229,7 @@
 
     // FillServiceCallbacks
     @Override
-    public void onFillRequestSuccess(@Nullable FillResponse response,
+    public void onFillRequestSuccess(@Nullable FillResponse response, int serviceUid,
             @NonNull String servicePackageName, int requestId) {
         if (response == null) {
             if ((mFlags & FLAG_MANUAL_REQUEST) != 0) {
@@ -241,6 +241,8 @@
             return;
         }
 
+        mService.setLastResponse(serviceUid, response);
+
         if ((response.getDatasets() == null || response.getDatasets().isEmpty())
                         && response.getAuthentication() == null) {
             // Response is "empty" from an UI point of view, need to notify client.
@@ -314,6 +316,9 @@
         synchronized (mLock) {
             fillInIntent = createAuthFillInIntent(mStructure, extras);
         }
+
+        mService.setAuthenticationSelected();
+
         mHandlerCaller.getHandler().post(() -> startAuthentication(intent, fillInIntent));
     }
 
@@ -442,7 +447,7 @@
      */
     public boolean showSaveLocked() {
         if (mStructure == null) {
-            Slog.wtf(TAG, "showSaveLocked(): no mStructure");
+            Slog.d(TAG, "showSaveLocked(): no mStructure");
             return true;
         }
         if (mResponses == null) {
@@ -542,6 +547,7 @@
                 }
             }
             if (atLeastOneChanged) {
+                mService.setSaveShown();
                 getUiForShowing().showSaveUi(mService.getServiceLabel(), saveInfo, mPackageName);
                 return false;
             }
@@ -764,7 +770,8 @@
         boolean saveOnAllViewsInvisible = false;
         SaveInfo saveInfo = mResponses.valueAt(getLastResponseIndex()).getSaveInfo();
         if (saveInfo != null) {
-            saveOnAllViewsInvisible = saveInfo.saveOnAllViewsInvisible();
+            saveOnAllViewsInvisible =
+                    (saveInfo.getFlags() & SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE) != 0;
 
             // We only need to track views if we want to save once they become invisible.
             if (saveOnAllViewsInvisible) {
@@ -881,12 +888,15 @@
         synchronized (mLock) {
             // Autofill it directly...
             if (dataset.getAuthentication() == null) {
+                mService.setDatasetSelected(dataset.getId());
+
                 autoFillApp(dataset);
                 return;
             }
 
             // ...or handle authentication.
             // TODO(b/33197203 , b/35707731): make sure it's ignored if there is one already
+            mService.setDatasetAuthenticationSelected(dataset.getId());
             mDatasetWaitingAuth = dataset;
             setViewStatesLocked(null, dataset, ViewState.STATE_WAITING_DATASET_AUTH);
             final Intent fillInIntent = createAuthFillInIntent(mStructure, null);
diff --git a/services/companion/Android.mk b/services/companion/Android.mk
new file mode 100644
index 0000000..be48761
--- /dev/null
+++ b/services/companion/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := services.companion
+
+LOCAL_SRC_FILES += \
+      $(call all-java-files-under,java)
+
+LOCAL_JAVA_LIBRARIES := services.core
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/print/java/com/android/server/print/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
similarity index 99%
rename from services/print/java/com/android/server/print/CompanionDeviceManagerService.java
rename to services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 122a954..41b70a1 100644
--- a/services/print/java/com/android/server/print/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -15,7 +15,7 @@
  */
 
 
-package com.android.server.print;
+package com.android.server.companion;
 
 import static com.android.internal.util.CollectionUtils.size;
 import static com.android.internal.util.Preconditions.checkArgument;
@@ -81,7 +81,6 @@
 import java.util.concurrent.ConcurrentMap;
 import java.util.function.Function;
 
-//TODO move to own package!
 //TODO onStop schedule unbind in 5 seconds
 //TODO make sure APIs are only callable from currently focused app
 //TODO schedule stopScan on activity destroy(except if configuration change)
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 2e61550..6502c01 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -25,7 +25,7 @@
 # This is logged when the screen on broadcast has completed
 2727 power_screen_broadcast_stop (which|1|5),(wakelockCount|1|1)
 # This is logged when the screen is turned on or off.
-2728 power_screen_state (offOrOn|1|5),(becauseOfUser|1|5),(totalTouchDownTime|2|3),(touchCycles|1|1)
+2728 power_screen_state (offOrOn|1|5),(becauseOfUser|1|5),(totalTouchDownTime|2|3),(touchCycles|1|1),(latency|1|3)
 # This is logged when the partial wake lock (keeping the device awake
 # regardless of whether the screen is off) is acquired or released.
 2729 power_partial_wake_state (releasedorAcquired|1|5),(tag|3)
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 39bfeda..8ad3d23 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -49,6 +49,7 @@
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
+import android.annotation.BinderThread;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -2146,6 +2147,7 @@
         return mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
     }
 
+    @BinderThread
     @SuppressWarnings("deprecation")
     @Override
     public void setImeWindowStatus(IBinder token, IBinder startInputToken, int vis,
@@ -2161,9 +2163,23 @@
             mBackDisposition = backDisposition;
             updateSystemUiLocked(token, vis, backDisposition);
         }
+
+        final boolean dismissImeOnBackKeyPressed;
+        switch (backDisposition) {
+            case InputMethodService.BACK_DISPOSITION_WILL_DISMISS:
+                dismissImeOnBackKeyPressed = true;
+                break;
+            case InputMethodService.BACK_DISPOSITION_WILL_NOT_DISMISS:
+                dismissImeOnBackKeyPressed = false;
+                break;
+            default:
+            case InputMethodService.BACK_DISPOSITION_DEFAULT:
+                dismissImeOnBackKeyPressed = ((vis & InputMethodService.IME_VISIBLE) != 0);
+                break;
+        }
         mWindowManagerInternal.updateInputMethodWindowStatus(token,
                 (vis & InputMethodService.IME_VISIBLE) != 0,
-                info != null ? info.mTargetWindow : null);
+                dismissImeOnBackKeyPressed, info != null ? info.mTargetWindow : null);
     }
 
     private void updateSystemUi(IBinder token, int vis, int backDisposition) {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index a3c691c..b91ecf5 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -312,8 +312,7 @@
     }
 
     ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
-            int id, Notification notification, int callingPid, int callingUid,
-            boolean fgRequired, String callingPackage, final int userId)
+            int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)
             throws TransactionTooLargeException {
         if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service
                 + " type=" + resolvedType + " args=" + service.getExtras());
@@ -464,10 +463,6 @@
         }
 
         ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
-        // STOPSHIP deprecated; remove when NotificationManager.startServiceInForeground is retired
-        if (notification != null) {
-            setServiceForegroundInnerLocked(r, id, notification, 0);
-        }
         return cmp;
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index be30e4b..6af4840 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7849,7 +7849,14 @@
                     r.pictureInPictureArgs.copyOnlySet(args);
                     final float aspectRatio = r.pictureInPictureArgs.getAspectRatio();
                     final List<RemoteAction> actions = r.pictureInPictureArgs.getActions();
-                    final Rect sourceBounds = r.pictureInPictureArgs.getSourceRectHint();
+                    // Adjust the source bounds by the insets for the transition down
+                    final Rect sourceBounds = new Rect(r.pictureInPictureArgs.getSourceRectHint());
+                    final Rect insets = r.pictureInPictureArgs.getSourceRectHintInsets();
+                    if (insets != null) {
+                        sourceBounds.offsetTo(Math.max(0, sourceBounds.left - insets.left),
+                                Math.max(0, sourceBounds.top - insets.top));
+                    }
+
                     mStackSupervisor.moveActivityToPinnedStackLocked(r, sourceBounds, aspectRatio,
                             true /* moveHomeStackToFront */, "enterPictureInPictureMode");
                     final PinnedActivityStack stack = mStackSupervisor.getStack(PINNED_STACK_ID);
@@ -10682,6 +10689,13 @@
             return;
         }
 
+        // When a task is locked, dismiss the pinned stack if it exists
+        final PinnedActivityStack pinnedStack = mStackSupervisor.getStack(
+                PINNED_STACK_ID);
+        if (pinnedStack != null) {
+            mStackSupervisor.removeStackLocked(PINNED_STACK_ID);
+        }
+
         // isSystemInitiated is used to distinguish between locked and pinned mode, as pinned mode
         // is initiated by system after the pinning request was shown and locked mode is initiated
         // by an authorized app directly
@@ -17928,8 +17942,7 @@
 
     @Override
     public ComponentName startService(IApplicationThread caller, Intent service,
-            String resolvedType, int id, Notification notification, boolean requireForeground,
-            String callingPackage, int userId)
+            String resolvedType, boolean requireForeground, String callingPackage, int userId)
             throws TransactionTooLargeException {
         enforceNotIsolatedCaller("startService");
         // Refuse possible leaked file descriptors
@@ -17950,7 +17963,7 @@
             ComponentName res;
             try {
                 res = mServices.startServiceLocked(caller, service,
-                        resolvedType, id, notification, callingPid, callingUid,
+                        resolvedType, callingPid, callingUid,
                         requireForeground, callingPackage, userId);
             } finally {
                 Binder.restoreCallingIdentity(origId);
@@ -17969,7 +17982,7 @@
             ComponentName res;
             try {
                 res = mServices.startServiceLocked(null, service,
-                        resolvedType, 0, null, -1, uid, fgRequired, callingPackage, userId);
+                        resolvedType, -1, uid, fgRequired, callingPackage, userId);
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 0fcf3e6..b6bfb00 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -524,7 +524,7 @@
         pw.println("Starting service: " + intent);
         pw.flush();
         ComponentName cn = mInterface.startService(null, intent, intent.getType(),
-                -1, null, asForeground, SHELL_PACKAGE_NAME, mUserId);
+                asForeground, SHELL_PACKAGE_NAME, mUserId);
         if (cn == null) {
             err.println("Error: Not found; no service started.");
             return -1;
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 17c7dde..fe03c36 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -1178,6 +1178,10 @@
      *         the activity is not currently visible and {@param noThrow} is not set.
      */
     boolean checkEnterPictureInPictureState(String caller, boolean noThrow, boolean beforeStopping) {
+        if (!supportsPictureInPicture()) {
+            return false;
+        }
+
         // Check app-ops and see if PiP is supported for this package
         if (!checkEnterPictureInPictureAppOpsState()) {
             return false;
@@ -2126,6 +2130,11 @@
         if (mWindowContainerController == null) {
             return;
         }
+        if (mTaskOverlay) {
+            // We don't show starting window for overlay activities.
+            return;
+        }
+
         final CompatibilityInfo compatInfo =
                 service.compatibilityInfoForPackageLocked(info.applicationInfo);
         final boolean shown = mWindowContainerController.addStartingWindow(packageName, theme,
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 825e8ac..85c5c64 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -65,6 +65,8 @@
 import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityRecord.ASSISTANT_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityStack.ActivityState.STOPPED;
+import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
 import static com.android.server.am.ActivityStackSupervisor.FindTaskResult;
 import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
 import static com.android.server.am.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
@@ -1179,7 +1181,7 @@
             final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
             for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
                 final ActivityRecord r = activities.get(activityNdx);
-                if (r.state == ActivityState.STOPPING || r.state == ActivityState.STOPPED
+                if (r.state == STOPPING || r.state == STOPPED
                         || r.state == ActivityState.PAUSED || r.state == ActivityState.PAUSING) {
                     r.setSleeping(true);
                 }
@@ -1362,7 +1364,7 @@
         if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Complete pause: " + prev);
 
         if (prev != null) {
-            final boolean wasStopping = prev.state == ActivityState.STOPPING;
+            final boolean wasStopping = prev.state == STOPPING;
             prev.state = ActivityState.PAUSED;
             if (prev.finishing) {
                 if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Executing finish of activity: " + prev);
@@ -1383,7 +1385,7 @@
                     // We are also stopping, the stop request must have gone soon after the pause.
                     // We can't clobber it, because the stop confirmation will not be handled.
                     // We don't need to schedule another stop, we only need to let it happen.
-                    prev.state = ActivityState.STOPPING;
+                    prev.state = STOPPING;
                 } else if ((!prev.visible && !hasVisibleBehindActivity())
                         || mService.isSleepingOrShuttingDownLocked()) {
                     // If we were visible then resumeTopActivities will release resources before
@@ -2002,10 +2004,17 @@
         // keeping the screen frozen.
         if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Making invisible: " + r + " " + r.state);
         try {
+            final boolean canEnterPictureInPicture = r.checkEnterPictureInPictureState(
+                    "makeInvisible", true /* noThrow */, true /* beforeStopping */);
+            // We don't want to call setVisible(false) to avoid notifying the client of this
+            // intermittent invisible state if it can enter Pip and isn't stopped or stopping.
+            if (!canEnterPictureInPicture || r.state == STOPPING || r.state == STOPPED) {
+                r.setVisible(false);
+            }
+
             switch (r.state) {
                 case STOPPING:
                 case STOPPED:
-                    r.setVisible(false);
                     if (r.app != null && r.app.thread != null) {
                         if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
                                 "Scheduling invisibility: " + r);
@@ -2024,25 +2033,16 @@
                     // This case created for transitioning activities from
                     // translucent to opaque {@link Activity#convertToOpaque}.
                     if (visibleBehind == r) {
-                        r.setVisible(false);
                         releaseBackgroundResources(r);
                     } else {
                         // If this activity is in a state where it can currently enter
                         // picture-in-picture, then don't immediately schedule the idle now in case
                         // the activity tries to enterPictureInPictureMode() later. Otherwise,
                         // we will try and stop the activity next time idle is processed.
-                        final boolean canEnterPictureInPicture = r.checkEnterPictureInPictureState(
-                                "makeInvisible", true /* noThrow */, true /* beforeStopping */);
 
                         if (canEnterPictureInPicture) {
-                            // We set r.visible=false so that Stop will later
-                            // call setVisible for us. In this case
-                            // we don't want to call setVisible(false) to avoid
-                            // notifying the client of this intermittent invisible
-                            // state.
+                            // We set r.visible=false so that Stop will later call setVisible for us
                             r.visible = false;
-                        } else {
-                            r.setVisible(false);
                         }
                         addToStopping(r, true /* scheduleIdle */,
                                 canEnterPictureInPicture /* idleDelayed */);
@@ -2318,9 +2318,20 @@
 
         mStackSupervisor.setLaunchSource(next.info.applicationInfo.uid);
 
+        final boolean prevCanPip = prev != null && prev.checkEnterPictureInPictureState(
+                "resumeTopActivity", true /* noThrow */, userLeaving /* beforeStopping */);
         // If the flag RESUME_WHILE_PAUSING is set, then continue to schedule the previous activity
-        // to be paused, while at the same time resuming the new resume activity
+        // to be paused, while at the same time resuming the new resume activity only if the
+        // previous activity can't go into Pip since we want to give Pip activities a chance to
+        // enter Pip before resuming the next activity.
         final boolean resumeWhilePausing = (next.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0;
+        // TODO: This would be go to have however, the various call points that pass in
+        // prev need to be corrected first. In some cases the prev is equal to the next e.g. launch
+        // an app from home. And, is come other cases it is null e.g. press home button after
+        // launching an app. The doc on the method says prev. is null expect for the case we are
+        // coming from pause. We need to see if that is a valid thing and also if all the code in
+        // this method using prev. are setup to function like that.
+        //&& !prevCanPip;
         boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, next, false);
         if (mResumedActivity != null) {
             if (DEBUG_STATES) Slog.d(TAG_STATES,
@@ -3360,11 +3371,11 @@
                 r.stopped = false;
                 if (DEBUG_STATES) Slog.v(TAG_STATES,
                         "Moving to STOPPING: " + r + " (stop requested)");
-                r.state = ActivityState.STOPPING;
+                r.state = STOPPING;
                 if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
                         "Stopping visible=" + r.visible + " for " + r);
                 if (!r.visible) {
-                    r.setVisibility(false);
+                    r.setVisible(false);
                 }
                 EventLogTags.writeAmStopActivity(
                         r.userId, System.identityHashCode(r), r.shortComponentName);
@@ -3382,7 +3393,7 @@
                 // Just in case, assume it to be stopped.
                 r.stopped = true;
                 if (DEBUG_STATES) Slog.v(TAG_STATES, "Stop failed; moving to STOPPED: " + r);
-                r.state = ActivityState.STOPPED;
+                r.state = STOPPED;
                 if (r.deferRelaunchUntilPaused) {
                     destroyActivityLocked(r, true, "stop-except");
                 }
@@ -3687,7 +3698,7 @@
             }
             if (DEBUG_STATES) Slog.v(TAG_STATES,
                     "Moving to STOPPING: "+ r + " (finish requested)");
-            r.state = ActivityState.STOPPING;
+            r.state = STOPPING;
             if (oomAdj) {
                 mService.updateOomAdjLocked();
             }
@@ -3712,8 +3723,8 @@
                 || (prevState == ActivityState.PAUSED
                     && (mode == FINISH_AFTER_PAUSE || mStackId == PINNED_STACK_ID))
                 || finishingActivityInNonFocusedStack
-                || prevState == ActivityState.STOPPING
-                || prevState == ActivityState.STOPPED
+                || prevState == STOPPING
+                || prevState == STOPPED
                 || prevState == ActivityState.INITIALIZING) {
             r.makeFinishingLocked();
             boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm");
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 43ae4b2..152d5f4 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2878,6 +2878,10 @@
 
         mWindowManager.deferSurfaceLayout();
 
+        // This will clear the pinned stack by moving an existing task to the full screen stack,
+        // ensuring only one task is present.
+        moveTasksToFullscreenStackLocked(PINNED_STACK_ID, !ON_TOP);
+
         // Need to make sure the pinned stack exist so we can resize it below...
         final PinnedActivityStack stack = getStack(PINNED_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
 
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index f8a4d4b..d42b6a7 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -48,6 +48,7 @@
 import android.util.DisplayMetrics;
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.util.XmlUtils;
 
@@ -445,10 +446,23 @@
 
         final Rect bounds = updateOverrideConfigurationFromLaunchBounds();
         final Configuration overrideConfig = getOverrideConfiguration();
-        mWindowContainerController = new TaskWindowContainerController(taskId, this,
+        setWindowContainerController(new TaskWindowContainerController(taskId, this,
                 getStack().getWindowContainerController(), userId, bounds, overrideConfig,
                 mResizeMode, mSupportsPictureInPicture, isHomeTask(), onTop, showForAllUsers,
-                lastTaskDescription);
+                lastTaskDescription));
+    }
+
+    /**
+     * Should only be invoked from {@link #createWindowContainer(boolean, boolean)}.
+     */
+    @VisibleForTesting
+    protected void setWindowContainerController(TaskWindowContainerController controller) {
+        if (mWindowContainerController != null) {
+            throw new IllegalArgumentException("Window container=" + mWindowContainerController
+                    + " already created for task=" + this);
+        }
+
+        mWindowContainerController = controller;
     }
 
     void removeWindowContainer() {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 2687242..aa1b74c 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -520,7 +520,11 @@
     private int mPrevVolDirection = AudioManager.ADJUST_SAME;
     // mVolumeControlStream is set by VolumePanel to temporarily force the stream type which volume
     // is controlled by Vol keys.
-    private int  mVolumeControlStream = -1;
+    private int mVolumeControlStream = -1;
+    // interpretation of whether the volume stream has been selected by the user by clicking on a
+    // volume slider to change which volume is controlled by the volume keys. Is false
+    // when mVolumeControlStream is -1.
+    private boolean mUserSelectedVolumeControlStream = false;
     private final Object mForceControlStreamLock = new Object();
     // VolumePanel is currently the only client of forceVolumeControlStream() and runs in system
     // server process so in theory it is not necessary to monitor the client death.
@@ -929,11 +933,8 @@
         synchronized (VolumeStreamState.class) {
             int numStreamTypes = AudioSystem.getNumStreamTypes();
             for (int streamType = 0; streamType < numStreamTypes; streamType++) {
-                if (streamType != mStreamVolumeAlias[streamType]) {
-                    mStreamStates[streamType].
-                                    setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]],
-                                            TAG);
-                }
+                mStreamStates[streamType]
+                        .setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]], TAG);
                 // apply stream volume
                 if (!mStreamStates[streamType].mIsMuted) {
                     mStreamStates[streamType].applyAllVolumes();
@@ -1022,20 +1023,21 @@
         }
 
         mStreamVolumeAlias[AudioSystem.STREAM_DTMF] = dtmfStreamAlias;
-        final int oldStreamA11yAlias = mStreamVolumeAlias[AudioSystem.STREAM_ACCESSIBILITY];
-        if (oldStreamA11yAlias != a11yStreamAlias) {
-            mStreamVolumeAlias[AudioSystem.STREAM_ACCESSIBILITY] = a11yStreamAlias;
-            mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].mVolumeIndexSettingName =
-                    System.VOLUME_SETTINGS_INT[a11yStreamAlias];
-            // restore the value from the settings when the alias changes
-            mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].readSettings();
-        }
+        mStreamVolumeAlias[AudioSystem.STREAM_ACCESSIBILITY] = a11yStreamAlias;
 
         if (updateVolumes) {
             mStreamStates[AudioSystem.STREAM_DTMF].setAllIndexes(mStreamStates[dtmfStreamAlias],
                     caller);
+
+            mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].mVolumeIndexSettingName =
+                    System.VOLUME_SETTINGS_INT[a11yStreamAlias];
             mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].setAllIndexes(
                     mStreamStates[a11yStreamAlias], caller);
+            if (sIndependentA11yVolume) {
+                // restore the a11y values from the settings
+                mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].readSettings();
+            }
+
             // apply stream mute states according to new value of mRingerModeAffectedStreams
             setRingerModeInt(getRingerModeInternal(), false);
             sendMsg(mAudioHandler,
@@ -1226,14 +1228,29 @@
     private void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
             String callingPackage, String caller, int uid) {
         if (DEBUG_VOL) Log.d(TAG, "adjustSuggestedStreamVolume() stream=" + suggestedStreamType
-                + ", flags=" + flags + ", caller=" + caller);
-        int streamType;
-        boolean isMute = isMuteAdjust(direction);
-        if (mVolumeControlStream != -1) {
+                + ", flags=" + flags + ", caller=" + caller
+                + ", volControlStream=" + mVolumeControlStream
+                + ", userSelect=" + mUserSelectedVolumeControlStream);
+        final int streamType;
+        if (mUserSelectedVolumeControlStream) { // implies mVolumeControlStream != -1
             streamType = mVolumeControlStream;
         } else {
-            streamType = getActiveStreamType(suggestedStreamType);
+            final int maybeActiveStreamType = getActiveStreamType(suggestedStreamType);
+            final boolean activeForReal;
+            if (maybeActiveStreamType == AudioSystem.STREAM_MUSIC) {
+                activeForReal = isAfMusicActiveRecently(0);
+            } else {
+                activeForReal = AudioSystem.isStreamActive(maybeActiveStreamType, 0);
+            }
+            if (activeForReal || mVolumeControlStream == -1) {
+                streamType = maybeActiveStreamType;
+            } else {
+                streamType = mVolumeControlStream;
+            }
         }
+
+        final boolean isMute = isMuteAdjust(direction);
+
         ensureValidStreamType(streamType);
         final int resolvedStream = mStreamVolumeAlias[streamType];
 
@@ -1709,13 +1726,18 @@
 
     /** @see AudioManager#forceVolumeControlStream(int) */
     public void forceVolumeControlStream(int streamType, IBinder cb) {
+        if (DEBUG_VOL) { Log.d(TAG, String.format("forceVolumeControlStream(%d)", streamType)); }
         synchronized(mForceControlStreamLock) {
+            if (mVolumeControlStream != -1 && streamType != -1) {
+                mUserSelectedVolumeControlStream = true;
+            }
             mVolumeControlStream = streamType;
             if (mVolumeControlStream == -1) {
                 if (mForceControlStreamClient != null) {
                     mForceControlStreamClient.release();
                     mForceControlStreamClient = null;
                 }
+                mUserSelectedVolumeControlStream = false;
             } else {
                 mForceControlStreamClient = new ForceControlStreamClient(cb);
             }
@@ -1746,6 +1768,7 @@
                 } else {
                     mForceControlStreamClient = null;
                     mVolumeControlStream = -1;
+                    mUserSelectedVolumeControlStream = false;
                 }
             }
         }
@@ -4228,7 +4251,17 @@
             return mIndexMin;
         }
 
+        /**
+         * Copies all device/index pairs from the given VolumeStreamState after initializing
+         * them with the volume for DEVICE_OUT_DEFAULT. No-op if the source VolumeStreamState
+         * has the same stream type as this instance.
+         * @param srcStream
+         * @param caller
+         */
         public void setAllIndexes(VolumeStreamState srcStream, String caller) {
+            if (mStreamType == srcStream.mStreamType) {
+                return;
+            }
             synchronized (VolumeStreamState.class) {
                 int srcStreamType = srcStream.getStreamType();
                 // apply default device volume from source stream to all devices first in case
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
index 1ffa864..601ed01 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
@@ -78,7 +78,6 @@
     public static final int CMD_IPV6_TETHER_UPDATE          = BASE_IFACE + 13;
 
     private final State mInitialState;
-    private final State mServingState;
     private final State mLocalHotspotState;
     private final State mTetheredState;
     private final State mUnavailableState;
@@ -107,14 +106,12 @@
         mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
 
         mInitialState = new InitialState();
-        mServingState = new ServingState();
         mLocalHotspotState = new LocalHotspotState();
         mTetheredState = new TetheredState();
         mUnavailableState = new UnavailableState();
         addState(mInitialState);
-        addState(mServingState);
-            addState(mLocalHotspotState, mServingState);
-            addState(mTetheredState, mServingState);
+        addState(mLocalHotspotState);
+        addState(mTetheredState);
         addState(mUnavailableState);
 
         setInitialState(mInitialState);
@@ -222,12 +219,11 @@
         }
     }
 
-    class ServingState extends State {
+    class BaseServingState extends State {
         @Override
         public void enter() {
             if (!configureIfaceIp(true)) {
                 mLastError = ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR;
-                transitionTo(mInitialState);
                 return;
             }
 
@@ -236,12 +232,13 @@
             } catch (Exception e) {
                 Log.e(TAG, "Error Tethering: " + e.toString());
                 mLastError = ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
-                transitionTo(mInitialState);
                 return;
             }
 
             if (!mIPv6TetherSvc.start()) {
                 Log.e(TAG, "Failed to start IPv6TetheringInterfaceServices");
+                // TODO: Make this a fatal error once Bluetooth IPv6 is sorted.
+                return;
             }
         }
 
@@ -254,9 +251,9 @@
 
             try {
                 mNMService.untetherInterface(mIfaceName);
-            } catch (Exception ee) {
+            } catch (Exception e) {
                 mLastError = ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
-                Log.e(TAG, "Failed to untether interface: " + ee.toString());
+                Log.e(TAG, "Failed to untether interface: " + e.toString());
             }
 
             configureIfaceIp(false);
@@ -293,15 +290,27 @@
         }
     }
 
-    class LocalHotspotState extends State {
+    // Handling errors in BaseServingState.enter() by transitioning is
+    // problematic because transitioning during a multi-state jump yields
+    // a Log.wtf(). Ultimately, there should be only one ServingState,
+    // and forwarding and NAT rules should be handled by a coordinating
+    // functional element outside of TetherInterfaceStateMachine.
+    class LocalHotspotState extends BaseServingState {
         @Override
         public void enter() {
+            super.enter();
+            if (mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+                transitionTo(mInitialState);
+            }
+
             if (DBG) Log.d(TAG, "Local hotspot " + mIfaceName);
             sendInterfaceState(IControlsTethering.STATE_LOCAL_HOTSPOT);
         }
 
         @Override
         public boolean processMessage(Message message) {
+            if (super.processMessage(message)) return true;
+
             maybeLogMessage(this, message.what);
             switch (message.what) {
                 case CMD_TETHER_REQUESTED:
@@ -317,9 +326,19 @@
         }
     }
 
-    class TetheredState extends State {
+    // Handling errors in BaseServingState.enter() by transitioning is
+    // problematic because transitioning during a multi-state jump yields
+    // a Log.wtf(). Ultimately, there should be only one ServingState,
+    // and forwarding and NAT rules should be handled by a coordinating
+    // functional element outside of TetherInterfaceStateMachine.
+    class TetheredState extends BaseServingState {
         @Override
         public void enter() {
+            super.enter();
+            if (mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+                transitionTo(mInitialState);
+            }
+
             if (DBG) Log.d(TAG, "Tethered " + mIfaceName);
             sendInterfaceState(IControlsTethering.STATE_TETHERED);
         }
@@ -327,6 +346,7 @@
         @Override
         public void exit() {
             cleanupUpstream();
+            super.exit();
         }
 
         private void cleanupUpstream() {
@@ -361,6 +381,8 @@
 
         @Override
         public boolean processMessage(Message message) {
+            if (super.processMessage(message)) return true;
+
             maybeLogMessage(this, message.what);
             boolean retValue = true;
             switch (message.what) {
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 3a1ddd7..fdaa599 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -288,6 +288,9 @@
     // current setting - 4 hours
     private static final long MAX_RETRY_INTERVAL = 4*60*60*1000;
 
+    // Timeout when holding wakelocks for downloading XTRA data.
+    private static final long DOWNLOAD_XTRA_DATA_TIMEOUT_MS = 60 * 1000;
+
     private BackOff mNtpBackOff = new BackOff(RETRY_INTERVAL, MAX_RETRY_INTERVAL);
     private BackOff mXtraBackOff = new BackOff(RETRY_INTERVAL, MAX_RETRY_INTERVAL);
 
@@ -986,7 +989,7 @@
         mDownloadXtraDataPending = STATE_DOWNLOADING;
 
         // hold wake lock while task runs
-        mWakeLock.acquire();
+        mWakeLock.acquire(DOWNLOAD_XTRA_DATA_TIMEOUT_MS);
         Log.i(TAG, "WakeLock acquired by handleDownloadXtraData()");
         AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
             @Override
@@ -1009,7 +1012,11 @@
                 }
 
                 // release wake lock held by task
-                mWakeLock.release();
+                if (mWakeLock.isHeld()) {
+                    mWakeLock.release();
+                } else {
+                    Log.e(TAG, "WakeLock expired before release in handleDownloadXtraData()");
+                }
                 Log.i(TAG, "WakeLock released by handleDownloadXtraData()");
             }
         });
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index 2f82915..0e69bca 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -121,9 +121,12 @@
     public void removeSession(MediaSessionRecord record) {
         mSessions.remove(record);
         if (mMediaButtonSession == record) {
-            // When the media button session is gone, try to find the alternative media session
-            // in the media button session app.
-            onMediaSessionChangeInMediaButtonSessionApp();
+            // When the media button session is removed, nullify the media button session and do not
+            // search for the alternative media session within the app. It's because the alternative
+            // media session might be a dummy which isn't able to handle the media key events.
+            mOnMediaButtonSessionChangedListener.onMediaButtonSessionChanged(
+                    mMediaButtonSession, null);
+            mMediaButtonSession = null;
         }
         clearCache(record.getUserId());
     }
@@ -157,7 +160,13 @@
         // In that case, we pick the media session whose PlaybackState matches
         // the audio playback configuration.
         if (mMediaButtonSession != null && mMediaButtonSession.getUid() == record.getUid()) {
-            onMediaSessionChangeInMediaButtonSessionApp();
+            MediaSessionRecord newMediaButtonSession =
+                    findMediaButtonSession(mMediaButtonSession.getUid());
+            if (newMediaButtonSession != mMediaButtonSession) {
+                mOnMediaButtonSessionChangedListener.onMediaButtonSessionChanged(
+                        mMediaButtonSession, newMediaButtonSession);
+                mMediaButtonSession = newMediaButtonSession;
+            }
         }
     }
 
@@ -200,22 +209,6 @@
     }
 
     /**
-     * Handle the change in a media session in the media button session app.
-     * <p>If the app has multiple media sessions, change in a media sesion in the app may change
-     * the media button session.
-     * @see #findMediaButtonSession
-     */
-    private void onMediaSessionChangeInMediaButtonSessionApp() {
-        MediaSessionRecord newMediaButtonSession =
-                findMediaButtonSession(mMediaButtonSession.getUid());
-        if (newMediaButtonSession != mMediaButtonSession) {
-            mOnMediaButtonSessionChangedListener.onMediaButtonSessionChanged(mMediaButtonSession,
-                    newMediaButtonSession);
-            mMediaButtonSession = newMediaButtonSession;
-        }
-    }
-
-    /**
      * Find the media button session with the given {@param uid}.
      * If the app has multiple media sessions, the media session matches the audio playback state
      * becomes the media button session.
diff --git a/services/core/java/com/android/server/notification/GlobalSortKeyComparator.java b/services/core/java/com/android/server/notification/GlobalSortKeyComparator.java
index ce976d2..acedafc 100644
--- a/services/core/java/com/android/server/notification/GlobalSortKeyComparator.java
+++ b/services/core/java/com/android/server/notification/GlobalSortKeyComparator.java
@@ -15,19 +15,25 @@
  */
 package com.android.server.notification;
 
+import android.util.Slog;
+
 import java.util.Comparator;
 
 /**
  * Sorts notifications by their global sort key.
  */
 public class GlobalSortKeyComparator implements Comparator<NotificationRecord> {
+    private final static String TAG = "GlobalSortComp";
+
     @Override
     public int compare(NotificationRecord left, NotificationRecord right) {
         if (left.getGlobalSortKey() == null) {
-            throw new IllegalStateException("Missing left global sort key: " + left);
+            Slog.wtf(TAG, "Missing left global sort key: " + left);
+            return 1;
         }
         if (right.getGlobalSortKey() == null) {
-            throw new IllegalStateException("Missing right global sort key: " + right);
+            Slog.wtf(TAG, "Missing right global sort key: " + right);
+            return  -1;
         }
         return left.getGlobalSortKey().compareTo(right.getGlobalSortKey());
     }
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 90e9b92..3cb2f35 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -356,7 +356,6 @@
 
     private void rebuildRestoredPackages() {
         mRestoredPackages.clear();
-        mSnoozingForCurrentProfiles.clear();
         String secureSettingName = restoredSettingName(mConfig.secureSettingName);
         String secondarySettingName = mConfig.secondarySettingName == null
                 ? null : restoredSettingName(mConfig.secondarySettingName);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index cc3948e..c289204 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1874,10 +1874,9 @@
             int userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
                     Binder.getCallingUid(), incomingUserId, true, false,
                     "getAppActiveNotifications", pkg);
-            final ArrayMap<String, StatusBarNotification> map
-                    = new ArrayMap<>(mNotificationList.size() + mEnqueuedNotifications.size());
-
             synchronized (mNotificationLock) {
+                final ArrayMap<String, StatusBarNotification> map
+                        = new ArrayMap<>(mNotificationList.size() + mEnqueuedNotifications.size());
                 final int N = mNotificationList.size();
                 for (int i = 0; i < N; i++) {
                     StatusBarNotification sbn = sanitizeSbn(pkg, userId,
@@ -1900,11 +1899,10 @@
                         map.put(sbn.getKey(), sbn); // pending update overwrites existing post here
                     }
                 }
+                final ArrayList<StatusBarNotification> list = new ArrayList<>(map.size());
+                list.addAll(map.values());
+                return new ParceledListSlice<StatusBarNotification>(list);
             }
-
-            final ArrayList<StatusBarNotification> list = new ArrayList<>(map.size());
-            list.addAll(map.values());
-            return new ParceledListSlice<StatusBarNotification>(list);
         }
 
         private StatusBarNotification sanitizeSbn(String pkg, int userId,
@@ -2036,8 +2034,10 @@
             long identity = Binder.clearCallingIdentity();
             try {
                 // allow bound services to disable themselves
-                final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
-                info.getOwner().setComponentState(info.component, false);
+                synchronized (mNotificationLock) {
+                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
+                    info.getOwner().setComponentState(info.component, false);
+                }
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -2101,8 +2101,10 @@
                 String key, String snoozeCriterionId) {
             long identity = Binder.clearCallingIdentity();
             try {
-                final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
-                snoozeNotificationInt(key, SNOOZE_UNTIL_UNSPECIFIED, snoozeCriterionId, info);
+                synchronized (mNotificationLock) {
+                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
+                    snoozeNotificationInt(key, SNOOZE_UNTIL_UNSPECIFIED, snoozeCriterionId, info);
+                }
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -2118,8 +2120,10 @@
                 long duration) {
             long identity = Binder.clearCallingIdentity();
             try {
-                final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
-                snoozeNotificationInt(key, duration, null, info);
+                synchronized (mNotificationLock) {
+                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
+                    snoozeNotificationInt(key, duration, null, info);
+                }
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -2134,9 +2138,11 @@
         public void unsnoozeNotificationFromAssistant(INotificationListener token, String key) {
             long identity = Binder.clearCallingIdentity();
             try {
-                final ManagedServiceInfo info =
-                        mNotificationAssistants.checkServiceTokenLocked(token);
-                unsnoozeNotificationInt(key, info);
+                synchronized (mNotificationLock) {
+                    final ManagedServiceInfo info =
+                            mNotificationAssistants.checkServiceTokenLocked(token);
+                    unsnoozeNotificationInt(key, info);
+                }
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -2734,7 +2740,10 @@
         }
 
         private void verifyPrivilegedListener(INotificationListener token, UserHandle user) {
-            ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
+            ManagedServiceInfo info;
+            synchronized (mNotificationLock) {
+                info = mListeners.checkServiceTokenLocked(token);
+            }
             if (!hasCompanionDevice(info)) {
                 throw new SecurityException(info + " does not have access");
             }
@@ -3099,8 +3108,10 @@
                 @Override
                 public void run() {
                     synchronized (mNotificationLock) {
-                        removeForegroundServiceFlagByListLocked(mEnqueuedNotifications, pkg, notificationId, userId);
-                        removeForegroundServiceFlagByListLocked(mNotificationList, pkg, notificationId, userId);
+                        removeForegroundServiceFlagByListLocked(
+                                mEnqueuedNotifications, pkg, notificationId, userId);
+                        removeForegroundServiceFlagByListLocked(
+                                mNotificationList, pkg, notificationId, userId);
                     }
                 }
             });
@@ -3192,6 +3203,8 @@
             return;
         } else if (channelId == null && shouldWarnUseChannels(pkg, notificationUid)) {
             // STOPSHIP TODO: remove once default channel is removed for all apps that target O.
+            Log.e(TAG, "Developer Warning for package " + pkg
+                    + ", no channel specified for posted notification: " + notification);
             doDebugOnlyToast("Developer warning for package \"" + pkg + "\"\n" +
                     "Posted notification should specify a channel");
         }
@@ -3229,8 +3242,12 @@
 
     private void doDebugOnlyToast(CharSequence toastText) {
         if (Build.IS_DEBUGGABLE) {
-            Toast toast = Toast.makeText(getContext(), toastText, Toast.LENGTH_LONG);
-            toast.show();
+            try {
+                Toast toast = Toast.makeText(getContext(), toastText, Toast.LENGTH_LONG);
+                toast.show();
+            } catch (RuntimeException e) {
+                Slog.w(TAG, "Unable to toast with text: " + toastText, e);
+            }
         }
     }
 
@@ -3596,7 +3613,7 @@
         final boolean aboveThreshold =
                 record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT;
         final boolean canInterrupt = aboveThreshold && !record.isIntercepted();
-        if (DBG || record.isIntercepted())
+        if (DBG)
             Slog.v(TAG,
                     "pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt +
                             " intercept=" + record.isIntercepted()
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index 89a303d..46bc69b 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -35,6 +35,7 @@
 import android.provider.Settings;
 import android.util.ArrayMap;
 import android.util.AtomicFile;
+import android.util.ByteStringUtils;
 import android.util.PackageUtils;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -56,8 +57,10 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
+import java.security.SecureRandom;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Locale;
 import java.util.Set;
 import java.util.function.Predicate;
 
@@ -81,6 +84,7 @@
     private static final String INSTANT_APP_COOKIE_FILE_PREFIX = "cookie_";
     private static final String INSTANT_APP_COOKIE_FILE_SIFFIX = ".dat";
     private static final String INSTANT_APP_METADATA_FILE = "metadata.xml";
+    private static final String INSTANT_APP_ANDROID_ID_FILE = "android_id";
 
     private static final String TAG_PACKAGE = "package";
     private static final String TAG_PERMISSIONS = "permissions";
@@ -195,6 +199,36 @@
         return null;
     }
 
+    public String getInstantAppAndroidIdLPw(@NonNull String packageName,
+                                            @UserIdInt int userId) {
+        File idFile = new File(getInstantApplicationDir(packageName, userId),
+                INSTANT_APP_ANDROID_ID_FILE);
+        if (idFile.exists()) {
+            try {
+                return IoUtils.readFileAsString(idFile.getAbsolutePath());
+            } catch (IOException e) {
+                Slog.e(LOG_TAG, "Failed to read instant app android id file: " + idFile, e);
+            }
+        }
+        return generateInstantAppAndroidIdLPw(packageName, userId);
+    }
+
+    private String generateInstantAppAndroidIdLPw(@NonNull String packageName,
+                                                @UserIdInt int userId) {
+        byte[] randomBytes = new byte[8];
+        new SecureRandom().nextBytes(randomBytes);
+        String id = ByteStringUtils.toHexString(randomBytes).toLowerCase(Locale.US);
+        File idFile = new File(getInstantApplicationDir(packageName, userId),
+                INSTANT_APP_ANDROID_ID_FILE);
+        try (FileOutputStream fos = new FileOutputStream(idFile)) {
+            fos.write(id.getBytes());
+        } catch (IOException e) {
+            Slog.e(LOG_TAG, "Error writing instant app android id file: " + idFile, e);
+        }
+        return id;
+
+    }
+
     public @Nullable List<InstantAppInfo> getInstantAppsLPr(@UserIdInt int userId) {
         List<InstantAppInfo> installedApps = getInstalledInstantApplicationsLPr(userId);
         List<InstantAppInfo> uninstalledApps = getUninstalledInstantApplicationsLPr(userId);
@@ -462,6 +496,7 @@
         File instantAppDir = getInstantApplicationDir(packageName, userId);
         new File(instantAppDir, INSTANT_APP_METADATA_FILE).delete();
         new File(instantAppDir, INSTANT_APP_ICON_FILE).delete();
+        new File(instantAppDir, INSTANT_APP_ANDROID_ID_FILE).delete();
         File cookie = peekInstantCookieFile(packageName, userId);
         if (cookie != null) {
             cookie.delete();
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 7b86542..2e4b49a 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -274,23 +274,56 @@
         // Intercept and collect dexopt requests
         final List<String> commands = new ArrayList<String>();
         final Installer collectingInstaller = new Installer(mContext, true) {
+            /**
+             * Encode the dexopt command into a string.
+             *
+             * Note: If you have to change the signature of this function, increase the version
+             *       number, and update the counterpart in
+             *       frameworks/native/cmds/installd/otapreopt.cpp.
+             */
             @Override
             public void dexopt(String apkPath, int uid, @Nullable String pkgName,
                     String instructionSet, int dexoptNeeded, @Nullable String outputPath,
                     int dexFlags, String compilerFilter, @Nullable String volumeUuid,
                     @Nullable String sharedLibraries, @Nullable String seInfo) throws InstallerException {
-                commands.add(buildCommand("dexopt",
-                        apkPath,
-                        uid,
-                        pkgName,
-                        instructionSet,
-                        dexoptNeeded,
-                        outputPath,
-                        dexFlags,
-                        compilerFilter,
-                        volumeUuid,
-                        sharedLibraries,
-                        seInfo));
+                final StringBuilder builder = new StringBuilder();
+
+                // The version. Right now it's 2.
+                builder.append("2 ");
+
+                builder.append("dexopt");
+
+                encodeParameter(builder, apkPath);
+                encodeParameter(builder, uid);
+                encodeParameter(builder, pkgName);
+                encodeParameter(builder, instructionSet);
+                encodeParameter(builder, dexoptNeeded);
+                encodeParameter(builder, outputPath);
+                encodeParameter(builder, dexFlags);
+                encodeParameter(builder, compilerFilter);
+                encodeParameter(builder, volumeUuid);
+                encodeParameter(builder, sharedLibraries);
+                encodeParameter(builder, seInfo);
+
+                commands.add(builder.toString());
+            }
+
+            /**
+             * Encode a parameter as necessary for the commands string.
+             */
+            private void encodeParameter(StringBuilder builder, Object arg) {
+                builder.append(' ');
+
+                if (arg == null) {
+                    builder.append('!');
+                }
+
+                String txt = String.valueOf(arg);
+                if (txt.indexOf('\0') != -1 || txt.indexOf(' ') != -1 || "!".equals(txt)) {
+                    throw new IllegalArgumentException(
+                            "Invalid argument while executing " + arg);
+                }
+                builder.append(txt);
             }
         };
 
@@ -430,28 +463,4 @@
             super(installer, installLock, context, "*otadexopt*");
         }
     }
-
-    /**
-     * Cook up argument list in the format that {@code installd} expects.
-     */
-    private static String buildCommand(Object... args) {
-        final StringBuilder builder = new StringBuilder();
-        for (Object arg : args) {
-            String escaped;
-            if (arg == null) {
-                escaped = "";
-            } else {
-                escaped = String.valueOf(arg);
-            }
-            if (escaped.indexOf('\0') != -1 || escaped.indexOf(' ') != -1 || "!".equals(escaped)) {
-                throw new IllegalArgumentException(
-                        "Invalid argument while executing " + Arrays.toString(args));
-            }
-            if (TextUtils.isEmpty(escaped)) {
-                escaped = "!";
-            }
-            builder.append(' ').append(escaped);
-        }
-        return builder.toString();
-    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 9039647..8413491 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -149,13 +149,14 @@
         final boolean profileUpdated = checkForProfileUpdates &&
                 isProfileUpdated(pkg, sharedGid, compilerFilter);
 
-        // TODO(calin,jeffhao): shared library paths should be adjusted to include previous code
-        // paths (b/34169257).
-        final String sharedLibrariesPath = getSharedLibrariesPath(sharedLibraries);
+        String sharedLibrariesPath = getSharedLibrariesPath(sharedLibraries);
         // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct flags.
         final int dexoptFlags = getDexFlags(pkg, compilerFilter);
 
         int result = DEX_OPT_SKIPPED;
+        // TODO: Iterate based on dependency hierarchy (currently alphabetically by name)
+        // (b/37480811).
+        String basePathCheck = null;
         for (String path : paths) {
             for (String dexCodeIsa : dexCodeInstructionSets) {
                 int newResult = dexOptPath(pkg, path, dexCodeIsa, compilerFilter, profileUpdated,
@@ -167,6 +168,22 @@
                 if ((result != DEX_OPT_FAILED) && (newResult != DEX_OPT_SKIPPED)) {
                     result = newResult;
                 }
+                // Add the relative path of code we just compiled to the shared libraries.
+                int slashIndex = path.lastIndexOf('/') + 1;
+                String relativePath = path.substring(slashIndex);
+                if (sharedLibrariesPath == null) {
+                    sharedLibrariesPath = relativePath;
+                } else {
+                    sharedLibrariesPath += ":" + relativePath;
+                }
+                // Sanity check that the base paths are all the same.
+                String basePath = path.substring(0, slashIndex);
+                if (basePathCheck == null) {
+                    basePathCheck = basePath;
+                } else if (!basePath.equals(basePathCheck)) {
+                    Slog.wtf(TAG, "Split paths have different base paths: " + basePath + " and " +
+                        basePathCheck);
+                }
             }
         }
         return result;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 95b4903..3411c4e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2805,8 +2805,12 @@
                 mRequiredInstallerPackage = getRequiredInstallerLPr();
                 mRequiredUninstallerPackage = getRequiredUninstallerLPr();
                 mIntentFilterVerifierComponent = getIntentFilterVerifierComponentNameLPr();
-                mIntentFilterVerifier = new IntentVerifierProxy(mContext,
-                        mIntentFilterVerifierComponent);
+                if (mIntentFilterVerifierComponent != null) {
+                    mIntentFilterVerifier = new IntentVerifierProxy(mContext,
+                            mIntentFilterVerifierComponent);
+                } else {
+                    mIntentFilterVerifier = null;
+                }
                 mServicesSystemSharedLibraryPackageName = getRequiredSharedLibraryLPr(
                         PackageManager.SYSTEM_SHARED_LIBRARY_SERVICES,
                         SharedLibraryInfo.VERSION_UNDEFINED);
@@ -3046,9 +3050,9 @@
 
         if (best != null) {
             return best.getComponentInfo().getComponentName();
-        } else {
-            throw new RuntimeException("There must be at least one intent filter verifier");
         }
+        Slog.w(TAG, "Intent filter verifier not found");
+        return null;
     }
 
     private @Nullable Pair<ComponentName, String> getInstantAppResolverLPr() {
@@ -4218,8 +4222,10 @@
                     }
 
                     SharedLibraryInfo resLibInfo = new SharedLibraryInfo(libInfo.getName(),
-                            libInfo.getVersion(), libInfo.getType(), libInfo.getDeclaringPackage(),
-                            getPackagesUsingSharedLibraryLPr(libInfo, flags, userId));
+                            // TODO: Remove cast for lib version once internally we support longs.
+                            (int) libInfo.getVersion(), libInfo.getType(),
+                            libInfo.getDeclaringPackage(), getPackagesUsingSharedLibraryLPr(libInfo,
+                            flags, userId));
 
                     if (result == null) {
                         result = new ArrayList<>();
@@ -13202,7 +13208,7 @@
                 if (dcsUid > 0) {
                     am.backgroundWhitelistUid(dcsUid);
                 }
-                am.startService(null, intent, null, -1, null, false, mContext.getOpPackageName(),
+                am.startService(null, intent, null, false, mContext.getOpPackageName(),
                         UserHandle.USER_SYSTEM);
             } catch (RemoteException e) {
             }
@@ -17593,7 +17599,8 @@
         for (int i = 0; i < versionCount; i++) {
             SharedLibraryEntry libEntry = versionedLib.valueAt(i);
             if (versionsCallerCanSee != null && versionsCallerCanSee.indexOfKey(
-                    libEntry.info.getVersion()) < 0) {
+                    // TODO: Remove cast for lib version once internally we support longs.
+                    (int) libEntry.info.getVersion()) < 0) {
                 continue;
             }
             // TODO: We will change version code to long, so in the new API it is long
@@ -23651,6 +23658,22 @@
         return mInstantAppInstallerActivity == null
                 ? null : mInstantAppInstallerActivity.getComponentName();
     }
+
+    @Override
+    public String getInstantAppAndroidId(String packageName, int userId) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_INSTANT_APPS,
+                "getInstantAppAndroidId");
+        enforceCrossUserPermission(Binder.getCallingUid(), userId,
+                true /* requireFullPermission */, false /* checkShell */,
+                "getInstantAppAndroidId");
+        // Make sure the target is an Instant App.
+        if (!isInstantApp(packageName, userId)) {
+            return null;
+        }
+        synchronized (mPackages) {
+            return mInstantAppRegistry.getInstantAppAndroidIdLPw(packageName, userId);
+        }
+    }
 }
 
 interface PackageSender {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 4f29bfa..5c9f749 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -222,7 +222,7 @@
 import android.view.animation.Animation;
 import android.view.animation.AnimationSet;
 import android.view.animation.AnimationUtils;
-import android.widget.ImageView;
+import android.view.inputmethod.InputMethodManagerInternal;
 
 import com.android.internal.R;
 import com.android.internal.logging.MetricsLogger;
@@ -279,6 +279,7 @@
     static final int SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP = 2;
     static final int SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME = 3;
     static final int SHORT_PRESS_POWER_GO_HOME = 4;
+    static final int SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME = 5;
 
     static final int LONG_PRESS_POWER_NOTHING = 0;
     static final int LONG_PRESS_POWER_GLOBAL_ACTIONS = 1;
@@ -407,6 +408,7 @@
     PowerManager mPowerManager;
     ActivityManagerInternal mActivityManagerInternal;
     InputManagerInternal mInputManagerInternal;
+    InputMethodManagerInternal mInputMethodManagerInternal;
     DreamManagerInternal mDreamManagerInternal;
     PowerManagerInternal mPowerManagerInternal;
     IStatusBarService mStatusBarService;
@@ -513,6 +515,7 @@
     volatile boolean mPictureInPictureVisible;
     // Written by vr manager thread, only read in this class
     volatile boolean mPersistentVrModeEnabled;
+    volatile private boolean mDismissImeOnBackKeyPressed;
 
     // Used to hold the last user key used to wake the device.  This helps us prevent up events
     // from being passed to the foregrounded app without a corresponding down event
@@ -1396,6 +1399,21 @@
                 case SHORT_PRESS_POWER_GO_HOME:
                     launchHomeFromHotKey(true /* awakenFromDreams */, false /*respectKeyguard*/);
                     break;
+                case SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME: {
+                    if (mDismissImeOnBackKeyPressed) {
+                        if (mInputMethodManagerInternal == null) {
+                            mInputMethodManagerInternal =
+                                    LocalServices.getService(InputMethodManagerInternal.class);
+                        }
+                        if (mInputMethodManagerInternal != null) {
+                            mInputMethodManagerInternal.hideCurrentInputMethod();
+                        }
+                    } else {
+                        launchHomeFromHotKey(true /* awakenFromDreams */,
+                                false /*respectKeyguard*/);
+                    }
+                    break;
+                }
             }
         }
     }
@@ -7955,6 +7973,11 @@
     }
 
     @Override
+    public void setDismissImeOnBackKeyPressed(boolean newValue) {
+        mDismissImeOnBackKeyPressed = newValue;
+    }
+
+    @Override
     public int getInputMethodWindowVisibleHeightLw() {
         return mDockBottom - mCurBottom;
     }
@@ -8170,6 +8193,8 @@
             pw.print(prefix); pw.print("mLastInputMethodTargetWindow=");
                     pw.println(mLastInputMethodTargetWindow);
         }
+        pw.print(prefix); pw.print("mDismissImeOnBackKeyPressed=");
+                pw.println(mDismissImeOnBackKeyPressed);
         if (mStatusBar != null) {
             pw.print(prefix); pw.print("mStatusBar=");
                     pw.print(mStatusBar); pw.print(" isStatusBarKeyguard=");
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 8f11436..f5bb082 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -406,11 +406,7 @@
                 mHandler.post(new Runnable() {
                     @Override
                     public void run() {
-                        LogMaker log = new LogMaker(MetricsEvent.SCREEN);
-                        log.setType(MetricsEvent.TYPE_OPEN);
-                        log.setSubtype(0); // not user initiated
-                        MetricsLogger.action(log);
-                        EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, 0, 0, 0);
+                        // Note a SCREEN tron event is logged in PowerManagerService.
                         mPolicy.startedWakingUp();
                     }
                 });
@@ -470,7 +466,7 @@
                         log.setType(MetricsEvent.TYPE_CLOSE);
                         log.setSubtype(why);
                         MetricsLogger.action(log);
-                        EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 0, why, 0, 0);
+                        EventLogTags.writePowerScreenState(0, why, 0, 0, 0);
                         mPolicy.finishedGoingToSleep(why);
                     }
                 });
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index cf597b05..4f239a5 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -32,6 +32,7 @@
 import android.hardware.display.DisplayManagerInternal;
 import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
 import android.hardware.power.V1_0.PowerHint;
+import android.metrics.LogMaker;
 import android.net.Uri;
 import android.os.BatteryManager;
 import android.os.BatteryManagerInternal;
@@ -75,6 +76,8 @@
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.hardware.AmbientDisplayConfiguration;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
@@ -188,6 +191,11 @@
     // System property indicating that the screen should remain off until an explicit user action
     private static final String SYSTEM_PROPERTY_QUIESCENT = "ro.boot.quiescent";
 
+    private static final String TRACE_SCREEN_ON = "Screen turning on";
+
+    /** If turning screen on takes more than this long, we show a warning on logcat. */
+    private static final int SCREEN_ON_LATENCY_WARNING_MS = 200;
+
     /** Constants for {@link #shutdownOrRebootInternal} */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({HALT_MODE_SHUTDOWN, HALT_MODE_REBOOT, HALT_MODE_REBOOT_SAFE_MODE})
@@ -1369,6 +1377,8 @@
             return false;
         }
 
+        Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, 0);
+
         Trace.traceBegin(Trace.TRACE_TAG_POWER, "wakeUp");
         try {
             switch (mWakefulness) {
@@ -1551,6 +1561,23 @@
         }
     }
 
+    private void logScreenOn() {
+        Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, 0);
+
+        final int latencyMs = (int) (SystemClock.uptimeMillis() - mLastWakeTime);
+
+        LogMaker log = new LogMaker(MetricsEvent.SCREEN);
+        log.setType(MetricsEvent.TYPE_OPEN);
+        log.setSubtype(0); // not user initiated
+        log.setLatency(latencyMs); // How long it took.
+        MetricsLogger.action(log);
+        EventLogTags.writePowerScreenState(1, 0, 0, 0, latencyMs);
+
+        if (latencyMs >= SCREEN_ON_LATENCY_WARNING_MS) {
+            Slog.w(TAG, "Screen on took " + latencyMs+ " ms");
+        }
+    }
+
     private void finishWakefulnessChangeIfNeededLocked() {
         if (mWakefulnessChanging && mDisplayReady) {
             if (mWakefulness == WAKEFULNESS_DOZING
@@ -1560,6 +1587,9 @@
             if (mWakefulness == WAKEFULNESS_DOZING || mWakefulness == WAKEFULNESS_ASLEEP) {
                 logSleepTimeoutRecapturedLocked();
             }
+            if (mWakefulness == WAKEFULNESS_AWAKE) {
+                logScreenOn();
+            }
             mWakefulnessChanging = false;
             mNotifier.onWakefulnessChangeFinished();
         }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 0049585..252b4d4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7267,13 +7267,16 @@
 
         @Override
         public void updateInputMethodWindowStatus(@NonNull IBinder imeToken,
-                boolean imeWindowVisible, @Nullable IBinder targetWindowToken) {
+                boolean imeWindowVisible, boolean dismissImeOnBackKeyPressed,
+                @Nullable IBinder targetWindowToken) {
             // TODO (b/34628091): Use this method to address the window animation issue.
             if (DEBUG_INPUT_METHOD) {
                 Slog.w(TAG_WM, "updateInputMethodWindowStatus: imeToken=" + imeToken
+                        + " dismissImeOnBackKeyPressed=" + dismissImeOnBackKeyPressed
                         + " imeWindowVisible=" + imeWindowVisible
                         + " targetWindowToken=" + targetWindowToken);
             }
+            mPolicy.setDismissImeOnBackKeyPressed(dismissImeOnBackKeyPressed);
         }
 
         @Override
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 86662b9..74ecd11 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -68,12 +68,15 @@
 
 using android::OK;
 using android::sp;
+using android::wp;
 using android::status_t;
 using android::String16;
 
 using android::hardware::Return;
 using android::hardware::Void;
 using android::hardware::hidl_vec;
+using android::hardware::hidl_death_recipient;
+using android::hidl::base::V1_0::IBase;
 
 using android::hardware::gnss::V1_0::IAGnss;
 using android::hardware::gnss::V1_0::IAGnssCallback;
@@ -97,7 +100,18 @@
 using android::hardware::gnss::V1_0::IGnssXtra;
 using android::hardware::gnss::V1_0::IGnssXtraCallback;
 
+struct GnssDeathRecipient : virtual public hidl_death_recipient
+{
+    // hidl_death_recipient interface
+    virtual void serviceDied(uint64_t cookie, const wp<IBase>& who) override {
+      // TODO(gomo): implement a better death recovery mechanism without
+      // crashing system server process as described in go//treble-gnss-death
+      LOG_ALWAYS_FATAL("Abort due to IGNSS hidl service failure,"
+            " restarting system server");
+    }
+};
 
+sp<GnssDeathRecipient> gnssHalDeathRecipient = nullptr;
 sp<IGnss> gnssHal = nullptr;
 sp<IGnssXtra> gnssXtraIface = nullptr;
 sp<IAGnssRil> agnssRilIface = nullptr;
@@ -1038,6 +1052,18 @@
     // TODO(b/31632518)
     gnssHal = IGnss::getService();
     if (gnssHal != nullptr) {
+      gnssHalDeathRecipient = new GnssDeathRecipient();
+      hardware::Return<bool> linked = gnssHal->linkToDeath(
+          gnssHalDeathRecipient, /*cookie*/ 0);
+        if (!linked.isOk()) {
+            ALOGE("Transaction error in linking to GnssHAL death: %s",
+                    linked.description().c_str());
+        } else if (!linked) {
+            ALOGW("Unable to link to GnssHal death notifications");
+        } else {
+            ALOGD("Link to death notification successful");
+        }
+
         auto gnssXtra = gnssHal->getExtensionXtra();
         if (!gnssXtra.isOk()) {
             ALOGD("Unable to get a handle to Xtra");
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index bfa1b99..e82ba9c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -742,8 +742,8 @@
         boolean forceEphemeralUsers = false; // Can only be set by a device owner.
         boolean isNetworkLoggingEnabled = false; // Can only be set by a device owner.
 
-        // one notification after enabling + 3 more after reboots
-        static final int DEF_MAXIMUM_NETWORK_LOGGING_NOTIFICATIONS_SHOWN = 4;
+        // one notification after enabling + one more after reboots
+        static final int DEF_MAXIMUM_NETWORK_LOGGING_NOTIFICATIONS_SHOWN = 2;
         int numNetworkLoggingNotifications = 0;
         long lastNetworkLoggingNotificationTimeMs = 0; // Time in milliseconds since epoch
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 42a9232..978803d 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -159,7 +159,7 @@
     private static final String PRINT_MANAGER_SERVICE_CLASS =
             "com.android.server.print.PrintManagerService";
     private static final String COMPANION_DEVICE_MANAGER_SERVICE_CLASS =
-            "com.android.server.print.CompanionDeviceManagerService";
+            "com.android.server.companion.CompanionDeviceManagerService";
     private static final String USB_SERVICE_CLASS =
             "com.android.server.usb.UsbService$Lifecycle";
     private static final String MIDI_SERVICE_CLASS =
diff --git a/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java b/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java
new file mode 100644
index 0000000..24cb72e
--- /dev/null
+++ b/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.notification;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.eq;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class GlobalSortKeyComparatorTest {
+
+    private final String PKG = "PKG";
+    private final int UID = 1111111;
+    private static final String TEST_CHANNEL_ID = "test_channel_id";
+
+    @Test
+    public void testComparator() throws Exception {
+        Notification n = new Notification.Builder(
+                InstrumentationRegistry.getContext(), TEST_CHANNEL_ID)
+                .build();
+        NotificationRecord left = new NotificationRecord(InstrumentationRegistry.getContext(),
+                new StatusBarNotification(PKG,
+                        PKG, 1, "media", UID, UID, n,
+                        new UserHandle(UserHandle.myUserId()),
+                        "", 1499), getDefaultChannel());
+        left.setGlobalSortKey("first");
+
+        NotificationRecord right = new NotificationRecord(InstrumentationRegistry.getContext(),
+                new StatusBarNotification(PKG,
+                        PKG, 1, "media", UID, UID, n,
+                        new UserHandle(UserHandle.myUserId()),
+                        "", 1499), getDefaultChannel());
+        right.setGlobalSortKey("second");
+
+        NotificationRecord last = new NotificationRecord(InstrumentationRegistry.getContext(),
+                new StatusBarNotification(PKG,
+                        PKG, 1, "media", UID, UID, n,
+                        new UserHandle(UserHandle.myUserId()),
+                        "", 1499), getDefaultChannel());
+
+
+        final List<NotificationRecord> expected = new ArrayList<>();
+        expected.add(left);
+        expected.add(right);
+        expected.add(last);
+
+        List<NotificationRecord> actual = new ArrayList<>();
+        actual.addAll(expected);
+        Collections.shuffle(actual);
+
+        Collections.sort(actual, new GlobalSortKeyComparator());
+
+        assertEquals(expected, actual);
+    }
+
+    @Test
+    public void testNoCrash_leftNull() throws Exception {
+        Notification n = new Notification.Builder(
+                InstrumentationRegistry.getContext(), TEST_CHANNEL_ID)
+                .build();
+        NotificationRecord left = new NotificationRecord(InstrumentationRegistry.getContext(),
+                new StatusBarNotification(PKG,
+                        PKG, 1, "media", UID, UID, n,
+                        new UserHandle(UserHandle.myUserId()),
+                        "", 1499), getDefaultChannel());
+
+        NotificationRecord right = new NotificationRecord(InstrumentationRegistry.getContext(),
+                new StatusBarNotification(PKG,
+                        PKG, 1, "media", UID, UID, n,
+                        new UserHandle(UserHandle.myUserId()),
+                        "", 1499), getDefaultChannel());
+        right.setGlobalSortKey("not null");
+
+        final List<NotificationRecord> expected = new ArrayList<>();
+        expected.add(right);
+        expected.add(left);
+
+        List<NotificationRecord> actual = new ArrayList<>();
+        actual.addAll(expected);
+        Collections.shuffle(actual);
+
+        Collections.sort(actual, new GlobalSortKeyComparator());
+
+        assertEquals(expected, actual);
+    }
+
+    @Test
+    public void testNoCrash_rightNull() throws Exception {
+        Notification n = new Notification.Builder(
+                InstrumentationRegistry.getContext(), TEST_CHANNEL_ID)
+                .build();
+        NotificationRecord left = new NotificationRecord(InstrumentationRegistry.getContext(),
+                new StatusBarNotification(PKG,
+                        PKG, 1, "media", UID, UID, n,
+                        new UserHandle(UserHandle.myUserId()),
+                        "", 1499), getDefaultChannel());
+        left.setGlobalSortKey("not null");
+
+        NotificationRecord right = new NotificationRecord(InstrumentationRegistry.getContext(),
+                new StatusBarNotification(PKG,
+                        PKG, 1, "media", UID, UID, n,
+                        new UserHandle(UserHandle.myUserId()),
+                        "", 1499), getDefaultChannel());
+
+        final List<NotificationRecord> expected = new ArrayList<>();
+        expected.add(left);
+        expected.add(right);
+
+        List<NotificationRecord> actual = new ArrayList<>();
+        actual.addAll(expected);
+        Collections.shuffle(actual);
+
+        Collections.sort(actual, new GlobalSortKeyComparator());
+
+        assertEquals(expected, actual);
+    }
+
+    private NotificationChannel getDefaultChannel() {
+        return new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "name",
+                NotificationManager.IMPORTANCE_LOW);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/BootReceiverFixFsckFsStatTest.java b/services/tests/servicestests/src/com/android/server/BootReceiverFixFsckFsStatTest.java
new file mode 100644
index 0000000..362c47a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/BootReceiverFixFsckFsStatTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static junit.framework.Assert.*;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BootReceiverFixFsckFsStatTest {
+
+    private static final String PARTITION = "userdata";
+
+    @Test
+    public void testTreeOptimization() {
+        final String[] logs = {
+                "e2fsck 1.43.3 (04-Sep-2016)",
+                "Pass 1: Checking inodes, blocks, and sizes",
+                "Inode 877141 extent tree (at level 1) could be shorter.  Fix? yes",
+                " ",
+                "Pass 1E: Optimizing extent trees",
+                "Pass 2: Checking directory structure",
+                "Pass 3: Checking directory connectivity",
+                "Pass 4: Checking reference counts",
+                "Pass 5: Checking group summary information",
+                "[QUOTA WARNING] Usage inconsistent for ID 10038:actual (71667712, 1000) != expected (71671808, 1000)",
+                "Update quota info for quota type 0? yes",
+                " ",
+                "[QUOTA WARNING] Usage inconsistent for ID 10038:actual (59555840, 953) != expected (59559936, 953)",
+                "Update quota info for quota type 1? yes",
+                " ",
+                "/dev/block/platform/soc/624000.ufshc/by-name/userdata: ***** FILE SYSTEM WAS MODIFIED *****"
+        };
+        doTestFsckFsStat(logs, 0x405, 5, 0, logs.length);
+
+        final String[] doubleLogs = new String[logs.length * 2];
+        System.arraycopy(logs, 0, doubleLogs, 0, logs.length);
+        System.arraycopy(logs, 0, doubleLogs, logs.length, logs.length);
+        doTestFsckFsStat(doubleLogs, 0x401, 1, 0, logs.length);
+        doTestFsckFsStat(doubleLogs, 0x402, 2, logs.length, logs.length * 2);
+    }
+
+    @Test
+    public void testQuotaOnly() {
+        final String[] logs = {
+                "e2fsck 1.43.3 (04-Sep-2016)",
+                "Pass 1: Checking inodes, blocks, and sizes",
+                "Pass 1E: Optimizing extent trees",
+                "Pass 2: Checking directory structure",
+                "Pass 3: Checking directory connectivity",
+                "Pass 4: Checking reference counts",
+                "Pass 5: Checking group summary information",
+                "[QUOTA WARNING] Usage inconsistent for ID 10038:actual (71667712, 1000) != expected (71671808, 1000)",
+                "Update quota info for quota type 0? yes",
+                " ",
+                "[QUOTA WARNING] Usage inconsistent for ID 10038:actual (59555840, 953) != expected (59559936, 953)",
+                "Update quota info for quota type 1? yes",
+                " ",
+                "/dev/block/platform/soc/624000.ufshc/by-name/userdata: ***** FILE SYSTEM WAS MODIFIED *****"
+        };
+        doTestFsckFsStat(logs, 0x405, 0x405, 0, logs.length);
+    }
+
+    @Test
+    public void testOrphaned() {
+        final String[] logs = {
+                "e2fsck 1.43.3 (04-Sep-2016)",
+                "Pass 1: Checking inodes, blocks, and sizes",
+                "Inodes that were part of a corrupted orphan linked list found.  Fix? yes",
+                " ",
+                "Inode 589877 was part of the orphaned inode list.  FIXED.",
+                " ",
+                "Inode 589878 was part of the orphaned inode list.  FIXED.",
+                " ",
+                "Pass 2: Checking directory structure",
+                "Pass 3: Checking directory connectivity",
+                "Pass 4: Checking reference counts",
+                "Pass 5: Checking group summary information",
+                " ",
+                "/dev/block/platform/soc/624000.ufshc/by-name/userdata: ***** FILE SYSTEM WAS MODIFIED *****"
+        };
+        doTestFsckFsStat(logs, 0x405, 0x405, 0, logs.length);
+    }
+
+    private void doTestFsckFsStat(String[] lines, int statOrg, int statUpdated, int startLineNumber,
+            int endLineNumber) {
+        assertEquals(statUpdated, BootReceiver.fixFsckFsStat(PARTITION, statOrg, lines,
+                startLineNumber, endLineNumber));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
index 54ecab3..f75d49c 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -16,7 +16,8 @@
 
 package com.android.server.am;
 
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 
 import android.content.ComponentName;
 import android.platform.test.annotations.Presubmit;
@@ -36,50 +37,61 @@
 @Presubmit
 @RunWith(AndroidJUnit4.class)
 public class ActivityRecordTests extends ActivityTestsBase {
+    private static final int TEST_STACK_ID = 100;
+
     private final ComponentName testActivityComponent =
             ComponentName.unflattenFromString("com.foo/.BarActivity");
     @Test
     public void testStackCleanupOnClearingTask() throws Exception {
         final ActivityManagerService service = createActivityManagerService();
-        final TestActivityStack testStack = new ActivityStackBuilder(service).build();
-        final TaskRecord task = createTask(service, testActivityComponent, testStack);
+        final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
         final ActivityRecord record = createActivity(service, testActivityComponent, task);
 
         record.setTask(null);
-        assertTrue(testStack.onActivityRemovedFromStackInvocationCount() == 1);
+        assertEquals(getActivityRemovedFromStackCount(service, TEST_STACK_ID), 1);
     }
 
     @Test
     public void testStackCleanupOnActivityRemoval() throws Exception {
         final ActivityManagerService service = createActivityManagerService();
-        final TestActivityStack testStack = new ActivityStackBuilder(service).build();
-        final TaskRecord task = createTask(service, testActivityComponent, testStack);
+        final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
         final ActivityRecord record = createActivity(service, testActivityComponent, task);
 
         task.removeActivity(record);
-        assertTrue(testStack.onActivityRemovedFromStackInvocationCount() == 1);
+        assertEquals(getActivityRemovedFromStackCount(service, TEST_STACK_ID),  1);
     }
 
     @Test
     public void testStackCleanupOnTaskRemoval() throws Exception {
         final ActivityManagerService service = createActivityManagerService();
-        final TestActivityStack testStack = new ActivityStackBuilder(service).build();
-        final TaskRecord task = createTask(service, testActivityComponent, testStack);
+        final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
         final ActivityRecord record = createActivity(service, testActivityComponent, task);
 
-        testStack.removeTask(task, null /*reason*/, ActivityStack.REMOVE_TASK_MODE_MOVING);
-        assertTrue(testStack.onActivityRemovedFromStackInvocationCount() == 1);
+        service.mStackSupervisor.getStack(TEST_STACK_ID)
+                .removeTask(task, null /*reason*/, ActivityStack.REMOVE_TASK_MODE_MOVING);
+
+        // Stack should be gone on task removal.
+        assertNull(service.mStackSupervisor.getStack(TEST_STACK_ID));
     }
 
     @Test
     public void testNoCleanupMovingActivityInSameStack() throws Exception {
         final ActivityManagerService service = createActivityManagerService();
-        final TestActivityStack testStack = new ActivityStackBuilder(service).build();
-        final TaskRecord oldTask = createTask(service, testActivityComponent, testStack);
+        final TaskRecord oldTask = createTask(service, testActivityComponent, TEST_STACK_ID);
         final ActivityRecord record = createActivity(service, testActivityComponent, oldTask);
-        final TaskRecord newTask = createTask(service, testActivityComponent, testStack);
+        final TaskRecord newTask = createTask(service, testActivityComponent, TEST_STACK_ID);
 
         record.reparent(newTask, 0, null /*reason*/);
-        assertTrue(testStack.onActivityRemovedFromStackInvocationCount() == 0);
+        assertEquals(getActivityRemovedFromStackCount(service, TEST_STACK_ID), 0);
+    }
+
+    private static int getActivityRemovedFromStackCount(ActivityManagerService service,
+            int stackId) {
+        final ActivityStack stack = service.mStackSupervisor.getStack(stackId);
+        if (stack instanceof ActivityStackReporter) {
+            return ((ActivityStackReporter) stack).onActivityRemovedFromStackInvocationCount();
+        }
+
+        return -1;
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
index 8423aff..fc9ab96 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -16,8 +16,14 @@
 
 package com.android.server.am;
 
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
+import android.content.ComponentName;
+import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -25,6 +31,10 @@
 import org.junit.runner.RunWith;
 import org.junit.Test;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
 import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
 
 /**
@@ -37,6 +47,9 @@
 @Presubmit
 @RunWith(AndroidJUnit4.class)
 public class ActivityStackSupervisorTests extends ActivityTestsBase {
+    private final ComponentName testActivityComponent =
+            ComponentName.unflattenFromString("com.foo/.BarActivity");
+
     /**
      * This test ensures that we do not try to restore a task based off an invalid task id. The
      * stack supervisor is a test version so there will be no tasks present. We should expect
@@ -49,4 +62,59 @@
                 MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, 0 /*stackId*/);
         assertNull(task);
     }
+
+    /**
+     * This test ensures that an existing task in the pinned stack is moved to the fullscreen
+     * activity stack when a new task is added.
+     */
+    @Test
+    public void testReplacingTaskInPinnedStack() throws Exception {
+        final ActivityManagerService service = createActivityManagerService();
+        final TaskRecord firstTask = createTask(service, testActivityComponent,
+                FULLSCREEN_WORKSPACE_STACK_ID);
+        final ActivityRecord firstActivity = createActivity(service, testActivityComponent,
+                firstTask);
+        // Create a new task on the full screen stack
+        final TaskRecord secondTask = createTask(service, testActivityComponent,
+                FULLSCREEN_WORKSPACE_STACK_ID);
+        final ActivityRecord secondActivity = createActivity(service, testActivityComponent,
+                secondTask);
+        service.mStackSupervisor.setFocusStackUnchecked("testReplacingTaskInPinnedStack",
+                service.mStackSupervisor.getStack(FULLSCREEN_WORKSPACE_STACK_ID));
+
+        // Ensure full screen stack has both tasks.
+        ensureStackPlacement(service.mStackSupervisor, FULLSCREEN_WORKSPACE_STACK_ID, firstTask,
+                secondTask);
+
+        // Move first activity to pinned stack.
+        service.mStackSupervisor.moveActivityToPinnedStackLocked(firstActivity,
+                new Rect() /*sourceBounds*/, 0f /*aspectRatio*/, false, "initialMove");
+
+        // Ensure a task has moved over.
+        ensureStackPlacement(service.mStackSupervisor, PINNED_STACK_ID, firstTask);
+        ensureStackPlacement(service.mStackSupervisor, FULLSCREEN_WORKSPACE_STACK_ID, secondTask);
+
+        // Move second activity to pinned stack.
+        service.mStackSupervisor.moveActivityToPinnedStackLocked(secondActivity,
+                new Rect() /*sourceBounds*/, 0f /*aspectRatio*/ /*destBounds*/, false, "secondMove");
+
+        // Ensure stacks have swapped tasks.
+        ensureStackPlacement(service.mStackSupervisor, PINNED_STACK_ID, secondTask);
+        ensureStackPlacement(service.mStackSupervisor, FULLSCREEN_WORKSPACE_STACK_ID, firstTask);
+    }
+
+    private static void ensureStackPlacement(ActivityStackSupervisor supervisor, int stackId,
+            TaskRecord... tasks) {
+        final ActivityStack stack = supervisor.getStack(stackId);
+        final ArrayList<TaskRecord> stackTasks = stack.getAllTasks();
+        assertEquals(stackTasks.size(), tasks != null ? tasks.length : 0);
+
+        if (tasks == null) {
+            return;
+        }
+
+        for (TaskRecord task : tasks) {
+            assertTrue(stackTasks.contains(task));
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index 1d80b33..f42abf1 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -37,28 +37,27 @@
 @Presubmit
 @RunWith(AndroidJUnit4.class)
 public class ActivityStackTests extends ActivityTestsBase {
-    private final ComponentName testActivityComponent =
+    private static final int TEST_STACK_ID = 100;
+    private static final ComponentName testActivityComponent =
             ComponentName.unflattenFromString("com.foo/.BarActivity");
 
     @Test
     public void testEmptyTaskCleanupOnRemove() throws Exception {
         final ActivityManagerService service = createActivityManagerService();
-        final TestActivityStack testStack = new ActivityStackBuilder(service).build();
-        final TaskRecord task = createTask(service, testActivityComponent, testStack);
+        final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
         assertNotNull(task.getWindowContainerController());
-        testStack.removeTask(task, "testEmptyTaskCleanupOnRemove",
-                ActivityStack.REMOVE_TASK_MODE_DESTROYING);
+        service.mStackSupervisor.getStack(TEST_STACK_ID).removeTask(task,
+                "testEmptyTaskCleanupOnRemove", ActivityStack.REMOVE_TASK_MODE_DESTROYING);
         assertNull(task.getWindowContainerController());
     }
     @Test
     public void testOccupiedTaskCleanupOnRemove() throws Exception {
         final ActivityManagerService service = createActivityManagerService();
-        final TestActivityStack testStack = new ActivityStackBuilder(service).build();
-        final TaskRecord task = createTask(service, testActivityComponent, testStack);
+        final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
         final ActivityRecord activityRecord = createActivity(service, testActivityComponent, task);
         assertNotNull(task.getWindowContainerController());
-        testStack.removeTask(task, "testOccupiedTaskCleanupOnRemove",
-                ActivityStack.REMOVE_TASK_MODE_DESTROYING);
+        service.mStackSupervisor.getStack(TEST_STACK_ID).removeTask(task,
+                "testOccupiedTaskCleanupOnRemove", ActivityStack.REMOVE_TASK_MODE_DESTROYING);
         assertNotNull(task.getWindowContainerController());
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 3bf0e5f..0827084 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -17,6 +17,11 @@
 package com.android.server.am;
 
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doAnswer;
+
+import org.mockito.invocation.InvocationOnMock;
 
 import android.app.ActivityManager;
 import android.content.ComponentName;
@@ -33,6 +38,7 @@
 import com.android.server.wm.AppWindowContainerController;
 import com.android.server.wm.StackWindowController;
 
+import com.android.server.wm.TaskWindowContainerController;
 import com.android.server.wm.WindowManagerService;
 import com.android.server.wm.WindowTestUtils;
 import org.junit.After;
@@ -64,16 +70,15 @@
 
     protected ActivityManagerService createActivityManagerService() {
         final ActivityManagerService service = new TestActivityManagerService(mContext);
-        service.mWindowManager = WindowTestUtils.getWindowManagerService(mContext);
+        service.mWindowManager = WindowTestUtils.getMockWindowManagerService();
         return service;
     }
 
-    protected static TestActivityStack createActivityStack(ActivityManagerService service,
+    protected static ActivityStack createActivityStack(ActivityManagerService service,
             int stackId, int displayId, boolean onTop) {
         if (service.mStackSupervisor instanceof TestActivityStackSupervisor) {
-            final TestActivityStack stack = ((TestActivityStackSupervisor) service.mStackSupervisor)
+            return ((TestActivityStackSupervisor) service.mStackSupervisor)
                     .createTestStack(service, stackId, onTop);
-            return stack;
         }
 
         return null;
@@ -103,7 +108,7 @@
     }
 
     protected static TaskRecord createTask(ActivityManagerService service,
-            ComponentName component, ActivityStack stack) {
+            ComponentName component, int stackId) {
         final ActivityInfo aInfo = new ActivityInfo();
         aInfo.applicationInfo = new ApplicationInfo();
         aInfo.applicationInfo.packageName = component.getPackageName();
@@ -113,13 +118,16 @@
 
         final TaskRecord task = new TaskRecord(service, 0, aInfo, intent /*intent*/,
                 null /*_taskDescription*/, new ActivityManager.TaskThumbnailInfo());
+        final ActivityStack stack = service.mStackSupervisor.getStack(stackId,
+                true /*createStaticStackIfNeeded*/, true /*onTop*/);
         stack.addTask(task, true, "creating test task");
         task.setStack(stack);
-        task.createWindowContainer(true, true);
+        task.setWindowContainerController(mock(TaskWindowContainerController.class));
 
         return task;
     }
 
+
     /**
      * An {@link ActivityManagerService} subclass which provides a test
      * {@link ActivityStackSupervisor}.
@@ -127,6 +135,9 @@
     protected static class TestActivityManagerService extends ActivityManagerService {
         public TestActivityManagerService(Context context) {
             super(context);
+            mSupportsMultiWindow = true;
+            mSupportsMultiDisplay = true;
+            mWindowManager = WindowTestUtils.getWindowManagerService(context);
         }
 
         @Override
@@ -142,6 +153,12 @@
     protected static class TestActivityStackSupervisor extends ActivityStackSupervisor {
         public TestActivityStackSupervisor(ActivityManagerService service, Looper looper) {
             super(service, looper);
+            mWindowManager = prepareMockWindowManager();
+        }
+
+        // No home stack is set.
+        @Override
+        void moveHomeStackToFront(String reason) {
         }
 
         // Invoked during {@link ActivityStack} creation.
@@ -149,18 +166,45 @@
         void updateUIDsPresentOnDisplay() {
         }
 
-        public TestActivityStack createTestStack(ActivityManagerService service, int stackId,
-                boolean onTop) {
+        // Just return the current front task.
+        @Override
+        ActivityStack getNextFocusableStackLocked(ActivityStack currentFocus) {
+            return mFocusedStack;
+        }
+
+        // Called when moving activity to pinned stack.
+        @Override
+        void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
+                boolean preserveWindows) {
+        }
+
+        public <T extends ActivityStack> T createTestStack(ActivityManagerService service,
+                int stackId, boolean onTop) {
             final ActivityDisplay display = new ActivityDisplay();
             final TestActivityContainer container =
                     new TestActivityContainer(service, stackId, display, onTop);
-            return container.getStack();
+            mActivityContainers.put(stackId, container);
+            return (T) container.getStack();
+        }
+
+        @Override
+        protected <T extends ActivityStack> T getStack(int stackId,
+                boolean createStaticStackIfNeeded, boolean createOnTop) {
+            final T stack = super.getStack(stackId, createStaticStackIfNeeded, createOnTop);
+
+            if (stack != null || !createStaticStackIfNeeded) {
+                return stack;
+            }
+
+            return createTestStack(mService, stackId, createOnTop);
         }
 
         private class TestActivityContainer extends ActivityContainer {
-            private ActivityManagerService mService;
-            private TestActivityStack mStack;
+            private final ActivityManagerService mService;
+
             private boolean mOnTop;
+            private int mStackId;
+            private ActivityStack mStack;
 
             TestActivityContainer(ActivityManagerService service, int stackId,
                     ActivityDisplay activityDisplay, boolean onTop) {
@@ -174,12 +218,16 @@
                 // we cannot set {@link mService} by the time the super constructor calling this
                 // method is invoked.
                 mOnTop = onTop;
+                mStackId = stackId;
             }
 
-            public TestActivityStack getStack() {
+            public ActivityStack getStack() {
                 if (mStack == null) {
-                    mStack = new TestActivityStack(this,
-                            new RecentTasks(mService, mService.mStackSupervisor), mOnTop);
+                    final RecentTasks recents =
+                            new RecentTasks(mService, mService.mStackSupervisor);
+                    mStack = mStackId == ActivityManager.StackId.PINNED_STACK_ID
+                    ? new PinnedActivityStack(this, recents, mOnTop)
+                    : new TestActivityStack(this, recents, mOnTop);
                 }
 
                 return mStack;
@@ -187,13 +235,31 @@
         }
     }
 
+    private static WindowManagerService prepareMockWindowManager() {
+        final WindowManagerService service = mock(WindowManagerService.class);
+
+        doAnswer((InvocationOnMock invocationOnMock) -> {
+            final Runnable runnable = invocationOnMock.<Runnable>getArgument(0);
+            if (runnable != null) {
+                runnable.run();
+            }
+            return null;
+        }).when(service).inSurfaceTransaction(any());
+
+        return service;
+    }
+
+    protected interface ActivityStackReporter {
+        int onActivityRemovedFromStackInvocationCount();
+    }
+
     /**
      * Override of {@link ActivityStack} that tracks test metrics, such as the number of times a
      * method is called. Note that its functionality depends on the implementations of the
      * construction arguments.
      */
     protected static class TestActivityStack<T extends StackWindowController>
-            extends ActivityStack<T> {
+            extends ActivityStack<T> implements ActivityStackReporter {
         private int mOnActivityRemovedFromStackCount = 0;
         private T mContainerController;
         TestActivityStack(ActivityStackSupervisor.ActivityContainer activityContainer,
@@ -208,6 +274,7 @@
         }
 
         // Returns the number of times {@link #onActivityRemovedFromStack} has been called
+        @Override
         public int onActivityRemovedFromStackInvocationCount() {
             return mOnActivityRemovedFromStackCount;
         }
@@ -225,6 +292,7 @@
         }
     }
 
+
     protected static class ActivityStackBuilder {
         private boolean mOnTop = true;
         private int mStackId = 0;
@@ -251,7 +319,7 @@
             return this;
         }
 
-        public TestActivityStack build() {
+        public ActivityStack build() {
             return createActivityStack(mService, mStackId, mDisplayId, mOnTop);
         }
     }
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java
index 929a73d..c314de4 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java
@@ -27,6 +27,7 @@
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.util.IconDrawableFactory;
 
 import com.android.server.LocalServices;
 
@@ -155,9 +156,9 @@
     public void testNumberOfBadges() {
         assertTrue("Max profiles greater than number of badges",
                 UserManagerService.MAX_MANAGED_PROFILES
-                <= ApplicationPackageManager.CORP_BADGE_COLORS.length);
+                <= IconDrawableFactory.CORP_BADGE_COLORS.length);
         assertEquals("Num colors doesn't match number of badge labels",
-                ApplicationPackageManager.CORP_BADGE_COLORS.length,
+                IconDrawableFactory.CORP_BADGE_COLORS.length,
                 ApplicationPackageManager.CORP_BADGE_LABEL_RES_ID.length);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index fbeda0a..9392e8e 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -90,6 +90,7 @@
                     return null;
                 }).when(am).notifyKeyguardFlagsChanged(any());
             }
+
             sWm = WindowManagerService.main(context, mock(InputManagerService.class), true, false,
                     false, new TestWindowManagerPolicy());
         }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
index 3a44357..ae3eb52 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
@@ -48,6 +48,13 @@
     }
 
     /**
+     * Retrieves an instance of a mock {@link WindowManagerService}.
+     */
+    public static WindowManagerService getMockWindowManagerService() {
+        return mock(WindowManagerService.class);
+    }
+
+    /**
      * Creates a mock instance of {@link StackWindowController}.
      */
     public static StackWindowController createMockStackWindowContainerController() {
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 80b73d3..31595a6 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -857,13 +857,16 @@
                     break;
                 case MSG_UPDATE_HOST_STATE:
                     SomeArgs args = (SomeArgs) msg.obj;
+                    boolean prevHostConnected = mHostConnected;
                     mHostConnected = (args.argi1 == 1);
                     mSourcePower = (args.argi2 == 1);
                     mSinkPower = (args.argi3 == 1);
                     args.recycle();
                     updateUsbNotification();
                     if (mBootCompleted) {
-                        updateUsbStateBroadcastIfNeeded(false);
+                        if (mHostConnected || prevHostConnected) {
+                            updateUsbStateBroadcastIfNeeded(false);
+                        }
                     } else {
                         mPendingBootBroadcast = true;
                     }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 397aa00..8ee6454 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -911,7 +911,7 @@
      *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      */
     public String getDeviceSoftwareVersion() {
-        return getDeviceSoftwareVersion(getDefaultSim());
+        return getDeviceSoftwareVersion(getSlotIndex());
     }
 
     /**
@@ -997,7 +997,7 @@
      *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      */
     public String getImei() {
-        return getImei(getDefaultSim());
+        return getImei(getSlotIndex());
     }
 
     /**
@@ -1029,7 +1029,7 @@
      *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      */
     public String getMeid() {
-        return getMeid(getDefaultSim());
+        return getMeid(getSlotIndex());
     }
 
     /**
@@ -1059,7 +1059,7 @@
      */
     /** {@hide}*/
     public String getNai() {
-        return getNai(getDefaultSim());
+        return getNai(getSlotIndex());
     }
 
     /**
@@ -1303,7 +1303,7 @@
     }
 
     private int getPhoneTypeFromProperty() {
-        return getPhoneTypeFromProperty(getDefaultPhone());
+        return getPhoneTypeFromProperty(getPhoneId());
     }
 
     /** {@hide} */
@@ -1317,7 +1317,7 @@
     }
 
     private int getPhoneTypeFromNetworkType() {
-        return getPhoneTypeFromNetworkType(getDefaultPhone());
+        return getPhoneTypeFromNetworkType(getPhoneId());
     }
 
     /** {@hide} */
@@ -1500,7 +1500,7 @@
      * on a CDMA network).
      */
     public String getNetworkOperator() {
-        return getNetworkOperatorForPhone(getDefaultPhone());
+        return getNetworkOperatorForPhone(getPhoneId());
     }
 
     /**
@@ -1546,7 +1546,7 @@
      * @see #createForPhoneAccountHandle(PhoneAccountHandle)
      */
     public String getNetworkSpecifier() {
-        return String.valueOf(mSubId);
+        return String.valueOf(getSubId());
     }
 
     /**
@@ -1565,7 +1565,7 @@
     public PersistableBundle getCarrierConfig() {
         CarrierConfigManager carrierConfigManager = mContext
                 .getSystemService(CarrierConfigManager.class);
-        return carrierConfigManager.getConfigForSubId(mSubId);
+        return carrierConfigManager.getConfigForSubId(getSubId());
     }
 
     /**
@@ -1602,7 +1602,7 @@
      * on a CDMA network).
      */
     public String getNetworkCountryIso() {
-        return getNetworkCountryIsoForPhone(getDefaultPhone());
+        return getNetworkCountryIsoForPhone(getPhoneId());
     }
 
     /**
@@ -1748,6 +1748,9 @@
      * Returns a constant indicating the radio technology (network type)
      * currently in use on the device for data transmission.
      *
+     * If this object has been created with {@link #createForSubscriptionId}, applies to the given
+     * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     *
      * <p>
      * Requires Permission:
      *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
@@ -1772,7 +1775,7 @@
      * @see #NETWORK_TYPE_HSPAP
      */
     public int getDataNetworkType() {
-        return getDataNetworkType(getSubId());
+        return getDataNetworkType(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
     }
 
     /**
@@ -1996,7 +1999,7 @@
      * @return true if a ICC card is present
      */
     public boolean hasIccCard() {
-        return hasIccCard(getDefaultSim());
+        return hasIccCard(getSlotIndex());
     }
 
     /**
@@ -2037,7 +2040,7 @@
      * @see #SIM_STATE_CARD_RESTRICTED
      */
     public int getSimState() {
-        int slotIndex = getDefaultSim();
+        int slotIndex = getSlotIndex();
         // slotIndex may be invalid due to sim being absent. In that case query all slots to get
         // sim state
         if (slotIndex < 0) {
@@ -2166,7 +2169,7 @@
      * @see #getSimState
      */
     public String getSimOperatorName() {
-        return getSimOperatorNameForPhone(getDefaultPhone());
+        return getSimOperatorNameForPhone(getPhoneId());
     }
 
     /**
@@ -2198,7 +2201,7 @@
      * Returns the ISO country code equivalent for the SIM provider's country code.
      */
     public String getSimCountryIso() {
-        return getSimCountryIsoForPhone(getDefaultPhone());
+        return getSimCountryIsoForPhone(getPhoneId());
     }
 
     /**
@@ -2754,7 +2757,7 @@
             ITelephony telephony = getITelephony();
             if (telephony != null) {
                 return telephony
-                        .getVisualVoicemailPackageName(mContext.getOpPackageName(), mSubId);
+                        .getVisualVoicemailPackageName(mContext.getOpPackageName(), getSubId());
             }
         } catch (RemoteException ex) {
         } catch (NullPointerException ex) {
@@ -4057,35 +4060,67 @@
      * subId is returned. Otherwise, the default subId will be returned.
      */
     private int getSubId() {
-      if (mSubId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
-        return getDefaultSubscription();
+      if (SubscriptionManager.isUsableSubIdValue(mSubId)) {
+        return mSubId;
       }
-      return mSubId;
+      return SubscriptionManager.getDefaultSubscriptionId();
     }
 
     /**
-     * Returns Default subscription.
+     * Return an appropriate subscription ID for any situation.
+     *
+     * If this object has been created with {@link #createForSubscriptionId}, then the provided
+     * subId is returned. Otherwise, the preferred subId which is based on caller's context is
+     * returned.
+     * {@see SubscriptionManager#getDefaultDataSubscriptionId()}
+     * {@see SubscriptionManager#getDefaultVoiceSubscriptionId()}
+     * {@see SubscriptionManager#getDefaultSmsSubscriptionId()}
      */
-    private static int getDefaultSubscription() {
-        return SubscriptionManager.getDefaultSubscriptionId();
+    private int getSubId(int preferredSubId) {
+        if (SubscriptionManager.isUsableSubIdValue(mSubId)) {
+            return mSubId;
+        }
+        return preferredSubId;
     }
 
     /**
-     * Returns Default phone.
+     * Return an appropriate phone ID for any situation.
+     *
+     * If this object has been created with {@link #createForSubscriptionId}, then the phoneId
+     * associated with the provided subId is returned. Otherwise, the default phoneId associated
+     * with the default subId will be returned.
      */
-    private static int getDefaultPhone() {
-        return SubscriptionManager.getPhoneId(SubscriptionManager.getDefaultSubscriptionId());
+    private int getPhoneId() {
+        return SubscriptionManager.getPhoneId(getSubId());
     }
 
     /**
-     *  @return default SIM's slot index. If SIM is not inserted, return default SIM slot index.
+     * Return an appropriate phone ID for any situation.
+     *
+     * If this object has been created with {@link #createForSubscriptionId}, then the phoneId
+     * associated with the provided subId is returned. Otherwise, return the phoneId associated
+     * with the preferred subId based on caller's context.
+     * {@see SubscriptionManager#getDefaultDataSubscriptionId()}
+     * {@see SubscriptionManager#getDefaultVoiceSubscriptionId()}
+     * {@see SubscriptionManager#getDefaultSmsSubscriptionId()}
+     */
+    private int getPhoneId(int preferredSubId) {
+        return SubscriptionManager.getPhoneId(getSubId(preferredSubId));
+    }
+
+    /**
+     * Return an appropriate slot index for any situation.
+     *
+     * if this object has been created with {@link #createForSubscriptionId}, then the slot index
+     * associated with the provided subId is returned. Otherwise, return the slot index associated
+     * with the default subId.
+     * If SIM is not inserted, return default SIM slot index.
      *
      * {@hide}
      */
     @VisibleForTesting
-    public int getDefaultSim() {
-        int slotIndex = SubscriptionManager.getSlotIndex(
-                SubscriptionManager.getDefaultSubscriptionId());
+    public int getSlotIndex() {
+        int slotIndex = SubscriptionManager.getSlotIndex(getSubId());
         if (slotIndex == SubscriptionManager.SIM_NOT_INSERTED) {
             slotIndex = SubscriptionManager.DEFAULT_SIM_SLOT_INDEX;
         }
@@ -4393,7 +4428,7 @@
      * Returns null if the query fails.
      *
      *
-     * <p>Requires that the caller has READ_PRIVILEGED_PHONE_STATE
+     * <p>Requires that the caller has READ_PHONE_STATE
      *
      * @return an array of forbidden PLMNs or null if not available
      */
@@ -4406,7 +4441,7 @@
      * Returns null if the query fails.
      *
      *
-     * <p>Requires that the calling app has READ_PRIVILEGED_PHONE_STATE
+     * <p>Requires that the calling app has READ_PHONE_STATE
      *
      * @param subId subscription ID used for authentication
      * @param appType the icc application type, like {@link #APPTYPE_USIM}
@@ -4902,7 +4937,7 @@
     /** @hide */
     @SystemApi
     public List<String> getCarrierPackageNamesForIntent(Intent intent) {
-        return getCarrierPackageNamesForIntentAndPhone(intent, getDefaultPhone());
+        return getCarrierPackageNamesForIntentAndPhone(intent, getPhoneId());
     }
 
     /** @hide */
@@ -5164,7 +5199,7 @@
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                telephony.handleUssdRequest(mSubId, ussdRequest, wrappedCallback);
+                telephony.handleUssdRequest(getSubId(), ussdRequest, wrappedCallback);
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling ITelephony#sendUSSDCode", e);
@@ -5184,7 +5219,8 @@
     public boolean isConcurrentVoiceAndDataSupported() {
         try {
             ITelephony telephony = getITelephony();
-            return (telephony == null ? false : telephony.isConcurrentVoiceAndDataAllowed(mSubId));
+            return (telephony == null ? false : telephony.isConcurrentVoiceAndDataAllowed(
+                    getSubId()));
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling ITelephony#isConcurrentVoiceAndDataAllowed", e);
         }
@@ -5321,6 +5357,8 @@
 
     /**
      * Turns mobile data on or off.
+     * If this object has been created with {@link #createForSubscriptionId}, applies to the given
+     * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
      *
      * <p>Requires Permission:
      *     {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the
@@ -5331,7 +5369,7 @@
      * @see #hasCarrierPrivileges
      */
     public void setDataEnabled(boolean enable) {
-        setDataEnabled(getSubId(), enable);
+        setDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enable);
     }
 
     /** @hide */
@@ -5361,6 +5399,9 @@
     /**
      * Returns whether mobile data is enabled or not.
      *
+     * If this object has been created with {@link #createForSubscriptionId}, applies to the given
+     * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     *
      * <p>Requires one of the following permissions:
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE ACCESS_NETWORK_STATE},
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}, or that the
@@ -5376,7 +5417,7 @@
      */
     @SuppressWarnings("deprecation")
     public boolean isDataEnabled() {
-        return getDataEnabled(getSubId());
+        return getDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
     }
 
     /**
@@ -5626,7 +5667,7 @@
     * @hide
     */
     public void setSimOperatorNumeric(String numeric) {
-        int phoneId = getDefaultPhone();
+        int phoneId = getPhoneId();
         setSimOperatorNumericForPhone(phoneId, numeric);
     }
 
@@ -5646,7 +5687,7 @@
      * @hide
      */
     public void setSimOperatorName(String name) {
-        int phoneId = getDefaultPhone();
+        int phoneId = getPhoneId();
         setSimOperatorNameForPhone(phoneId, name);
     }
 
@@ -5666,7 +5707,7 @@
     * @hide
     */
     public void setSimCountryIso(String iso) {
-        int phoneId = getDefaultPhone();
+        int phoneId = getPhoneId();
         setSimCountryIsoForPhone(phoneId, iso);
     }
 
@@ -5686,7 +5727,7 @@
      * @hide
      */
     public void setSimState(String state) {
-        int phoneId = getDefaultPhone();
+        int phoneId = getPhoneId();
         setSimStateForPhone(phoneId, state);
     }
 
@@ -5711,7 +5752,7 @@
      * @hide
      **/
     public void setSimPowerState(boolean powerUp) {
-        setSimPowerStateForSlot(getDefaultSim(), powerUp);
+        setSimPowerStateForSlot(getSlotIndex(), powerUp);
     }
 
     /**
@@ -5745,7 +5786,7 @@
      * @hide
      */
     public void setBasebandVersion(String version) {
-        int phoneId = getDefaultPhone();
+        int phoneId = getPhoneId();
         setBasebandVersionForPhone(phoneId, version);
     }
 
@@ -5772,7 +5813,7 @@
      * @hide
      */
     public void setPhoneType(int type) {
-        int phoneId = getDefaultPhone();
+        int phoneId = getPhoneId();
         setPhoneType(phoneId, type);
     }
 
@@ -5800,7 +5841,7 @@
      * @hide
      */
     public String getOtaSpNumberSchema(String defaultValue) {
-        int phoneId = getDefaultPhone();
+        int phoneId = getPhoneId();
         return getOtaSpNumberSchemaForPhone(phoneId, defaultValue);
     }
 
@@ -5831,7 +5872,7 @@
      * @hide
      */
     public boolean getSmsReceiveCapable(boolean defaultValue) {
-        int phoneId = getDefaultPhone();
+        int phoneId = getPhoneId();
         return getSmsReceiveCapableForPhone(phoneId, defaultValue);
     }
 
@@ -5862,7 +5903,7 @@
      * @hide
      */
     public boolean getSmsSendCapable(boolean defaultValue) {
-        int phoneId = getDefaultPhone();
+        int phoneId = getPhoneId();
         return getSmsSendCapableForPhone(phoneId, defaultValue);
     }
 
@@ -5890,7 +5931,7 @@
      * @hide
      */
     public void setNetworkOperatorName(String name) {
-        int phoneId = getDefaultPhone();
+        int phoneId = getPhoneId();
         setNetworkOperatorNameForPhone(phoneId, name);
     }
 
@@ -5912,7 +5953,7 @@
      * @hide
      */
     public void setNetworkOperatorNumeric(String numeric) {
-        int phoneId = getDefaultPhone();
+        int phoneId = getPhoneId();
         setNetworkOperatorNumericForPhone(phoneId, numeric);
     }
 
@@ -5932,7 +5973,7 @@
      * @hide
      */
     public void setNetworkRoaming(boolean isRoaming) {
-        int phoneId = getDefaultPhone();
+        int phoneId = getPhoneId();
         setNetworkRoamingForPhone(phoneId, isRoaming);
     }
 
@@ -5956,7 +5997,7 @@
      * @hide
      */
     public void setNetworkCountryIso(String iso) {
-        int phoneId = getDefaultPhone();
+        int phoneId = getPhoneId();
         setNetworkCountryIsoForPhone(phoneId, iso);
     }
 
@@ -5976,11 +6017,15 @@
 
     /**
      * Set the network type currently in use on the device for data transmission.
+     *
+     * If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * phoneId associated with the given subId. Otherwise, applies to the phoneId associated with
+     * {@link SubscriptionManager#getDefaultDataSubscriptionId()}
      * @param type the network type currently in use on the device for data transmission
      * @hide
      */
     public void setDataNetworkType(int type) {
-        int phoneId = getDefaultPhone();
+        int phoneId = getPhoneId(SubscriptionManager.getDefaultDataSubscriptionId());
         setDataNetworkTypeForPhone(phoneId, type);
     }
 
@@ -6200,7 +6245,7 @@
      * @hide
      */
     public String getAidForAppType(int appType) {
-        return getAidForAppType(getDefaultSubscription(), appType);
+        return getAidForAppType(getSubId(), appType);
     }
 
     /**
@@ -6234,7 +6279,7 @@
      * @hide
      */
     public String getEsn() {
-        return getEsn(getDefaultSubscription());
+        return getEsn(getSubId());
     }
 
     /**
@@ -6267,7 +6312,7 @@
      * @hide
      */
     public String getCdmaPrlVersion() {
-        return getCdmaPrlVersion(getDefaultSubscription());
+        return getCdmaPrlVersion(getSubId());
     }
 
     /**
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index a8eb986..b4e3a47 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -530,13 +530,6 @@
         throw new UnsupportedOperationException();
     }
 
-    /** STOPSHIP remove when trial API is turned down */
-    @Override
-    public ComponentName startServiceInForeground(Intent service,
-            int id, Notification notification) {
-        throw new UnsupportedOperationException();
-    }
-
     @Override
     public boolean stopService(Intent service) {
         throw new UnsupportedOperationException();
@@ -554,13 +547,6 @@
         throw new UnsupportedOperationException();
     }
 
-    /** @hide STOPSHIP removed when trial API is turned down */
-    @Override
-    public ComponentName startServiceInForegroundAsUser(Intent service,
-            int id, Notification notification, UserHandle user) {
-        throw new UnsupportedOperationException();
-    }
-
     /** @hide */
     @Override
     public boolean stopServiceAsUser(Intent service, UserHandle user) {
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 20392e7..589dd0c 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -1134,4 +1134,11 @@
     public ComponentName getInstantAppInstallerComponent() {
         throw new UnsupportedOperationException();
     }
+
+    /**
+     * @hide
+     */
+    public String getInstantAppAndroidId(String packageName, UserHandle user) {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/tests/net/java/android/net/NetworkCapabilitiesTest.java b/tests/net/java/android/net/NetworkCapabilitiesTest.java
new file mode 100644
index 0000000..e3b06c8
--- /dev/null
+++ b/tests/net/java/android/net/NetworkCapabilitiesTest.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2017 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;
+
+import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_EIMS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
+import static android.net.NetworkCapabilities.RESTRICTED_CAPABILITIES;
+import static android.net.NetworkCapabilities.UNRESTRICTED_CAPABILITIES;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+
+import android.net.NetworkCapabilities;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class NetworkCapabilitiesTest {
+    @Test
+    public void testMaybeMarkCapabilitiesRestricted() {
+        // verify EIMS is restricted
+        assertEquals((1 << NET_CAPABILITY_EIMS) & RESTRICTED_CAPABILITIES,
+                (1 << NET_CAPABILITY_EIMS));
+
+        // verify CBS is also restricted
+        assertEquals((1 << NET_CAPABILITY_CBS) & RESTRICTED_CAPABILITIES,
+                (1 << NET_CAPABILITY_CBS));
+
+        // verify default is not restricted
+        assertEquals((1 << NET_CAPABILITY_INTERNET) & RESTRICTED_CAPABILITIES, 0);
+
+        // just to see
+        assertEquals(RESTRICTED_CAPABILITIES & UNRESTRICTED_CAPABILITIES, 0);
+
+        // check that internet does not get restricted
+        NetworkCapabilities netCap = new NetworkCapabilities();
+        netCap.addCapability(NET_CAPABILITY_INTERNET);
+        netCap.maybeMarkCapabilitiesRestricted();
+        assertTrue(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
+
+        // metered-ness shouldn't matter
+        netCap = new NetworkCapabilities();
+        netCap.addCapability(NET_CAPABILITY_INTERNET);
+        netCap.addCapability(NET_CAPABILITY_NOT_METERED);
+        netCap.maybeMarkCapabilitiesRestricted();
+        assertTrue(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
+        netCap = new NetworkCapabilities();
+        netCap.addCapability(NET_CAPABILITY_INTERNET);
+        netCap.removeCapability(NET_CAPABILITY_NOT_METERED);
+        netCap.maybeMarkCapabilitiesRestricted();
+        assertTrue(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
+
+        // add EIMS - bundled with unrestricted means it's unrestricted
+        netCap = new NetworkCapabilities();
+        netCap.addCapability(NET_CAPABILITY_INTERNET);
+        netCap.addCapability(NET_CAPABILITY_EIMS);
+        netCap.addCapability(NET_CAPABILITY_NOT_METERED);
+        netCap.maybeMarkCapabilitiesRestricted();
+        assertTrue(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
+        netCap = new NetworkCapabilities();
+        netCap.addCapability(NET_CAPABILITY_INTERNET);
+        netCap.addCapability(NET_CAPABILITY_EIMS);
+        netCap.removeCapability(NET_CAPABILITY_NOT_METERED);
+        netCap.maybeMarkCapabilitiesRestricted();
+        assertTrue(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
+
+        // just a restricted cap should be restricted regardless of meteredness
+        netCap = new NetworkCapabilities();
+        netCap.addCapability(NET_CAPABILITY_EIMS);
+        netCap.addCapability(NET_CAPABILITY_NOT_METERED);
+        netCap.maybeMarkCapabilitiesRestricted();
+        assertFalse(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
+        netCap = new NetworkCapabilities();
+        netCap.addCapability(NET_CAPABILITY_EIMS);
+        netCap.removeCapability(NET_CAPABILITY_NOT_METERED);
+        netCap.maybeMarkCapabilitiesRestricted();
+        assertFalse(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
+
+        // try 2 restricted caps
+        netCap = new NetworkCapabilities();
+        netCap.addCapability(NET_CAPABILITY_CBS);
+        netCap.addCapability(NET_CAPABILITY_EIMS);
+        netCap.addCapability(NET_CAPABILITY_NOT_METERED);
+        netCap.maybeMarkCapabilitiesRestricted();
+        assertFalse(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
+        netCap = new NetworkCapabilities();
+        netCap.addCapability(NET_CAPABILITY_CBS);
+        netCap.addCapability(NET_CAPABILITY_EIMS);
+        netCap.removeCapability(NET_CAPABILITY_NOT_METERED);
+        netCap.maybeMarkCapabilitiesRestricted();
+        assertFalse(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
+    }
+
+}
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 2bf5206..bd2b2a36 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -782,7 +782,79 @@
     }
 }
 
-status_t massageManifest(Bundle* bundle, sp<XMLNode> root)
+static sp<ResourceTable::ConfigList> findEntry(const String16& packageStr, const String16& typeStr,
+                                               const String16& nameStr, ResourceTable* table) {
+  sp<ResourceTable::Package> pkg = table->getPackage(packageStr);
+  if (pkg != NULL) {
+      sp<ResourceTable::Type> type = pkg->getTypes().valueFor(typeStr);
+      if (type != NULL) {
+          return type->getConfigs().valueFor(nameStr);
+      }
+  }
+  return NULL;
+}
+
+static uint16_t getMaxSdkVersion(const sp<ResourceTable::ConfigList>& configList) {
+  const DefaultKeyedVector<ConfigDescription, sp<ResourceTable::Entry>>& entries =
+          configList->getEntries();
+  uint16_t maxSdkVersion = 0u;
+  for (size_t i = 0; i < entries.size(); i++) {
+    maxSdkVersion = std::max(maxSdkVersion, entries.keyAt(i).sdkVersion);
+  }
+  return maxSdkVersion;
+}
+
+static void massageRoundIconSupport(const String16& iconRef, const String16& roundIconRef,
+                                    ResourceTable* table) {
+  bool publicOnly = false;
+  const char* err;
+
+  String16 iconPackage, iconType, iconName;
+  if (!ResTable::expandResourceRef(iconRef.string(), iconRef.size(), &iconPackage, &iconType,
+                                   &iconName, NULL, &table->getAssetsPackage(), &err,
+                                   &publicOnly)) {
+      // Errors will be raised in later XML compilation.
+      return;
+  }
+
+  sp<ResourceTable::ConfigList> iconEntry = findEntry(iconPackage, iconType, iconName, table);
+  if (iconEntry == NULL || getMaxSdkVersion(iconEntry) < SDK_O) {
+      // The icon is not adaptive, so nothing to massage.
+      return;
+  }
+
+  String16 roundIconPackage, roundIconType, roundIconName;
+  if (!ResTable::expandResourceRef(roundIconRef.string(), roundIconRef.size(), &roundIconPackage,
+                                   &roundIconType, &roundIconName, NULL, &table->getAssetsPackage(),
+                                   &err, &publicOnly)) {
+      // Errors will be raised in later XML compilation.
+      return;
+  }
+
+  sp<ResourceTable::ConfigList> roundIconEntry = findEntry(roundIconPackage, roundIconType,
+                                                           roundIconName, table);
+  if (roundIconEntry == NULL || getMaxSdkVersion(roundIconEntry) >= SDK_O) {
+      // The developer explicitly used a v26 compatible drawable as the roundIcon, meaning we should
+      // not generate an alias to the icon drawable.
+      return;
+  }
+
+  String16 aliasValue = String16(String8::format("@%s:%s/%s", String8(iconPackage).string(),
+                                                 String8(iconType).string(),
+                                                 String8(iconName).string()));
+
+  // Add an equivalent v26 entry to the roundIcon for each v26 variant of the regular icon.
+  const DefaultKeyedVector<ConfigDescription, sp<ResourceTable::Entry>>& configList =
+          iconEntry->getEntries();
+  for (size_t i = 0; i < configList.size(); i++) {
+      if (configList.keyAt(i).sdkVersion >= SDK_O) {
+          table->addEntry(SourcePos(), roundIconPackage, roundIconType, roundIconName, aliasValue,
+                          NULL, &configList.keyAt(i));
+      }
+  }
+}
+
+status_t massageManifest(Bundle* bundle, ResourceTable* table, sp<XMLNode> root)
 {
     root = root->searchElement(String16(), String16("manifest"));
     if (root == NULL) {
@@ -916,7 +988,7 @@
             String8 tag(child->getElementName());
             if (tag == "instrumentation") {
                 XMLNode::attribute_entry* attr = child->editAttribute(
-                        String16("http://schemas.android.com/apk/res/android"), String16("targetPackage"));
+                        String16(RESOURCES_ANDROID_NAMESPACE), String16("targetPackage"));
                 if (attr != NULL) {
                     attr->string.setTo(String16(instrumentationPackageNameOverride));
                 }
@@ -924,6 +996,19 @@
         }
     }
     
+    sp<XMLNode> application = root->getChildElement(String16(), String16("application"));
+    if (application != NULL) {
+        XMLNode::attribute_entry* icon_attr = application->editAttribute(
+                String16(RESOURCES_ANDROID_NAMESPACE), String16("icon"));
+        if (icon_attr != NULL) {
+            XMLNode::attribute_entry* round_icon_attr = application->editAttribute(
+                    String16(RESOURCES_ANDROID_NAMESPACE), String16("roundIcon"));
+            if (round_icon_attr != NULL) {
+                massageRoundIconSupport(icon_attr->string, round_icon_attr->string, table);
+            }
+        }
+    }
+
     // Generate split name if feature is present.
     const XMLNode::attribute_entry* attr = root->getAttribute(String16(), String16("featureName"));
     if (attr != NULL) {
@@ -1638,7 +1723,7 @@
     if (manifestTree == NULL) {
         return UNKNOWN_ERROR;
     }
-    err = massageManifest(bundle, manifestTree);
+    err = massageManifest(bundle, &table, manifestTree);
     if (err < NO_ERROR) {
         return err;
     }
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 221f3c2..52b93a9 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -4832,8 +4832,7 @@
     item.resPath = resPath;
     item.file = newFile;
     item.xmlRoot = root->clone();
-    item.needsCompiling = false;    // This step occurs after we parse/assign, so we don't need
-                                    // to do it again.
+    item.needsCompiling = true;
     mWorkQueue.push(item);
 
     // Now mark the old entry as deleted.
diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h
index aff22d4..b340fc5 100644
--- a/tools/aapt/ResourceTable.h
+++ b/tools/aapt/ResourceTable.h
@@ -590,9 +590,10 @@
                        const String16& comment,
                        bool appendComment);
 
+    sp<Package> getPackage(const String16& package);
+
 private:
     void writePublicDefinitions(const String16& package, FILE* fp, bool pub);
-    sp<Package> getPackage(const String16& package);
     sp<Type> getType(const String16& package,
                      const String16& type,
                      const SourcePos& pos,
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 328fc0a..1e77ac1 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -1865,13 +1865,6 @@
     }
 
     @Override
-    public ComponentName startServiceInForeground(Intent service,
-            int id, Notification notification) {
-        // pass
-        return null;
-    }
-
-    @Override
     public boolean stopService(Intent arg0) {
         // pass
         return false;
@@ -1884,13 +1877,6 @@
     }
 
     @Override
-    public ComponentName startServiceInForegroundAsUser(Intent service,
-            int id, Notification notification, UserHandle user) {
-        // pass
-        return null;
-    }
-
-    @Override
     public boolean stopServiceAsUser(Intent arg0, UserHandle arg1) {
         // pass
         return false;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
index ad26bc8..93319a3 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
@@ -928,4 +928,9 @@
     public ComponentName getInstantAppInstallerComponent() {
         return null;
     }
+
+    @Override
+    public String getInstantAppAndroidId(String packageName, UserHandle user) {
+        return null;
+    }
 }
diff --git a/wifi/java/android/net/wifi/aware/DiscoverySession.java b/wifi/java/android/net/wifi/aware/DiscoverySession.java
index 82b3792..bf5c42b 100644
--- a/wifi/java/android/net/wifi/aware/DiscoverySession.java
+++ b/wifi/java/android/net/wifi/aware/DiscoverySession.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.net.NetworkSpecifier;
 import android.net.wifi.RttManager;
 import android.util.Log;
 
@@ -250,8 +251,8 @@
     }
 
     /**
-     * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an
-     * unencrypted WiFi Aware connection (link) to the specified peer. The
+     * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for
+     * an unencrypted WiFi Aware connection (link) to the specified peer. The
      * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
      * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
      * <p>
@@ -276,13 +277,13 @@
      *                   request from only that peer. A RESPONDER may specify a {@code null} -
      *                   indicating that it will accept connection requests from any device.
      *
-     * @return A string to be used to construct
-     * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
+     * @return A {@link NetworkSpecifier} to be used to construct
+     * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to
      * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,
      * android.net.ConnectivityManager.NetworkCallback)}
      * [or other varieties of that API].
      */
-    public String createNetworkSpecifierOpen(@Nullable PeerHandle peerHandle) {
+    public NetworkSpecifier createNetworkSpecifierOpen(@Nullable PeerHandle peerHandle) {
         if (mTerminated) {
             Log.w(TAG, "createNetworkSpecifierOpen: called on terminated session");
             return null;
@@ -302,8 +303,8 @@
     }
 
     /**
-     * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an
-     * encrypted WiFi Aware connection (link) to the specified peer. The
+     * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for
+     * an encrypted WiFi Aware connection (link) to the specified peer. The
      * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
      * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
      * <p>
@@ -329,14 +330,14 @@
      *                   {@link #createNetworkSpecifierOpen(PeerHandle)} API to
      *                   specify an open (unencrypted) link.
      *
-     * @return A string to be used to construct
-     * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
+     * @return A {@link NetworkSpecifier} to be used to construct
+     * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to
      * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,
      * android.net.ConnectivityManager.NetworkCallback)}
      * [or other varieties of that API].
      */
-    public String createNetworkSpecifierPassphrase(@Nullable PeerHandle peerHandle,
-            @NonNull String passphrase) {
+    public NetworkSpecifier createNetworkSpecifierPassphrase(
+            @Nullable PeerHandle peerHandle, @NonNull String passphrase) {
         if (passphrase == null || passphrase.length() == 0) {
             throw new IllegalArgumentException("Passphrase must not be null or empty");
         }
@@ -361,8 +362,8 @@
     }
 
     /**
-     * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an
-     * encrypted WiFi Aware connection (link) to the specified peer. The
+     * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for
+     * an encrypted WiFi Aware connection (link) to the specified peer. The
      * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
      * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
      * <p>
@@ -389,8 +390,8 @@
      *            Passphrase or {@link #createNetworkSpecifierOpen(PeerHandle)} to specify an
      *            open (unencrypted) link.
      *
-     * @return A string to be used to construct
-     * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
+     * @return A {@link NetworkSpecifier} to be used to construct
+     * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to
      * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,
      * android.net.ConnectivityManager.NetworkCallback)}
      * [or other varieties of that API].
@@ -398,7 +399,7 @@
      * @hide
      */
     @SystemApi
-    public String createNetworkSpecifierPmk(@Nullable PeerHandle peerHandle,
+    public NetworkSpecifier createNetworkSpecifierPmk(@Nullable PeerHandle peerHandle,
             @NonNull byte[] pmk) {
         if (pmk == null || pmk.length == 0) {
             throw new IllegalArgumentException("PMK must not be null or empty");
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index 4d3957a..3fcbd4b 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.NetworkRequest;
+import android.net.NetworkSpecifier;
 import android.net.wifi.RttManager;
 import android.os.Binder;
 import android.os.Bundle;
@@ -31,7 +32,6 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
-import android.util.Base64;
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -39,9 +39,6 @@
 
 import libcore.util.HexEncoding;
 
-import org.json.JSONException;
-import org.json.JSONObject;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
@@ -129,65 +126,6 @@
     private static final boolean VDBG = false; // STOPSHIP if true
 
     /**
-     * Keys used to generate a Network Specifier for the Aware network request. The network
-     * specifier is formatted as a JSON string.
-     */
-
-    /**
-     * TYPE: in band, specific peer: role, client_id, session_id, peer_id, pmk/passphrase optional
-     * @hide
-     */
-    public static final int NETWORK_SPECIFIER_TYPE_IB = 0;
-
-    /**
-     * TYPE: in band, any peer: role, client_id, session_id, pmk/passphrase optional
-     * [only permitted for RESPONDER]
-     * @hide
-     */
-    public static final int NETWORK_SPECIFIER_TYPE_IB_ANY_PEER = 1;
-
-    /**
-     * TYPE: out-of-band: role, client_id, peer_mac, pmk/passphrase optional
-     * @hide
-     */
-    public static final int NETWORK_SPECIFIER_TYPE_OOB = 2;
-
-    /**
-     * TYPE: out-of-band, any peer: role, client_id, pmk/passphrase optional
-     * [only permitted for RESPONDER]
-     * @hide
-     */
-    public static final int NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER = 3;
-
-
-    /** @hide */
-    public static final int NETWORK_SPECIFIER_TYPE_MAX_VALID = NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER;
-
-    /** @hide */
-    public static final String NETWORK_SPECIFIER_KEY_TYPE = "type";
-
-    /** @hide */
-    public static final String NETWORK_SPECIFIER_KEY_ROLE = "role";
-
-    /** @hide */
-    public static final String NETWORK_SPECIFIER_KEY_CLIENT_ID = "client_id";
-
-    /** @hide */
-    public static final String NETWORK_SPECIFIER_KEY_SESSION_ID = "session_id";
-
-    /** @hide */
-    public static final String NETWORK_SPECIFIER_KEY_PEER_ID = "peer_id";
-
-    /** @hide */
-    public static final String NETWORK_SPECIFIER_KEY_PEER_MAC = "peer_mac";
-
-    /** @hide */
-    public static final String NETWORK_SPECIFIER_KEY_PMK = "pmk";
-
-    /** @hide */
-    public static final String NETWORK_SPECIFIER_KEY_PASSPHRASE = "passphrase";
-
-    /**
      * Broadcast intent action to indicate that the state of Wi-Fi Aware availability has changed.
      * Use the {@link #isAvailable()} to query the current status.
      * This broadcast is <b>not</b> sticky, use the {@link #isAvailable()} API after registering
@@ -483,7 +421,7 @@
     }
 
     /** @hide */
-    public String createNetworkSpecifier(int clientId, int role, int sessionId,
+    public NetworkSpecifier createNetworkSpecifier(int clientId, int role, int sessionId,
             PeerHandle peerHandle, @Nullable byte[] pmk, @Nullable String passphrase) {
         if (VDBG) {
             Log.v(TAG, "createNetworkSpecifier: role=" + role + ", sessionId=" + sessionId
@@ -492,9 +430,6 @@
                     + ", passphrase=" + ((passphrase == null) ? "null" : "non-null"));
         }
 
-        int type = (peerHandle == null) ? NETWORK_SPECIFIER_TYPE_IB_ANY_PEER
-                : NETWORK_SPECIFIER_TYPE_IB;
-
         if (role != WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
                 && role != WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) {
             throw new IllegalArgumentException(
@@ -509,35 +444,20 @@
             }
         }
 
-        JSONObject json;
-        try {
-            json = new JSONObject();
-            json.put(NETWORK_SPECIFIER_KEY_TYPE, type);
-            json.put(NETWORK_SPECIFIER_KEY_ROLE, role);
-            json.put(NETWORK_SPECIFIER_KEY_CLIENT_ID, clientId);
-            json.put(NETWORK_SPECIFIER_KEY_SESSION_ID, sessionId);
-            if (peerHandle != null) {
-                json.put(NETWORK_SPECIFIER_KEY_PEER_ID, peerHandle.peerId);
-            }
-            if (pmk == null) {
-                pmk = new byte[0];
-            }
-            json.put(NETWORK_SPECIFIER_KEY_PMK,
-                    Base64.encodeToString(pmk, 0, pmk.length, Base64.DEFAULT));
-            if (passphrase == null) {
-                passphrase = new String();
-            }
-            json.put(NETWORK_SPECIFIER_KEY_PASSPHRASE, passphrase);
-
-        } catch (JSONException e) {
-            return "";
-        }
-
-        return json.toString();
+        return new WifiAwareNetworkSpecifier(
+                (peerHandle == null) ? WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB_ANY_PEER
+                        : WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB,
+                role,
+                clientId,
+                sessionId,
+                peerHandle != null ? peerHandle.peerId : 0, // 0 is an invalid peer ID
+                null, // peerMac (not used in this method)
+                pmk,
+                passphrase);
     }
 
     /** @hide */
-    public String createNetworkSpecifier(int clientId, @DataPathRole int role,
+    public NetworkSpecifier createNetworkSpecifier(int clientId, @DataPathRole int role,
             @Nullable byte[] peer, @Nullable byte[] pmk, @Nullable String passphrase) {
         if (VDBG) {
             Log.v(TAG, "createNetworkSpecifier: role=" + role
@@ -545,9 +465,6 @@
                     + ", passphrase=" + ((passphrase == null) ? "null" : "non-null"));
         }
 
-        int type = (peer == null) ?
-                NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER : NETWORK_SPECIFIER_TYPE_OOB;
-
         if (role != WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
                 && role != WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) {
             throw new IllegalArgumentException(
@@ -564,29 +481,16 @@
             throw new IllegalArgumentException("createNetworkSpecifier: Invalid peer MAC address");
         }
 
-        JSONObject json;
-        try {
-            json = new JSONObject();
-            json.put(NETWORK_SPECIFIER_KEY_TYPE, type);
-            json.put(NETWORK_SPECIFIER_KEY_ROLE, role);
-            json.put(NETWORK_SPECIFIER_KEY_CLIENT_ID, clientId);
-            if (peer != null) {
-                json.put(NETWORK_SPECIFIER_KEY_PEER_MAC, new String(HexEncoding.encode(peer)));
-            }
-            if (pmk == null) {
-                pmk = new byte[0];
-            }
-            json.put(NETWORK_SPECIFIER_KEY_PMK,
-                    Base64.encodeToString(pmk, 0, pmk.length, Base64.DEFAULT));
-            if (passphrase == null) {
-                passphrase = new String();
-            }
-            json.put(NETWORK_SPECIFIER_KEY_PASSPHRASE, passphrase);
-        } catch (JSONException e) {
-            return "";
-        }
-
-        return json.toString();
+        return new WifiAwareNetworkSpecifier(
+                (peer == null) ? WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER
+                        : WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_OOB,
+                role,
+                clientId,
+                0, // 0 is an invalid session ID
+                0, // 0 is an invalid peer ID
+                peer,
+                pmk,
+                passphrase);
     }
 
     private static class WifiAwareEventCallbackProxy extends IWifiAwareEventCallback.Stub {
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java
new file mode 100644
index 0000000..5993480
--- /dev/null
+++ b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2017 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.wifi.aware;
+
+import android.net.NetworkSpecifier;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Network specifier object used to request a Wi-Fi Aware network. Apps do not create these objects
+ * directly but obtain them using
+ * {@link WifiAwareSession#createNetworkSpecifierOpen(int, byte[])} or
+ * {@link DiscoverySession#createNetworkSpecifierOpen(PeerHandle)} or their secure (Passphrase)
+ * versions.
+ *
+ * @hide
+ */
+public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements Parcelable {
+    /**
+     * TYPE: in band, specific peer: role, client_id, session_id, peer_id, pmk/passphrase optional
+     * @hide
+     */
+    public static final int NETWORK_SPECIFIER_TYPE_IB = 0;
+
+    /**
+     * TYPE: in band, any peer: role, client_id, session_id, pmk/passphrase optional
+     * [only permitted for RESPONDER]
+     * @hide
+     */
+    public static final int NETWORK_SPECIFIER_TYPE_IB_ANY_PEER = 1;
+
+    /**
+     * TYPE: out-of-band: role, client_id, peer_mac, pmk/passphrase optional
+     * @hide
+     */
+    public static final int NETWORK_SPECIFIER_TYPE_OOB = 2;
+
+    /**
+     * TYPE: out-of-band, any peer: role, client_id, pmk/passphrase optional
+     * [only permitted for RESPONDER]
+     * @hide
+     */
+    public static final int NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER = 3;
+
+    /** @hide */
+    public static final int NETWORK_SPECIFIER_TYPE_MAX_VALID = NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER;
+
+    /**
+     * One of the NETWORK_SPECIFIER_TYPE_* constants. The type of the network specifier object.
+     * @hide
+     */
+    public final int type;
+
+    /**
+     * The role of the device: WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR or
+     * WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER.
+     * @hide
+     */
+    public final int role;
+
+    /**
+     * The client ID of the device.
+     * @hide
+     */
+    public final int clientId;
+
+    /**
+     * The session ID in which context to request a data-path. Only relevant for IB requests.
+     * @hide
+     */
+    public final int sessionId;
+
+    /**
+     * The peer ID of the device which the data-path should be connected to. Only relevant for
+     * IB requests (i.e. not IB_ANY_PEER or OOB*).
+     * @hide
+     */
+    public final int peerId;
+
+    /**
+     * The peer MAC address of the device which the data-path should be connected to. Only relevant
+     * for OB requests (i.e. not OOB_ANY_PEER or IB*).
+     * @hide
+     */
+    public final byte[] peerMac;
+
+    /**
+     * The PMK of the requested data-path. Can be null. Only one or none of pmk or passphrase should
+     * be specified.
+     * @hide
+     */
+    public final byte[] pmk;
+
+    /**
+     * The Passphrase of the requested data-path. Can be null. Only one or none of the pmk or
+     * passphrase should be specified.
+     * @hide
+     */
+    public final String passphrase;
+
+    /** @hide */
+    public WifiAwareNetworkSpecifier(int type, int role, int clientId, int sessionId, int peerId,
+            byte[] peerMac, byte[] pmk, String passphrase) {
+        this.type = type;
+        this.role = role;
+        this.clientId = clientId;
+        this.sessionId = sessionId;
+        this.peerId = peerId;
+        this.peerMac = peerMac;
+        this.pmk = pmk;
+        this.passphrase = passphrase;
+    }
+
+    public static final Creator<WifiAwareNetworkSpecifier> CREATOR =
+            new Creator<WifiAwareNetworkSpecifier>() {
+                @Override
+                public WifiAwareNetworkSpecifier createFromParcel(Parcel in) {
+                    return new WifiAwareNetworkSpecifier(
+                        in.readInt(), // type
+                        in.readInt(), // role
+                        in.readInt(), // clientId
+                        in.readInt(), // sessionId
+                        in.readInt(), // peerId
+                        in.createByteArray(), // peerMac
+                        in.createByteArray(), // pmk
+                        in.readString()); // passphrase
+                }
+
+                @Override
+                public WifiAwareNetworkSpecifier[] newArray(int size) {
+                    return new WifiAwareNetworkSpecifier[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(type);
+        dest.writeInt(role);
+        dest.writeInt(clientId);
+        dest.writeInt(sessionId);
+        dest.writeInt(peerId);
+        dest.writeByteArray(peerMac);
+        dest.writeByteArray(pmk);
+        dest.writeString(passphrase);
+    }
+
+    /** @hide */
+    @Override
+    public boolean satisfiedBy(NetworkSpecifier other) {
+        // MatchAllNetworkSpecifier is taken care in NetworkCapabilities#satisfiedBySpecifier.
+        return equals(other);
+    }
+
+    /** @hide */
+    @Override
+    public int hashCode() {
+        int result = 17;
+
+        result = 31 * result + type;
+        result = 31 * result + role;
+        result = 31 * result + clientId;
+        result = 31 * result + sessionId;
+        result = 31 * result + peerId;
+        result = 31 * result + Arrays.hashCode(peerMac);
+        result = 31 * result + Arrays.hashCode(pmk);
+        result = 31 * result + Objects.hashCode(passphrase);
+
+        return result;
+    }
+
+    /** @hide */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (!(obj instanceof WifiAwareNetworkSpecifier)) {
+            return false;
+        }
+
+        WifiAwareNetworkSpecifier lhs = (WifiAwareNetworkSpecifier) obj;
+
+        return type == lhs.type
+                && role == lhs.role
+                && clientId == lhs.clientId
+                && sessionId == lhs.sessionId
+                && peerId == lhs.peerId
+                && Arrays.equals(peerMac, lhs.peerMac)
+                && Arrays.equals(pmk, lhs.pmk)
+                && Objects.equals(passphrase, lhs.passphrase);
+    }
+
+    /** @hide */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder("WifiAwareNetworkSpecifier [");
+        sb.append("type=").append(type)
+                .append(", role=").append(role)
+                .append(", clientId=").append(clientId)
+                .append(", sessionId=").append(sessionId)
+                .append(", peerId=").append(peerId)
+                // masking potential PII (although low impact information)
+                .append(", peerMac=").append((peerMac == null) ? "<null>" : "<non-null>")
+                // masking PII
+                .append(", pmk=").append((pmk == null) ? "<null>" : "<non-null>")
+                // masking PII
+                .append(", passphrase=").append((passphrase == null) ? "<null>" : "<non-null>")
+                .append("]");
+        return sb.toString();
+    }
+}
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareSession.java b/wifi/java/android/net/wifi/aware/WifiAwareSession.java
index 895defb..ac3a6bb 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareSession.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareSession.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.net.NetworkSpecifier;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.Looper;
@@ -184,8 +185,8 @@
     }
 
     /**
-     * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an
-     * unencrypted WiFi Aware connection (link) to the specified peer. The
+     * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for
+     * an unencrypted WiFi Aware connection (link) to the specified peer. The
      * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
      * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
      * <p>
@@ -205,29 +206,29 @@
      *              peer. A RESPONDER may specify a {@code null} - indicating that it will accept
      *              connection requests from any device.
      *
-     * @return A string to be used to construct
-     * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
+     * @return A {@link NetworkSpecifier} to be used to construct
+     * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to
      * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,
      * android.net.ConnectivityManager.NetworkCallback)}
      * [or other varieties of that API].
      */
-    public String createNetworkSpecifierOpen(@WifiAwareManager.DataPathRole int role,
-            @Nullable byte[] peer) {
+    public NetworkSpecifier createNetworkSpecifierOpen(
+            @WifiAwareManager.DataPathRole int role, @Nullable byte[] peer) {
         WifiAwareManager mgr = mMgr.get();
         if (mgr == null) {
             Log.e(TAG, "createNetworkSpecifierOpen: called post GC on WifiAwareManager");
-            return "";
+            return null;
         }
         if (mTerminated) {
             Log.e(TAG, "createNetworkSpecifierOpen: called after termination");
-            return "";
+            return null;
         }
         return mgr.createNetworkSpecifier(mClientId, role, peer, null, null);
     }
 
     /**
-     * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an
-     * encrypted WiFi Aware connection (link) to the specified peer. The
+     * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for
+     * an encrypted WiFi Aware connection (link) to the specified peer. The
      * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
      * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
      * <p>
@@ -247,22 +248,23 @@
      *                   the passphrase. Use {@link #createNetworkSpecifierOpen(int, byte[])} to
      *                   specify an open (unencrypted) link.
      *
-     * @return A string to be used to construct
-     * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
+     * @return A {@link NetworkSpecifier} to be used to construct
+     * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to
      * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,
      * android.net.ConnectivityManager.NetworkCallback)}
      * [or other varieties of that API].
      */
-    public String createNetworkSpecifierPassphrase(@WifiAwareManager.DataPathRole int role,
-            @Nullable byte[] peer, @NonNull String passphrase) {
+    public NetworkSpecifier createNetworkSpecifierPassphrase(
+            @WifiAwareManager.DataPathRole int role, @Nullable byte[] peer,
+            @NonNull String passphrase) {
         WifiAwareManager mgr = mMgr.get();
         if (mgr == null) {
             Log.e(TAG, "createNetworkSpecifierPassphrase: called post GC on WifiAwareManager");
-            return "";
+            return null;
         }
         if (mTerminated) {
             Log.e(TAG, "createNetworkSpecifierPassphrase: called after termination");
-            return "";
+            return null;
         }
         if (passphrase == null || passphrase.length() == 0) {
             throw new IllegalArgumentException("Passphrase must not be null or empty");
@@ -271,8 +273,8 @@
     }
 
     /**
-     * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an
-     * encrypted WiFi Aware connection (link) to the specified peer. The
+     * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for
+     * an encrypted WiFi Aware connection (link) to the specified peer. The
      * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
      * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
      * <p>
@@ -294,8 +296,8 @@
      *            Passphrase or {@link #createNetworkSpecifierOpen(int, byte[])} to specify an
      *            open (unencrypted) link.
      *
-     * @return A string to be used to construct
-     * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
+     * @return A {@link NetworkSpecifier} to be used to construct
+     * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to
      * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,
      * android.net.ConnectivityManager.NetworkCallback)}
      * [or other varieties of that API].
@@ -303,16 +305,16 @@
      * @hide
      */
     @SystemApi
-    public String createNetworkSpecifierPmk(@WifiAwareManager.DataPathRole int role,
-            @Nullable byte[] peer, @NonNull byte[] pmk) {
+    public NetworkSpecifier createNetworkSpecifierPmk(
+            @WifiAwareManager.DataPathRole int role, @Nullable byte[] peer, @NonNull byte[] pmk) {
         WifiAwareManager mgr = mMgr.get();
         if (mgr == null) {
             Log.e(TAG, "createNetworkSpecifierPmk: called post GC on WifiAwareManager");
-            return "";
+            return null;
         }
         if (mTerminated) {
             Log.e(TAG, "createNetworkSpecifierPmk: called after termination");
-            return "";
+            return null;
         }
         if (pmk == null || pmk.length == 0) {
             throw new IllegalArgumentException("PMK must not be null or empty");
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
index 830db22..72a6a7a 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -34,11 +34,9 @@
 import android.os.Parcel;
 import android.os.test.TestLooper;
 import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Base64;
 
 import libcore.util.HexEncoding;
 
-import org.json.JSONObject;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -959,8 +957,6 @@
         final ConfigRequest configRequest = new ConfigRequest.Builder().build();
         final PublishConfig publishConfig = new PublishConfig.Builder().build();
 
-        String pmkB64 = Base64.encodeToString(pmk, Base64.DEFAULT);
-
         ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
                 WifiAwareSession.class);
         ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
@@ -991,51 +987,37 @@
         inOrder.verify(mockSessionCallback).onPublishStarted(publishSession.capture());
 
         // (3) request an open (unencrypted) network specifier from the session
-        String networkSpecifier = publishSession.getValue().createNetworkSpecifierOpen(peerHandle);
+        WifiAwareNetworkSpecifier ns =
+                (WifiAwareNetworkSpecifier) publishSession.getValue().createNetworkSpecifierOpen(
+                        peerHandle);
 
         // validate format
-        JSONObject jsonObject = new JSONObject(networkSpecifier);
-        collector.checkThat("role", role,
-                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE)));
-        collector.checkThat("client_id", clientId,
-                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID)));
-        collector.checkThat("session_id", sessionId,
-                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_SESSION_ID)));
-        collector.checkThat("peer_id", peerHandle.peerId,
-                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_ID)));
+        collector.checkThat("role", role, equalTo(ns.role));
+        collector.checkThat("client_id", clientId, equalTo(ns.clientId));
+        collector.checkThat("session_id", sessionId, equalTo(ns.sessionId));
+        collector.checkThat("peer_id", peerHandle.peerId, equalTo(ns.peerId));
 
         // (4) request an encrypted (PMK) network specifier from the session
-        networkSpecifier = publishSession.getValue().createNetworkSpecifierPmk(peerHandle, pmk);
+        ns = (WifiAwareNetworkSpecifier) publishSession.getValue().createNetworkSpecifierPmk(
+                peerHandle, pmk);
 
         // validate format
-        jsonObject = new JSONObject(networkSpecifier);
-        collector.checkThat("role", role,
-                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE)));
-        collector.checkThat("client_id", clientId,
-                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID)));
-        collector.checkThat("session_id", sessionId,
-                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_SESSION_ID)));
-        collector.checkThat("peer_id", peerHandle.peerId,
-                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_ID)));
-        collector.checkThat("pmk", pmkB64 ,
-                equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PMK)));
+        collector.checkThat("role", role, equalTo(ns.role));
+        collector.checkThat("client_id", clientId, equalTo(ns.clientId));
+        collector.checkThat("session_id", sessionId, equalTo(ns.sessionId));
+        collector.checkThat("peer_id", peerHandle.peerId, equalTo(ns.peerId));
+        collector.checkThat("pmk", pmk , equalTo(ns.pmk));
 
         // (5) request an encrypted (Passphrase) network specifier from the session
-        networkSpecifier = publishSession.getValue().createNetworkSpecifierPassphrase(peerHandle,
-                passphrase);
+        ns = (WifiAwareNetworkSpecifier) publishSession.getValue().createNetworkSpecifierPassphrase(
+                peerHandle, passphrase);
 
         // validate format
-        jsonObject = new JSONObject(networkSpecifier);
-        collector.checkThat("role", role,
-                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE)));
-        collector.checkThat("client_id", clientId,
-                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID)));
-        collector.checkThat("session_id", sessionId,
-                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_SESSION_ID)));
-        collector.checkThat("peer_id", peerHandle.peerId,
-                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_ID)));
-        collector.checkThat("passphrase", passphrase,
-                equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PASSPHRASE)));
+        collector.checkThat("role", role, equalTo(ns.role));
+        collector.checkThat("client_id", clientId, equalTo(ns.clientId));
+        collector.checkThat("session_id", sessionId, equalTo(ns.sessionId));
+        collector.checkThat("peer_id", peerHandle.peerId, equalTo(ns.peerId));
+        collector.checkThat("passphrase", passphrase, equalTo(ns.passphrase));
 
         verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
                 mockPublishSession, mockRttListener);
@@ -1054,8 +1036,6 @@
         final byte[] pmk = "Some arbitrary pmk data".getBytes();
         final String passphrase = "A really bad password";
 
-        String pmkB64 = Base64.encodeToString(pmk, Base64.DEFAULT);
-
         ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
                 WifiAwareSession.class);
         ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
@@ -1074,47 +1054,32 @@
         WifiAwareSession session = sessionCaptor.getValue();
 
         // (2) request an open (unencrypted) direct network specifier
-        String networkSpecifier = session.createNetworkSpecifierOpen(role, someMac);
+        WifiAwareNetworkSpecifier ns =
+                (WifiAwareNetworkSpecifier) session.createNetworkSpecifierOpen(role, someMac);
 
         // validate format
-        JSONObject jsonObject = new JSONObject(networkSpecifier);
-        collector.checkThat("role", role,
-                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE)));
-        collector.checkThat("client_id", clientId,
-                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID)));
-        collector.checkThat("peer_mac", someMac, equalTo(HexEncoding.decode(
-                jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_MAC).toCharArray(),
-                false)));
+        collector.checkThat("role", role, equalTo(ns.role));
+        collector.checkThat("client_id", clientId, equalTo(ns.clientId));
+        collector.checkThat("peer_mac", someMac, equalTo(ns.peerMac));
 
         // (3) request an encrypted (PMK) direct network specifier
-        networkSpecifier = session.createNetworkSpecifierPmk(role, someMac, pmk);
+        ns = (WifiAwareNetworkSpecifier) session.createNetworkSpecifierPmk(role, someMac, pmk);
 
         // validate format
-        jsonObject = new JSONObject(networkSpecifier);
-        collector.checkThat("role", role,
-                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE)));
-        collector.checkThat("client_id", clientId,
-                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID)));
-        collector.checkThat("peer_mac", someMac, equalTo(HexEncoding.decode(
-                jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_MAC).toCharArray(),
-                false)));
-        collector.checkThat("pmk", pmkB64,
-                equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PMK)));
+        collector.checkThat("role", role, equalTo(ns.role));
+        collector.checkThat("client_id", clientId, equalTo(ns.clientId));
+        collector.checkThat("peer_mac", someMac, equalTo(ns.peerMac));
+        collector.checkThat("pmk", pmk, equalTo(ns.pmk));
 
         // (4) request an encrypted (Passphrase) direct network specifier
-        networkSpecifier = session.createNetworkSpecifierPassphrase(role, someMac, passphrase);
+        ns = (WifiAwareNetworkSpecifier) session.createNetworkSpecifierPassphrase(role, someMac,
+                passphrase);
 
         // validate format
-        jsonObject = new JSONObject(networkSpecifier);
-        collector.checkThat("role", role,
-                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE)));
-        collector.checkThat("client_id", clientId,
-                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID)));
-        collector.checkThat("peer_mac", someMac, equalTo(HexEncoding.decode(
-                jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_MAC).toCharArray(),
-                false)));
-        collector.checkThat("passphrase", passphrase,
-                equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PASSPHRASE)));
+        collector.checkThat("role", role, equalTo(ns.role));
+        collector.checkThat("client_id", clientId, equalTo(ns.clientId));
+        collector.checkThat("peer_mac", someMac, equalTo(ns.peerMac));
+        collector.checkThat("passphrase", passphrase, equalTo(ns.passphrase));
 
         verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
                 mockPublishSession, mockRttListener);