Merge "Minor UI adjustments to a11y button chooser" into oc-dev
diff --git a/api/current.txt b/api/current.txt
index 7a3ec57..b51ea54 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3848,7 +3848,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();
@@ -5604,7 +5604,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 +6624,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();
@@ -6890,7 +6889,7 @@
   }
 
   public abstract class JobServiceEngine {
-    ctor public JobServiceEngine(android.content.Context);
+    ctor public JobServiceEngine(android.app.Service);
     method public final android.os.IBinder getBinder();
     method public final void jobFinished(android.app.job.JobParameters, boolean);
     method public abstract boolean onStartJob(android.app.job.JobParameters);
@@ -22775,7 +22774,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 +22788,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 +22815,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 +22856,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 +22869,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 +22900,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 +22909,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 +22940,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 {
@@ -24011,7 +24017,6 @@
     method public android.content.ComponentName getServiceComponent();
     method public android.media.session.MediaSession.Token getSessionToken();
     method public boolean isConnected();
-    method public void search(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SearchCallback);
     method public void subscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback);
     method public void subscribe(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SubscriptionCallback);
     method public void unsubscribe(java.lang.String);
@@ -24047,12 +24052,6 @@
     field public static final int FLAG_PLAYABLE = 2; // 0x2
   }
 
-  public static abstract class MediaBrowser.SearchCallback {
-    ctor public MediaBrowser.SearchCallback();
-    method public void onError(java.lang.String, android.os.Bundle);
-    method public void onSearchResult(java.lang.String, android.os.Bundle, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
-  }
-
   public static abstract class MediaBrowser.SubscriptionCallback {
     ctor public MediaBrowser.SubscriptionCallback();
     method public void onChildrenLoaded(java.lang.String, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
@@ -24261,8 +24260,6 @@
 
   public final class MediaController {
     ctor public MediaController(android.content.Context, android.media.session.MediaSession.Token);
-    method public void addQueueItem(android.media.MediaDescription);
-    method public void addQueueItem(android.media.MediaDescription, int);
     method public void adjustVolume(int, int);
     method public boolean dispatchMediaButtonEvent(android.view.KeyEvent);
     method public android.os.Bundle getExtras();
@@ -24274,15 +24271,11 @@
     method public java.util.List<android.media.session.MediaSession.QueueItem> getQueue();
     method public java.lang.CharSequence getQueueTitle();
     method public int getRatingType();
-    method public int getRepeatMode();
     method public android.app.PendingIntent getSessionActivity();
     method public android.media.session.MediaSession.Token getSessionToken();
     method public android.media.session.MediaController.TransportControls getTransportControls();
-    method public boolean isShuffleModeEnabled();
     method public void registerCallback(android.media.session.MediaController.Callback);
     method public void registerCallback(android.media.session.MediaController.Callback, android.os.Handler);
-    method public void removeQueueItem(android.media.MediaDescription);
-    method public void removeQueueItemAt(int);
     method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
     method public void setVolumeTo(int, int);
     method public void unregisterCallback(android.media.session.MediaController.Callback);
@@ -24296,10 +24289,8 @@
     method public void onPlaybackStateChanged(android.media.session.PlaybackState);
     method public void onQueueChanged(java.util.List<android.media.session.MediaSession.QueueItem>);
     method public void onQueueTitleChanged(java.lang.CharSequence);
-    method public void onRepeatModeChanged(int);
     method public void onSessionDestroyed();
     method public void onSessionEvent(java.lang.String, android.os.Bundle);
-    method public void onShuffleModeChanged(boolean);
   }
 
   public static final class MediaController.PlaybackInfo {
@@ -24328,8 +24319,6 @@
     method public void sendCustomAction(android.media.session.PlaybackState.CustomAction, android.os.Bundle);
     method public void sendCustomAction(java.lang.String, android.os.Bundle);
     method public void setRating(android.media.Rating);
-    method public void setRepeatMode(int);
-    method public void setShuffleModeEnabled(boolean);
     method public void skipToNext();
     method public void skipToPrevious();
     method public void skipToQueueItem(long);
@@ -24356,18 +24345,13 @@
     method public void setQueue(java.util.List<android.media.session.MediaSession.QueueItem>);
     method public void setQueueTitle(java.lang.CharSequence);
     method public void setRatingType(int);
-    method public void setRepeatMode(int);
     method public void setSessionActivity(android.app.PendingIntent);
-    method public void setShuffleModeEnabled(boolean);
     field public static final deprecated int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
-    field public static final int FLAG_HANDLES_QUEUE_COMMANDS = 4; // 0x4
     field public static final deprecated int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
   }
 
   public static abstract class MediaSession.Callback {
     ctor public MediaSession.Callback();
-    method public void onAddQueueItem(android.media.MediaDescription);
-    method public void onAddQueueItem(android.media.MediaDescription, int);
     method public void onCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
     method public void onCustomAction(java.lang.String, android.os.Bundle);
     method public void onFastForward();
@@ -24381,13 +24365,9 @@
     method public void onPrepareFromMediaId(java.lang.String, android.os.Bundle);
     method public void onPrepareFromSearch(java.lang.String, android.os.Bundle);
     method public void onPrepareFromUri(android.net.Uri, android.os.Bundle);
-    method public void onRemoveQueueItem(android.media.MediaDescription);
-    method public void onRemoveQueueItemAt(int);
     method public void onRewind();
     method public void onSeekTo(long);
     method public void onSetRating(android.media.Rating);
-    method public void onSetRepeatMode(int);
-    method public void onSetShuffleModeEnabled(boolean);
     method public void onSkipToNext();
     method public void onSkipToPrevious();
     method public void onSkipToQueueItem(long);
@@ -24448,17 +24428,12 @@
     field public static final long ACTION_REWIND = 8L; // 0x8L
     field public static final long ACTION_SEEK_TO = 256L; // 0x100L
     field public static final long ACTION_SET_RATING = 128L; // 0x80L
-    field public static final long ACTION_SET_REPEAT_MODE = 262144L; // 0x40000L
-    field public static final long ACTION_SET_SHUFFLE_MODE_ENABLED = 524288L; // 0x80000L
     field public static final long ACTION_SKIP_TO_NEXT = 32L; // 0x20L
     field public static final long ACTION_SKIP_TO_PREVIOUS = 16L; // 0x10L
     field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L
     field public static final long ACTION_STOP = 1L; // 0x1L
     field public static final android.os.Parcelable.Creator<android.media.session.PlaybackState> CREATOR;
     field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL
-    field public static final int REPEAT_MODE_ALL = 2; // 0x2
-    field public static final int REPEAT_MODE_NONE = 0; // 0x0
-    field public static final int REPEAT_MODE_ONE = 1; // 0x1
     field public static final int STATE_BUFFERING = 6; // 0x6
     field public static final int STATE_CONNECTING = 8; // 0x8
     field public static final int STATE_ERROR = 7; // 0x7
@@ -26783,8 +26758,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[]);
   }
@@ -26870,8 +26845,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);
@@ -37360,7 +37335,6 @@
     method public abstract void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>);
     method public void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>, android.os.Bundle);
     method public void onLoadItem(java.lang.String, android.service.media.MediaBrowserService.Result<android.media.browse.MediaBrowser.MediaItem>);
-    method public void onSearch(java.lang.String, android.os.Bundle, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>);
     method public void setSessionToken(android.media.session.MediaSession.Token);
     field public static final java.lang.String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
   }
@@ -40938,7 +40912,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);
@@ -55422,6 +55395,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-current.txt b/api/system-current.txt
index 37bba95..dc4c21f 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3989,7 +3989,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();
@@ -5806,7 +5806,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 +6869,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();
@@ -7326,7 +7325,7 @@
   }
 
   public abstract class JobServiceEngine {
-    ctor public JobServiceEngine(android.content.Context);
+    ctor public JobServiceEngine(android.app.Service);
     method public final android.os.IBinder getBinder();
     method public final void jobFinished(android.app.job.JobParameters, boolean);
     method public abstract boolean onStartJob(android.app.job.JobParameters);
@@ -24611,7 +24610,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 +24624,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 +24651,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 +24692,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 +24705,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 +24736,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 +24745,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 +24776,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 {
@@ -25933,7 +25939,6 @@
     method public android.content.ComponentName getServiceComponent();
     method public android.media.session.MediaSession.Token getSessionToken();
     method public boolean isConnected();
-    method public void search(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SearchCallback);
     method public void subscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback);
     method public void subscribe(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SubscriptionCallback);
     method public void unsubscribe(java.lang.String);
@@ -25969,12 +25974,6 @@
     field public static final int FLAG_PLAYABLE = 2; // 0x2
   }
 
-  public static abstract class MediaBrowser.SearchCallback {
-    ctor public MediaBrowser.SearchCallback();
-    method public void onError(java.lang.String, android.os.Bundle);
-    method public void onSearchResult(java.lang.String, android.os.Bundle, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
-  }
-
   public static abstract class MediaBrowser.SubscriptionCallback {
     ctor public MediaBrowser.SubscriptionCallback();
     method public void onChildrenLoaded(java.lang.String, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
@@ -26183,8 +26182,6 @@
 
   public final class MediaController {
     ctor public MediaController(android.content.Context, android.media.session.MediaSession.Token);
-    method public void addQueueItem(android.media.MediaDescription);
-    method public void addQueueItem(android.media.MediaDescription, int);
     method public void adjustVolume(int, int);
     method public boolean dispatchMediaButtonEvent(android.view.KeyEvent);
     method public android.os.Bundle getExtras();
@@ -26196,15 +26193,11 @@
     method public java.util.List<android.media.session.MediaSession.QueueItem> getQueue();
     method public java.lang.CharSequence getQueueTitle();
     method public int getRatingType();
-    method public int getRepeatMode();
     method public android.app.PendingIntent getSessionActivity();
     method public android.media.session.MediaSession.Token getSessionToken();
     method public android.media.session.MediaController.TransportControls getTransportControls();
-    method public boolean isShuffleModeEnabled();
     method public void registerCallback(android.media.session.MediaController.Callback);
     method public void registerCallback(android.media.session.MediaController.Callback, android.os.Handler);
-    method public void removeQueueItem(android.media.MediaDescription);
-    method public void removeQueueItemAt(int);
     method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
     method public void setVolumeTo(int, int);
     method public void unregisterCallback(android.media.session.MediaController.Callback);
@@ -26218,10 +26211,8 @@
     method public void onPlaybackStateChanged(android.media.session.PlaybackState);
     method public void onQueueChanged(java.util.List<android.media.session.MediaSession.QueueItem>);
     method public void onQueueTitleChanged(java.lang.CharSequence);
-    method public void onRepeatModeChanged(int);
     method public void onSessionDestroyed();
     method public void onSessionEvent(java.lang.String, android.os.Bundle);
-    method public void onShuffleModeChanged(boolean);
   }
 
   public static final class MediaController.PlaybackInfo {
@@ -26250,8 +26241,6 @@
     method public void sendCustomAction(android.media.session.PlaybackState.CustomAction, android.os.Bundle);
     method public void sendCustomAction(java.lang.String, android.os.Bundle);
     method public void setRating(android.media.Rating);
-    method public void setRepeatMode(int);
-    method public void setShuffleModeEnabled(boolean);
     method public void skipToNext();
     method public void skipToPrevious();
     method public void skipToQueueItem(long);
@@ -26278,18 +26267,13 @@
     method public void setQueue(java.util.List<android.media.session.MediaSession.QueueItem>);
     method public void setQueueTitle(java.lang.CharSequence);
     method public void setRatingType(int);
-    method public void setRepeatMode(int);
     method public void setSessionActivity(android.app.PendingIntent);
-    method public void setShuffleModeEnabled(boolean);
     field public static final deprecated int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
-    field public static final int FLAG_HANDLES_QUEUE_COMMANDS = 4; // 0x4
     field public static final deprecated int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
   }
 
   public static abstract class MediaSession.Callback {
     ctor public MediaSession.Callback();
-    method public void onAddQueueItem(android.media.MediaDescription);
-    method public void onAddQueueItem(android.media.MediaDescription, int);
     method public void onCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
     method public void onCustomAction(java.lang.String, android.os.Bundle);
     method public void onFastForward();
@@ -26303,13 +26287,9 @@
     method public void onPrepareFromMediaId(java.lang.String, android.os.Bundle);
     method public void onPrepareFromSearch(java.lang.String, android.os.Bundle);
     method public void onPrepareFromUri(android.net.Uri, android.os.Bundle);
-    method public void onRemoveQueueItem(android.media.MediaDescription);
-    method public void onRemoveQueueItemAt(int);
     method public void onRewind();
     method public void onSeekTo(long);
     method public void onSetRating(android.media.Rating);
-    method public void onSetRepeatMode(int);
-    method public void onSetShuffleModeEnabled(boolean);
     method public void onSkipToNext();
     method public void onSkipToPrevious();
     method public void onSkipToQueueItem(long);
@@ -26380,17 +26360,12 @@
     field public static final long ACTION_REWIND = 8L; // 0x8L
     field public static final long ACTION_SEEK_TO = 256L; // 0x100L
     field public static final long ACTION_SET_RATING = 128L; // 0x80L
-    field public static final long ACTION_SET_REPEAT_MODE = 262144L; // 0x40000L
-    field public static final long ACTION_SET_SHUFFLE_MODE_ENABLED = 524288L; // 0x80000L
     field public static final long ACTION_SKIP_TO_NEXT = 32L; // 0x20L
     field public static final long ACTION_SKIP_TO_PREVIOUS = 16L; // 0x10L
     field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L
     field public static final long ACTION_STOP = 1L; // 0x1L
     field public static final android.os.Parcelable.Creator<android.media.session.PlaybackState> CREATOR;
     field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL
-    field public static final int REPEAT_MODE_ALL = 2; // 0x2
-    field public static final int REPEAT_MODE_NONE = 0; // 0x0
-    field public static final int REPEAT_MODE_ONE = 1; // 0x1
     field public static final int STATE_BUFFERING = 6; // 0x6
     field public static final int STATE_CONNECTING = 8; // 0x8
     field public static final int STATE_ERROR = 7; // 0x7
@@ -29526,9 +29501,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[]);
   }
@@ -29614,9 +29589,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);
@@ -40472,7 +40447,6 @@
     method public abstract void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>);
     method public void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>, android.os.Bundle);
     method public void onLoadItem(java.lang.String, android.service.media.MediaBrowserService.Result<android.media.browse.MediaBrowser.MediaItem>);
-    method public void onSearch(java.lang.String, android.os.Bundle, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>);
     method public void setSessionToken(android.media.session.MediaSession.Token);
     field public static final java.lang.String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
   }
@@ -44494,7 +44468,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);
@@ -59365,6 +59338,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-current.txt b/api/test-current.txt
index 02b3db7..88adb6e 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3852,7 +3852,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();
@@ -5618,7 +5618,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 +6654,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();
@@ -6920,7 +6919,7 @@
   }
 
   public abstract class JobServiceEngine {
-    ctor public JobServiceEngine(android.content.Context);
+    ctor public JobServiceEngine(android.app.Service);
     method public final android.os.IBinder getBinder();
     method public final void jobFinished(android.app.job.JobParameters, boolean);
     method public abstract boolean onStartJob(android.app.job.JobParameters);
@@ -22882,7 +22881,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 +22895,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 +22922,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 +22963,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 +22976,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 +23007,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 +23016,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 +23047,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 {
@@ -24118,7 +24124,6 @@
     method public android.content.ComponentName getServiceComponent();
     method public android.media.session.MediaSession.Token getSessionToken();
     method public boolean isConnected();
-    method public void search(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SearchCallback);
     method public void subscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback);
     method public void subscribe(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SubscriptionCallback);
     method public void unsubscribe(java.lang.String);
@@ -24154,12 +24159,6 @@
     field public static final int FLAG_PLAYABLE = 2; // 0x2
   }
 
-  public static abstract class MediaBrowser.SearchCallback {
-    ctor public MediaBrowser.SearchCallback();
-    method public void onError(java.lang.String, android.os.Bundle);
-    method public void onSearchResult(java.lang.String, android.os.Bundle, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
-  }
-
   public static abstract class MediaBrowser.SubscriptionCallback {
     ctor public MediaBrowser.SubscriptionCallback();
     method public void onChildrenLoaded(java.lang.String, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
@@ -24368,8 +24367,6 @@
 
   public final class MediaController {
     ctor public MediaController(android.content.Context, android.media.session.MediaSession.Token);
-    method public void addQueueItem(android.media.MediaDescription);
-    method public void addQueueItem(android.media.MediaDescription, int);
     method public void adjustVolume(int, int);
     method public boolean dispatchMediaButtonEvent(android.view.KeyEvent);
     method public android.os.Bundle getExtras();
@@ -24381,15 +24378,11 @@
     method public java.util.List<android.media.session.MediaSession.QueueItem> getQueue();
     method public java.lang.CharSequence getQueueTitle();
     method public int getRatingType();
-    method public int getRepeatMode();
     method public android.app.PendingIntent getSessionActivity();
     method public android.media.session.MediaSession.Token getSessionToken();
     method public android.media.session.MediaController.TransportControls getTransportControls();
-    method public boolean isShuffleModeEnabled();
     method public void registerCallback(android.media.session.MediaController.Callback);
     method public void registerCallback(android.media.session.MediaController.Callback, android.os.Handler);
-    method public void removeQueueItem(android.media.MediaDescription);
-    method public void removeQueueItemAt(int);
     method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
     method public void setVolumeTo(int, int);
     method public void unregisterCallback(android.media.session.MediaController.Callback);
@@ -24403,10 +24396,8 @@
     method public void onPlaybackStateChanged(android.media.session.PlaybackState);
     method public void onQueueChanged(java.util.List<android.media.session.MediaSession.QueueItem>);
     method public void onQueueTitleChanged(java.lang.CharSequence);
-    method public void onRepeatModeChanged(int);
     method public void onSessionDestroyed();
     method public void onSessionEvent(java.lang.String, android.os.Bundle);
-    method public void onShuffleModeChanged(boolean);
   }
 
   public static final class MediaController.PlaybackInfo {
@@ -24435,8 +24426,6 @@
     method public void sendCustomAction(android.media.session.PlaybackState.CustomAction, android.os.Bundle);
     method public void sendCustomAction(java.lang.String, android.os.Bundle);
     method public void setRating(android.media.Rating);
-    method public void setRepeatMode(int);
-    method public void setShuffleModeEnabled(boolean);
     method public void skipToNext();
     method public void skipToPrevious();
     method public void skipToQueueItem(long);
@@ -24463,18 +24452,13 @@
     method public void setQueue(java.util.List<android.media.session.MediaSession.QueueItem>);
     method public void setQueueTitle(java.lang.CharSequence);
     method public void setRatingType(int);
-    method public void setRepeatMode(int);
     method public void setSessionActivity(android.app.PendingIntent);
-    method public void setShuffleModeEnabled(boolean);
     field public static final deprecated int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
-    field public static final int FLAG_HANDLES_QUEUE_COMMANDS = 4; // 0x4
     field public static final deprecated int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
   }
 
   public static abstract class MediaSession.Callback {
     ctor public MediaSession.Callback();
-    method public void onAddQueueItem(android.media.MediaDescription);
-    method public void onAddQueueItem(android.media.MediaDescription, int);
     method public void onCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
     method public void onCustomAction(java.lang.String, android.os.Bundle);
     method public void onFastForward();
@@ -24488,13 +24472,9 @@
     method public void onPrepareFromMediaId(java.lang.String, android.os.Bundle);
     method public void onPrepareFromSearch(java.lang.String, android.os.Bundle);
     method public void onPrepareFromUri(android.net.Uri, android.os.Bundle);
-    method public void onRemoveQueueItem(android.media.MediaDescription);
-    method public void onRemoveQueueItemAt(int);
     method public void onRewind();
     method public void onSeekTo(long);
     method public void onSetRating(android.media.Rating);
-    method public void onSetRepeatMode(int);
-    method public void onSetShuffleModeEnabled(boolean);
     method public void onSkipToNext();
     method public void onSkipToPrevious();
     method public void onSkipToQueueItem(long);
@@ -24555,17 +24535,12 @@
     field public static final long ACTION_REWIND = 8L; // 0x8L
     field public static final long ACTION_SEEK_TO = 256L; // 0x100L
     field public static final long ACTION_SET_RATING = 128L; // 0x80L
-    field public static final long ACTION_SET_REPEAT_MODE = 262144L; // 0x40000L
-    field public static final long ACTION_SET_SHUFFLE_MODE_ENABLED = 524288L; // 0x80000L
     field public static final long ACTION_SKIP_TO_NEXT = 32L; // 0x20L
     field public static final long ACTION_SKIP_TO_PREVIOUS = 16L; // 0x10L
     field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L
     field public static final long ACTION_STOP = 1L; // 0x1L
     field public static final android.os.Parcelable.Creator<android.media.session.PlaybackState> CREATOR;
     field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL
-    field public static final int REPEAT_MODE_ALL = 2; // 0x2
-    field public static final int REPEAT_MODE_NONE = 0; // 0x0
-    field public static final int REPEAT_MODE_ONE = 1; // 0x1
     field public static final int STATE_BUFFERING = 6; // 0x6
     field public static final int STATE_CONNECTING = 8; // 0x8
     field public static final int STATE_ERROR = 7; // 0x7
@@ -26890,8 +26865,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[]);
   }
@@ -26977,8 +26952,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);
@@ -37513,7 +37488,6 @@
     method public abstract void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>);
     method public void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>, android.os.Bundle);
     method public void onLoadItem(java.lang.String, android.service.media.MediaBrowserService.Result<android.media.browse.MediaBrowser.MediaItem>);
-    method public void onSearch(java.lang.String, android.os.Bundle, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>);
     method public void setSessionToken(android.media.session.MediaSession.Token);
     field public static final java.lang.String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
   }
@@ -41133,7 +41107,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);
@@ -55815,6 +55788,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/cmds/media/src/com/android/commands/media/Media.java b/cmds/media/src/com/android/commands/media/Media.java
index 4be4654..9df229c 100644
--- a/cmds/media/src/com/android/commands/media/Media.java
+++ b/cmds/media/src/com/android/commands/media/Media.java
@@ -228,16 +228,6 @@
             System.out.println("onVolumeInfoChanged " + info);
         }
 
-        @Override
-        public void onRepeatModeChanged(int repeatMode) throws RemoteException {
-            System.out.println("onRepeatModeChanged " + repeatMode);
-        }
-
-        @Override
-        public void onShuffleModeChanged(boolean enabled) throws RemoteException {
-            System.out.println("onShuffleModeChanged " + enabled);
-        }
-
         void printUsageMessage() {
             try {
                 System.out.println("V2Monitoring session " + mController.getTag()
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/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/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/Notification.java b/core/java/android/app/Notification.java
index 8ed52a1..cab2114 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -4202,9 +4202,22 @@
          * @hide
          */
         public RemoteViews makePublicContentView() {
+            return makePublicView(false /* ambient */);
+        }
+
+        /**
+         * Construct a RemoteViews for the display in public contexts like on the lockscreen.
+         *
+         * @hide
+         */
+        public RemoteViews makePublicAmbientNotification() {
+            return makePublicView(true /* ambient */);
+        }
+
+        private RemoteViews makePublicView(boolean ambient) {
             if (mN.publicVersion != null) {
                 final Builder builder = recoverBuilder(mContext, mN.publicVersion);
-                return builder.createContentView();
+                return ambient ? builder.makeAmbientNotification() : builder.createContentView();
             }
             Bundle savedBundle = mN.extras;
             Style style = mStyle;
@@ -4221,14 +4234,15 @@
             publicExtras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN,
                     savedBundle.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN));
             publicExtras.putCharSequence(EXTRA_TITLE,
-                    mContext.getString(R.string.notification_hidden_text));
+                    mContext.getString(com.android.internal.R.string.notification_hidden_text));
             mN.extras = publicExtras;
-            final RemoteViews publicView = applyStandardTemplate(getBaseLayoutResource());
+            final RemoteViews view = ambient ? makeAmbientNotification()
+                    : applyStandardTemplate(getBaseLayoutResource());
             mN.extras = savedBundle;
             mN.mLargeIcon = largeIcon;
             mN.largeIcon = largeIconLegacy;
             mStyle = style;
-            return publicView;
+            return view;
         }
 
         /**
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/app/job/JobParameters.java b/core/java/android/app/job/JobParameters.java
index 016a0fa..673d1b8 100644
--- a/core/java/android/app/job/JobParameters.java
+++ b/core/java/android/app/job/JobParameters.java
@@ -164,6 +164,20 @@
      * you should not call {@link JobService#jobFinished(JobParameters, boolean)} yourself
      * (otherwise you risk losing an upcoming JobWorkItem that is being enqueued at the same time).
      *
+     * <p>Once you are done with the {@link JobWorkItem} returned by this method, you must call
+     * {@link #completeWork(JobWorkItem)} with it to inform the system that you are done
+     * executing the work.  The job will not be finished until all dequeued work has been
+     * completed.  You do not, however, have to complete each returned work item before deqeueing
+     * the next one -- you can use {@link #dequeueWork()} multiple times before completing
+     * previous work if you want to process work in parallel, and you can complete the work
+     * in whatever order you want.</p>
+     *
+     * <p>If the job runs to the end of its available time period before all work has been
+     * completed, it will stop as normal.  You should return true from
+     * {@link JobService#onStopJob(JobParameters)} in order to have the job rescheduled, and by
+     * doing so any pending as well as remaining uncompleted work will be re-queued
+     * for the next time the job runs.</p>
+     *
      * @return Returns a new {@link JobWorkItem} if there is one pending, otherwise null.
      * If null is returned, the system will also stop the job if all work has also been completed.
      * (This means that for correct operation, you must always call dequeueWork() after you have
diff --git a/core/java/android/app/job/JobServiceEngine.java b/core/java/android/app/job/JobServiceEngine.java
index 879212e..a628619 100644
--- a/core/java/android/app/job/JobServiceEngine.java
+++ b/core/java/android/app/job/JobServiceEngine.java
@@ -54,7 +54,7 @@
     /**
      * Context we are running in.
      */
-    private final Context mContext;
+    private final Service mService;
 
     private final IJobService mBinder;
 
@@ -182,12 +182,12 @@
     /**
      * Create a new engine, ready for use.
      *
-     * @param context The {@link Service} that is creating this engine.
+     * @param service The {@link Service} that is creating this engine and in which it will run.
      */
-    public JobServiceEngine(Context context) {
-        mContext = context;
+    public JobServiceEngine(Service service) {
+        mService = service;
         mBinder = new JobInterface(this);
-        mHandler = new JobHandler(mContext.getMainLooper());
+        mHandler = new JobHandler(mService.getMainLooper());
     }
 
     /**
diff --git a/core/java/android/app/job/JobWorkItem.java b/core/java/android/app/job/JobWorkItem.java
index 4bb057e..05687ee 100644
--- a/core/java/android/app/job/JobWorkItem.java
+++ b/core/java/android/app/job/JobWorkItem.java
@@ -27,6 +27,7 @@
 final public class JobWorkItem implements Parcelable {
     final Intent mIntent;
     int mWorkId;
+    Object mGrants;
 
     /**
      * Create a new piece of work.
@@ -57,6 +58,20 @@
         return mWorkId;
     }
 
+    /**
+     * @hide
+     */
+    public void setGrants(Object grants) {
+        mGrants = grants;
+    }
+
+    /**
+     * @hide
+     */
+    public Object getGrants() {
+        return mGrants;
+    }
+
     public String toString() {
         return "JobWorkItem{id=" + mWorkId + " intent=" + mIntent + "}";
     }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 18120c7..0adab1a 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2647,18 +2647,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 +2684,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);
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/IntentFilter.java b/core/java/android/content/IntentFilter.java
index 23e54ba..d64f018 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -1787,7 +1787,7 @@
                     sb.append(", mHasPartialTypes="); sb.append(mHasPartialTypes);
             du.println(sb.toString());
         }
-        {
+        if (getAutoVerify()) {
             sb.setLength(0);
             sb.append(prefix); sb.append("AutoVerify="); sb.append(getAutoVerify());
             du.println(sb.toString());
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 09906be..666ba60 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -6256,18 +6256,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();
 
diff --git a/core/java/android/metrics/LogMaker.java b/core/java/android/metrics/LogMaker.java
index 0448221..a76a8a0 100644
--- a/core/java/android/metrics/LogMaker.java
+++ b/core/java/android/metrics/LogMaker.java
@@ -94,6 +94,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.
      *
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/view/IPinnedStackController.aidl b/core/java/android/view/IPinnedStackController.aidl
index 2fe98c0..dbeb747 100644
--- a/core/java/android/view/IPinnedStackController.aidl
+++ b/core/java/android/view/IPinnedStackController.aidl
@@ -28,4 +28,9 @@
      * Notifies the controller that the PiP is currently minimized.
      */
     oneway void setIsMinimized(boolean isMinimized);
+
+    /**
+     * @return what WM considers to be the current device rotation.
+     */
+    int getDisplayRotation();
 }
diff --git a/core/java/android/view/IPinnedStackListener.aidl b/core/java/android/view/IPinnedStackListener.aidl
index 782f349..9382741 100644
--- a/core/java/android/view/IPinnedStackListener.aidl
+++ b/core/java/android/view/IPinnedStackListener.aidl
@@ -41,9 +41,13 @@
      * current state with the aspect ratio applied.  The {@param animatingBounds} are provided
      * to indicate the current target bounds of the pinned stack (the final bounds if animating,
      * the current bounds if not), which may be helpful in calculating dependent animation bounds.
+     *
+     * The {@param displayRotation} is provided so that the client can verify when making certain
+     * calls that it will not provide stale information based on an old display rotation (ie. if
+     * the WM has changed in the mean time but the client has not received onMovementBoundsChanged).
      */
     void onMovementBoundsChanged(in Rect insetBounds, in Rect normalBounds, in Rect animatingBounds,
-            boolean fromImeAdjustement);
+            boolean fromImeAdjustement, int displayRotation);
 
     /**
      * Called when window manager decides to adjust the pinned stack bounds because of the IME, or
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a7ececf..080ffeb 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
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/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/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/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/res/res/values/config.xml b/core/res/res/values/config.xml
index 45dccb6..6a31e16 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>
 
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..0acff9b
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -0,0 +1,212 @@
+/*
+ * 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 android.view.textclassifier.TextClassificationManager;
+import android.view.textclassifier.TextClassificationResult;
+import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextLanguage;
+import android.view.textclassifier.TextSelection;
+
+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_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 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/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/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/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index 5bf205e..789d5e0 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -365,7 +365,7 @@
      * @param parentId The id of the parent media item whose list of children
      *            will be subscribed.
      * @param options The bundle of service-specific arguments to send to the media
-     *            browse service. The contents of this bundle may affect the
+     *            browser service. The contents of this bundle may affect the
      *            information returned when browsing.
      * @param callback The callback to receive the list of children.
      */
@@ -453,7 +453,7 @@
         try {
             mServiceBinder.getMediaItem(mediaId, receiver, mServiceCallbacks);
         } catch (RemoteException e) {
-            Log.i(TAG, "Remote error getting media item.", e);
+            Log.i(TAG, "Remote error getting media item.");
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
@@ -463,62 +463,6 @@
         }
     }
 
-    /**
-     * Searches {@link MediaItem media items} from the connected service. Not
-     * all services may support this, and {@link SearchCallback#onError} will be
-     * called if not implemented.
-     *
-     * @param query The search query that contains keywords separated by space. Should not be
-     *            an empty string.
-     * @param extras The bundle of service-specific arguments to send to the media browser
-     *            service. The contents of this bundle may affect the search result.
-     * @param callback The callback to receive the search result.
-     * @throws IllegalStateException if the browser is not connected to the media browser service.
-     */
-    public void search(@NonNull final String query, final Bundle extras, SearchCallback callback) {
-        if (TextUtils.isEmpty(query)) {
-            throw new IllegalArgumentException("query cannot be empty.");
-        }
-        if (callback == null) {
-            throw new IllegalArgumentException("callback cannot be null.");
-        }
-        if (mState != CONNECT_STATE_CONNECTED) {
-            throw new IllegalStateException("search() called while not connected (state="
-                    + getStateLabel(mState) + ")");
-        }
-        ResultReceiver receiver = new ResultReceiver(mHandler) {
-            @Override
-            protected void onReceiveResult(int resultCode, Bundle resultData) {
-                if (resultCode != 0 || resultData == null
-                        || !resultData.containsKey(MediaBrowserService.KEY_SEARCH_RESULTS)) {
-                    callback.onError(query, extras);
-                    return;
-                }
-                Parcelable[] items = resultData.getParcelableArray(
-                        MediaBrowserService.KEY_SEARCH_RESULTS);
-                List<MediaItem> results = null;
-                if (items != null) {
-                    results = new ArrayList<>();
-                    for (Parcelable item : items) {
-                        results.add((MediaItem) item);
-                    }
-                }
-                callback.onSearchResult(query, extras, results);
-            }
-        };
-        try {
-            mServiceBinder.search(query, extras, receiver, mServiceCallbacks);
-        } catch (RemoteException e) {
-            Log.i(TAG, "Remote error getting media item.", e);
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    callback.onError(query, extras);
-                }
-            });
-        }
-    }
-
     private void subscribeInternal(String parentId, Bundle options, SubscriptionCallback callback) {
         // Check arguments.
         if (TextUtils.isEmpty(parentId)) {
@@ -945,7 +889,7 @@
          * @param parentId The media id of the parent media item.
          * @param children The children which were loaded.
          * @param options The bundle of service-specific arguments sent to the media
-         *            browse service. The contents of this bundle may affect the
+         *            browser service. The contents of this bundle may affect the
          *            information returned when browsing.
          */
         public void onChildrenLoaded(@NonNull String parentId, @NonNull List<MediaItem> children,
@@ -1004,32 +948,6 @@
     }
 
     /**
-     * Callback for receiving the result of {@link #search}.
-     */
-    public static abstract class SearchCallback {
-        /**
-         * Called when the {@link #search} finished successfully.
-         *
-         * @param query The search query sent for the search request to the connected service.
-         * @param extras The bundle of service-specific arguments sent to the connected service.
-         * @param items The list of media items which contains the search result.
-         */
-        public void onSearchResult(@NonNull String query, Bundle extras,
-                @NonNull List<MediaItem> items) {
-        }
-
-        /**
-         * Called when an error happens while {@link #search} or the connected service doesn't
-         * support {@link #search}.
-         *
-         * @param query The search query sent for the search request to the connected service.
-         * @param extras The bundle of service-specific arguments sent to the connected service.
-         */
-        public void onError(@NonNull String query, Bundle extras) {
-        }
-    }
-
-    /**
      * ServiceConnection to the other app.
      */
     private class MediaServiceConnection implements ServiceConnection {
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index 4f3314c..3affee5c0 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -45,8 +45,6 @@
     void setQueueTitle(CharSequence title);
     void setExtras(in Bundle extras);
     void setRatingType(int type);
-    void setRepeatMode(int repeatMode);
-    void setShuffleModeEnabled(boolean enabled);
 
     // These commands relate to volume handling
     void setPlaybackToLocal(in AudioAttributes attributes);
diff --git a/media/java/android/media/session/ISessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl
index a146c62..893bd3c 100644
--- a/media/java/android/media/session/ISessionCallback.aidl
+++ b/media/java/android/media/session/ISessionCallback.aidl
@@ -16,7 +16,6 @@
 package android.media.session;
 
 import android.content.Intent;
-import android.media.MediaDescription;
 import android.media.Rating;
 import android.net.Uri;
 import android.os.Bundle;
@@ -47,13 +46,7 @@
     void onRewind();
     void onSeekTo(long pos);
     void onRate(in Rating rating);
-    void onRepeatMode(int repeatMode);
-    void onShuffleMode(boolean enabled);
     void onCustomAction(String action, in Bundle args);
-    void onAddQueueItem(in MediaDescription description);
-    void onAddQueueItemAt(in MediaDescription description, int index);
-    void onRemoveQueueItem(in MediaDescription description);
-    void onRemoveQueueItemAt(int index);
 
     // These callbacks are for volume handling
     void onAdjustVolume(int direction);
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index 7b5233a..249bcdc 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -18,7 +18,6 @@
 import android.app.PendingIntent;
 import android.content.Intent;
 import android.content.pm.ParceledListSlice;
-import android.media.MediaDescription;
 import android.media.MediaMetadata;
 import android.media.Rating;
 import android.media.session.ISessionControllerCallback;
@@ -49,19 +48,6 @@
     ParcelableVolumeInfo getVolumeAttributes();
     void adjustVolume(int direction, int flags, String packageName);
     void setVolumeTo(int value, int flags, String packageName);
-    MediaMetadata getMetadata();
-    PlaybackState getPlaybackState();
-    ParceledListSlice getQueue();
-    void addQueueItem(in MediaDescription description);
-    void addQueueItemAt(in MediaDescription description, int index);
-    void removeQueueItem(in MediaDescription description);
-    void removeQueueItemAt(int index);
-
-    CharSequence getQueueTitle();
-    Bundle getExtras();
-    int getRatingType();
-    int getRepeatMode();
-    boolean isShuffleModeEnabled();
 
     // These commands are for the TransportControls
     void prepare();
@@ -81,7 +67,11 @@
     void rewind();
     void seekTo(long pos);
     void rate(in Rating rating);
-    void repeatMode(int repeatMode);
-    void shuffleMode(boolean enabled);
     void sendCustomAction(String action, in Bundle args);
+    MediaMetadata getMetadata();
+    PlaybackState getPlaybackState();
+    ParceledListSlice getQueue();
+    CharSequence getQueueTitle();
+    Bundle getExtras();
+    int getRatingType();
 }
diff --git a/media/java/android/media/session/ISessionControllerCallback.aidl b/media/java/android/media/session/ISessionControllerCallback.aidl
index 9517c08..cf31767 100644
--- a/media/java/android/media/session/ISessionControllerCallback.aidl
+++ b/media/java/android/media/session/ISessionControllerCallback.aidl
@@ -36,6 +36,4 @@
     void onQueueTitleChanged(CharSequence title);
     void onExtrasChanged(in Bundle extras);
     void onVolumeInfoChanged(in ParcelableVolumeInfo info);
-    void onRepeatModeChanged(int repeatMode);
-    void onShuffleModeChanged(boolean shuffleMode);
 }
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index bab2af2..622900f 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -23,7 +23,6 @@
 import android.content.pm.ParceledListSlice;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
-import android.media.MediaDescription;
 import android.media.MediaMetadata;
 import android.media.Rating;
 import android.media.VolumeProvider;
@@ -39,7 +38,6 @@
 import android.view.KeyEvent;
 
 import java.lang.ref.WeakReference;
-import java.lang.UnsupportedOperationException;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -65,9 +63,7 @@
     private static final int MSG_UPDATE_QUEUE = 5;
     private static final int MSG_UPDATE_QUEUE_TITLE = 6;
     private static final int MSG_UPDATE_EXTRAS = 7;
-    private static final int MSG_UPDATE_REPEAT_MODE = 8;
-    private static final int MSG_UPDATE_SHUFFLE_MODE = 9;
-    private static final int MSG_DESTROYED = 10;
+    private static final int MSG_DESTROYED = 8;
 
     private final ISessionController mSessionBinder;
 
@@ -113,7 +109,8 @@
     }
 
     /**
-     * Get a {@link TransportControls} instance to send transport actions to this session.
+     * Get a {@link TransportControls} instance to send transport actions to
+     * the associated session.
      *
      * @return A transport controls instance.
      */
@@ -152,7 +149,7 @@
         try {
             return mSessionBinder.getPlaybackState();
         } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling getPlaybackState", e);
+            Log.wtf(TAG, "Error calling getPlaybackState.", e);
             return null;
         }
     }
@@ -166,7 +163,7 @@
         try {
             return mSessionBinder.getMetadata();
         } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling getMetadata", e);
+            Log.wtf(TAG, "Error calling getMetadata.", e);
             return null;
         }
     }
@@ -184,103 +181,12 @@
                 return queue.getList();
             }
         } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling getQueue", e);
+            Log.wtf(TAG, "Error calling getQueue.", e);
         }
         return null;
     }
 
     /**
-     * Add a queue item from the given {@code description} at the end of the play queue
-     * of this session. Not all sessions may support this.
-     *
-     * @param description The {@link MediaDescription} for creating the
-     *                    {@link MediaSession.QueueItem} to be inserted.
-     * @throws UnsupportedOperationException If this session doesn't support this.
-     * @see MediaSession#FLAG_HANDLES_QUEUE_COMMANDS
-     */
-    public void addQueueItem(MediaDescription description) {
-        try {
-            long flags = mSessionBinder.getFlags();
-            if ((flags & MediaSession.FLAG_HANDLES_QUEUE_COMMANDS) == 0) {
-                throw new UnsupportedOperationException(
-                        "This session doesn't support queue management operations");
-            }
-            mSessionBinder.addQueueItem(description);
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling addQueueItem", e);
-        }
-    }
-
-    /**
-     * Add a queue item from the given {@code description} at the specified position
-     * in the play queue of this session. Shifts the queue item currently at that position
-     * (if any) and any subsequent queue items to the right (adds one to their indices).
-     * Not all sessions may support this.
-     *
-     * @param description The {@link MediaDescription} for creating the
-     *                    {@link MediaSession.QueueItem} to be inserted.
-     * @param index The index at which the created {@link MediaSession.QueueItem} is to be inserted.
-     * @throws UnsupportedOperationException If this session doesn't support this.
-     * @see MediaSession#FLAG_HANDLES_QUEUE_COMMANDS
-     */
-    public void addQueueItem(MediaDescription description, int index) {
-        try {
-            long flags = mSessionBinder.getFlags();
-            if ((flags & MediaSession.FLAG_HANDLES_QUEUE_COMMANDS) == 0) {
-                throw new UnsupportedOperationException(
-                        "This session doesn't support queue management operations");
-            }
-            mSessionBinder.addQueueItemAt(description, index);
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling addQueueItemAt", e);
-        }
-    }
-
-    /**
-     * Remove the first occurrence of the specified {@link MediaSession.QueueItem}
-     * with the given {@link MediaDescription description} in the play queue of the associated
-     * session. Not all sessions may support this.
-     *
-     * @param description The {@link MediaDescription} for denoting the
-     *                    {@link MediaSession.QueueItem} to be removed.
-     * @throws UnsupportedOperationException If this session doesn't support this.
-     * @see MediaSession#FLAG_HANDLES_QUEUE_COMMANDS
-     */
-    public void removeQueueItem(MediaDescription description) {
-        try {
-            long flags = mSessionBinder.getFlags();
-            if ((flags & MediaSession.FLAG_HANDLES_QUEUE_COMMANDS) == 0) {
-                throw new UnsupportedOperationException(
-                        "This session doesn't support queue management operations");
-            }
-            mSessionBinder.removeQueueItem(description);
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling removeQueueItem", e);
-        }
-    }
-
-    /**
-     * Remove an queue item at the specified position in the play queue
-     * of this session. Not all sessions may support this.
-     *
-     * @param index The index of the element to be removed.
-     * @throws UnsupportedOperationException If this session doesn't support this.
-     * @see MediaSession#FLAG_HANDLES_QUEUE_COMMANDS
-     */
-    public void removeQueueItemAt(int index) {
-        try {
-            long flags = mSessionBinder.getFlags();
-            if ((flags & MediaSession.FLAG_HANDLES_QUEUE_COMMANDS) == 0) {
-                throw new UnsupportedOperationException(
-                        "This session doesn't support queue management operations");
-            }
-            mSessionBinder.removeQueueItemAt(index);
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling removeQueueItemAt", e);
-        }
-    }
-
-    /**
      * Get the queue title for this session.
      */
     public @Nullable CharSequence getQueueTitle() {
@@ -322,41 +228,12 @@
         try {
             return mSessionBinder.getRatingType();
         } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling getRatingType", e);
+            Log.wtf(TAG, "Error calling getRatingType.", e);
             return Rating.RATING_NONE;
         }
     }
 
     /**
-     * Get the repeat mode for this session.
-     *
-     * @return The latest repeat mode set to the session, or
-     *         {@link PlaybackState#REPEAT_MODE_NONE} if not set.
-     */
-    public int getRepeatMode() {
-        try {
-            return mSessionBinder.getRepeatMode();
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling getRepeatMode", e);
-            return PlaybackState.REPEAT_MODE_NONE;
-        }
-    }
-
-    /**
-     * Return whether the shuffle mode is enabled for this session.
-     *
-     * @return {@code true} if the shuffle mode is enabled, {@code false} if disabled or not set.
-     */
-    public boolean isShuffleModeEnabled() {
-        try {
-            return mSessionBinder.isShuffleModeEnabled();
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling isShuffleModeEnabled", e);
-            return false;
-        }
-    }
-
-    /**
      * Get the flags for this session. Flags are defined in {@link MediaSession}.
      *
      * @return The current set of flags for the session.
@@ -365,7 +242,7 @@
         try {
             return mSessionBinder.getFlags();
         } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling getFlags", e);
+            Log.wtf(TAG, "Error calling getFlags.", e);
         }
         return 0;
     }
@@ -382,7 +259,7 @@
                     result.maxVolume, result.currentVolume);
 
         } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling getAudioInfo", e);
+            Log.wtf(TAG, "Error calling getAudioInfo.", e);
         }
         return null;
     }
@@ -397,7 +274,7 @@
         try {
             return mSessionBinder.getLaunchPendingIntent();
         } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling getPendingIntent", e);
+            Log.wtf(TAG, "Error calling getPendingIntent.", e);
         }
         return null;
     }
@@ -426,7 +303,7 @@
         try {
             mSessionBinder.setVolumeTo(value, flags, mContext.getPackageName());
         } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling setVolumeTo", e);
+            Log.wtf(TAG, "Error calling setVolumeTo.", e);
         }
     }
 
@@ -447,7 +324,7 @@
         try {
             mSessionBinder.adjustVolume(direction, flags, mContext.getPackageName());
         } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling adjustVolumeBy", e);
+            Log.wtf(TAG, "Error calling adjustVolumeBy.", e);
         }
     }
 
@@ -513,7 +390,7 @@
         try {
             mSessionBinder.sendCommand(command, args, cb);
         } catch (RemoteException e) {
-            Log.d(TAG, "Dead object in sendCommand", e);
+            Log.d(TAG, "Dead object in sendCommand.", e);
         }
     }
 
@@ -527,7 +404,7 @@
             try {
                 mPackageName = mSessionBinder.getPackageName();
             } catch (RemoteException e) {
-                Log.d(TAG, "Dead object in getPackageName", e);
+                Log.d(TAG, "Dead object in getPackageName.", e);
             }
         }
         return mPackageName;
@@ -544,7 +421,7 @@
             try {
                 mTag = mSessionBinder.getTag();
             } catch (RemoteException e) {
-                Log.d(TAG, "Dead object in getTag", e);
+                Log.d(TAG, "Dead object in getTag.", e);
             }
         }
         return mTag;
@@ -702,25 +579,6 @@
          */
         public void onAudioInfoChanged(PlaybackInfo info) {
         }
-
-        /**
-         * Override to handle changes to the repeat mode.
-         *
-         * @param repeatMode The repeat mode. It should be one of followings:
-         *                   {@link PlaybackState#REPEAT_MODE_NONE},
-         *                   {@link PlaybackState#REPEAT_MODE_ONE},
-         *                   {@link PlaybackState#REPEAT_MODE_ALL}
-         */
-        public void onRepeatModeChanged(@PlaybackState.RepeatMode int repeatMode) {
-        }
-
-        /**
-         * Override to handle changes to the shuffle mode.
-         *
-         * @param enabled {@code true} if the shuffle mode is enabled, {@code false} otherwise.
-         */
-        public void onShuffleModeChanged(boolean enabled) {
-        }
     }
 
     /**
@@ -744,7 +602,7 @@
             try {
                 mSessionBinder.prepare();
             } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling prepare", e);
+                Log.wtf(TAG, "Error calling prepare.", e);
             }
         }
 
@@ -763,12 +621,12 @@
         public void prepareFromMediaId(String mediaId, Bundle extras) {
             if (TextUtils.isEmpty(mediaId)) {
                 throw new IllegalArgumentException(
-                        "You must specify a non-empty String for prepareFromMediaId");
+                        "You must specify a non-empty String for prepareFromMediaId.");
             }
             try {
                 mSessionBinder.prepareFromMediaId(mediaId, extras);
             } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling prepare(" + mediaId + ")", e);
+                Log.wtf(TAG, "Error calling prepare(" + mediaId + ").", e);
             }
         }
 
@@ -794,7 +652,7 @@
             try {
                 mSessionBinder.prepareFromSearch(query, extras);
             } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling prepare(" + query + ")", e);
+                Log.wtf(TAG, "Error calling prepare(" + query + ").", e);
             }
         }
 
@@ -813,12 +671,12 @@
         public void prepareFromUri(Uri uri, Bundle extras) {
             if (uri == null || Uri.EMPTY.equals(uri)) {
                 throw new IllegalArgumentException(
-                        "You must specify a non-empty Uri for prepareFromUri");
+                        "You must specify a non-empty Uri for prepareFromUri.");
             }
             try {
                 mSessionBinder.prepareFromUri(uri, extras);
             } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling prepare(" + uri + ")", e);
+                Log.wtf(TAG, "Error calling prepare(" + uri + ").", e);
             }
         }
 
@@ -829,7 +687,7 @@
             try {
                 mSessionBinder.play();
             } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling play", e);
+                Log.wtf(TAG, "Error calling play.", e);
             }
         }
 
@@ -843,12 +701,12 @@
         public void playFromMediaId(String mediaId, Bundle extras) {
             if (TextUtils.isEmpty(mediaId)) {
                 throw new IllegalArgumentException(
-                        "You must specify a non-empty String for playFromMediaId");
+                        "You must specify a non-empty String for playFromMediaId.");
             }
             try {
                 mSessionBinder.playFromMediaId(mediaId, extras);
             } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling play(" + mediaId + ")", e);
+                Log.wtf(TAG, "Error calling play(" + mediaId + ").", e);
             }
         }
 
@@ -870,7 +728,7 @@
             try {
                 mSessionBinder.playFromSearch(query, extras);
             } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling play(" + query + ")", e);
+                Log.wtf(TAG, "Error calling play(" + query + ").", e);
             }
         }
 
@@ -884,12 +742,12 @@
         public void playFromUri(Uri uri, Bundle extras) {
             if (uri == null || Uri.EMPTY.equals(uri)) {
                 throw new IllegalArgumentException(
-                        "You must specify a non-empty Uri for playFromUri");
+                        "You must specify a non-empty Uri for playFromUri.");
             }
             try {
                 mSessionBinder.playFromUri(uri, extras);
             } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling play(" + uri + ")", e);
+                Log.wtf(TAG, "Error calling play(" + uri + ").", e);
             }
         }
 
@@ -901,7 +759,7 @@
             try {
                 mSessionBinder.skipToQueueItem(id);
             } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling skipToItem(" + id + ")", e);
+                Log.wtf(TAG, "Error calling skipToItem(" + id + ").", e);
             }
         }
 
@@ -913,7 +771,7 @@
             try {
                 mSessionBinder.pause();
             } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling pause", e);
+                Log.wtf(TAG, "Error calling pause.", e);
             }
         }
 
@@ -925,7 +783,7 @@
             try {
                 mSessionBinder.stop();
             } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling stop", e);
+                Log.wtf(TAG, "Error calling stop.", e);
             }
         }
 
@@ -938,7 +796,7 @@
             try {
                 mSessionBinder.seekTo(pos);
             } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling seekTo", e);
+                Log.wtf(TAG, "Error calling seekTo.", e);
             }
         }
 
@@ -950,7 +808,7 @@
             try {
                 mSessionBinder.fastForward();
             } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling fastForward", e);
+                Log.wtf(TAG, "Error calling fastForward.", e);
             }
         }
 
@@ -961,7 +819,7 @@
             try {
                 mSessionBinder.next();
             } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling next", e);
+                Log.wtf(TAG, "Error calling next.", e);
             }
         }
 
@@ -973,7 +831,7 @@
             try {
                 mSessionBinder.rewind();
             } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling rewind", e);
+                Log.wtf(TAG, "Error calling rewind.", e);
             }
         }
 
@@ -984,7 +842,7 @@
             try {
                 mSessionBinder.previous();
             } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling previous", e);
+                Log.wtf(TAG, "Error calling previous.", e);
             }
         }
 
@@ -999,36 +857,7 @@
             try {
                 mSessionBinder.rate(rating);
             } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling rate", e);
-            }
-        }
-
-        /**
-         * Set the repeat mode for this session.
-         *
-         * @param repeatMode The repeat mode. Must be one of the followings:
-         *                   {@link PlaybackState#REPEAT_MODE_NONE},
-         *                   {@link PlaybackState#REPEAT_MODE_ONE},
-         *                   {@link PlaybackState#REPEAT_MODE_ALL}
-         */
-        public void setRepeatMode(@PlaybackState.RepeatMode int repeatMode) {
-            try {
-                mSessionBinder.repeatMode(repeatMode);
-            } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling setRepeatMode", e);
-            }
-        }
-
-        /**
-         * Set the shuffle mode for this session.
-         *
-         * @param enabled {@code true} to enable the shuffle mode, {@code false} to disable.
-         */
-        public void setShuffleModeEnabled(boolean enabled) {
-            try {
-                mSessionBinder.shuffleMode(enabled);
-            } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling shuffleMode", e);
+                Log.wtf(TAG, "Error calling rate.", e);
             }
         }
 
@@ -1042,7 +871,7 @@
         public void sendCustomAction(@NonNull PlaybackState.CustomAction customAction,
                     @Nullable Bundle args) {
             if (customAction == null) {
-                throw new IllegalArgumentException("CustomAction cannot be null");
+                throw new IllegalArgumentException("CustomAction cannot be null.");
             }
             sendCustomAction(customAction.getAction(), args);
         }
@@ -1058,12 +887,12 @@
          */
         public void sendCustomAction(@NonNull String action, @Nullable Bundle args) {
             if (TextUtils.isEmpty(action)) {
-                throw new IllegalArgumentException("CustomAction cannot be null");
+                throw new IllegalArgumentException("CustomAction cannot be null.");
             }
             try {
                 mSessionBinder.sendCustomAction(action, args);
             } catch (RemoteException e) {
-                Log.d(TAG, "Dead object in sendCustomAction", e);
+                Log.d(TAG, "Dead object in sendCustomAction.", e);
             }
         }
     }
@@ -1233,21 +1062,6 @@
             }
         }
 
-        @Override
-        public void onRepeatModeChanged(int repeatMode) {
-            MediaController controller = mController.get();
-            if (controller != null) {
-                controller.postMessage(MSG_UPDATE_REPEAT_MODE, repeatMode, null);
-            }
-        }
-
-        @Override
-        public void onShuffleModeChanged(boolean enabled) {
-            MediaController controller = mController.get();
-            if (controller != null) {
-                controller.postMessage(MSG_UPDATE_SHUFFLE_MODE, enabled, null);
-            }
-        }
     }
 
     private final static class MessageHandler extends Handler {
@@ -1286,12 +1100,6 @@
                 case MSG_UPDATE_VOLUME:
                     mCallback.onAudioInfoChanged((PlaybackInfo) msg.obj);
                     break;
-                case MSG_UPDATE_REPEAT_MODE:
-                    mCallback.onRepeatModeChanged((int) msg.obj);
-                    break;
-                case MSG_UPDATE_SHUFFLE_MODE:
-                    mCallback.onShuffleModeChanged((boolean) msg.obj);
-                    break;
                 case MSG_DESTROYED:
                     mCallback.onSessionDestroyed();
                     break;
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index f10f442..dfd2bb35 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -93,12 +93,6 @@
     public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 1 << 1;
 
     /**
-     * Set this flag on the session to indicate that it handles queue
-     * management commands through its {@link Callback}.
-     */
-    public static final int FLAG_HANDLES_QUEUE_COMMANDS = 1 << 2;
-
-    /**
      * System only flag for a session that needs to have priority over all other
      * sessions. This flag ensures this session will receive media button events
      * regardless of the current ordering in the system.
@@ -112,7 +106,6 @@
     @IntDef(flag = true, value = {
             FLAG_HANDLES_MEDIA_BUTTONS,
             FLAG_HANDLES_TRANSPORT_CONTROLS,
-            FLAG_HANDLES_QUEUE_COMMANDS,
             FLAG_EXCLUSIVE_GLOBAL_PRIORITY })
     public @interface SessionFlags { }
 
@@ -493,41 +486,6 @@
     }
 
     /**
-     * Set the repeat mode for this session.
-     * <p>
-     * Note that if this method is not called before, {@link MediaController#getRepeatMode}
-     * will return {@link PlaybackState#REPEAT_MODE_NONE}.
-     *
-     * @param repeatMode The repeat mode. Must be one of the followings:
-     *                   {@link PlaybackState#REPEAT_MODE_NONE},
-     *                   {@link PlaybackState#REPEAT_MODE_ONE},
-     *                   {@link PlaybackState#REPEAT_MODE_ALL}
-     */
-    public void setRepeatMode(@PlaybackState.RepeatMode int repeatMode) {
-        try {
-            mBinder.setRepeatMode(repeatMode);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error in setRepeatMode.", e);
-        }
-    }
-
-    /**
-     * Set the shuffle mode for this session.
-     * <p>
-     * Note that if this method is not called before, {@link MediaController#isShuffleModeEnabled}
-     * will return {@code false}.
-     *
-     * @param enabled {@code true} to enable the shuffle mode, {@code false} to disable.
-     */
-    public void setShuffleModeEnabled(boolean enabled) {
-        try {
-            mBinder.setShuffleModeEnabled(enabled);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error in setShuffleModeEnabled.", e);
-        }
-    }
-
-    /**
      * Set some extras that can be associated with the {@link MediaSession}. No assumptions should
      * be made as to how a {@link MediaController} will handle these extras.
      * Keys should be fully qualified (e.g. com.example.MY_EXTRA) to avoid conflicts.
@@ -646,34 +604,10 @@
         postToCallback(CallbackMessageHandler.MSG_RATE, rating);
     }
 
-    private void dispatchRepeatMode(int repeatMode) {
-        postToCallback(CallbackMessageHandler.MSG_REPEAT_MODE, repeatMode);
-    }
-
-    private void dispatchShuffleMode(boolean enabled) {
-        postToCallback(CallbackMessageHandler.MSG_SHUFFLE_MODE, enabled);
-    }
-
     private void dispatchCustomAction(String action, Bundle args) {
         postToCallback(CallbackMessageHandler.MSG_CUSTOM_ACTION, action, args);
     }
 
-    private void dispatchAddQueueItem(MediaDescription description) {
-        postToCallback(CallbackMessageHandler.MSG_ADD_QUEUE_ITEM, description);
-    }
-
-    private void dispatchAddQueueItem(MediaDescription description, int index) {
-        postToCallback(CallbackMessageHandler.MSG_ADD_QUEUE_ITEM_AT, description, index);
-    }
-
-    private void dispatchRemoveQueueItem(MediaDescription description) {
-        postToCallback(CallbackMessageHandler.MSG_REMOVE_QUEUE_ITEM, description);
-    }
-
-    private void dispatchRemoveQueueItemAt(int index) {
-        postToCallback(CallbackMessageHandler.MSG_REMOVE_QUEUE_ITEM_AT, index);
-    }
-
     private void dispatchMediaButton(Intent mediaButtonIntent) {
         postToCallback(CallbackMessageHandler.MSG_MEDIA_BUTTON, mediaButtonIntent);
     }
@@ -695,22 +629,10 @@
         postToCallback(CallbackMessageHandler.MSG_COMMAND, cmd);
     }
 
-    private void postToCallback(int what, int arg1) {
-        postToCallback(what, null, arg1);
-    }
-
     private void postToCallback(int what, Object obj) {
         postToCallback(what, obj, null);
     }
 
-    private void postToCallback(int what, Object obj, int arg1) {
-        synchronized (mLock) {
-            if (mCallback != null) {
-                mCallback.post(what, obj, arg1);
-            }
-        }
-    }
-
     private void postToCallback(int what, Object obj, Bundle extras) {
         synchronized (mLock) {
             if (mCallback != null) {
@@ -1048,33 +970,6 @@
         }
 
         /**
-         * Override to handle the setting of the repeat mode.
-         * <p>
-         * You should call {@link #setRepeatMode} before end of this method in order to notify
-         * the change to the {@link MediaController}, or {@link MediaController#getRepeatMode}
-         * could return an invalid value.
-         *
-         * @param repeatMode The repeat mode which is one of followings:
-         *                   {@link PlaybackState#REPEAT_MODE_NONE},
-         *                   {@link PlaybackState#REPEAT_MODE_ONE},
-         *                   {@link PlaybackState#REPEAT_MODE_ALL}
-         */
-        public void onSetRepeatMode(@PlaybackState.RepeatMode int repeatMode) {
-        }
-
-        /**
-         * Override to handle the setting of the shuffle mode.
-         * <p>
-         * You should call {@link #setShuffleModeEnabled} before the end of this method in order to
-         * notify the change to the {@link MediaController}, or
-         * {@link MediaController#isShuffleModeEnabled} could return an invalid value.
-         *
-         * @param enabled true when the shuffle mode is enabled, false otherwise.
-         */
-        public void onSetShuffleModeEnabled(boolean enabled) {
-        }
-
-        /**
          * Called when a {@link MediaController} wants a {@link PlaybackState.CustomAction} to be
          * performed.
          *
@@ -1084,47 +979,6 @@
          */
         public void onCustomAction(@NonNull String action, @Nullable Bundle extras) {
         }
-
-        /**
-         * Called when a {@link MediaController} wants to add a {@link QueueItem} with the given
-         * {@link MediaDescription description} at the end of the play queue.
-         *
-         * @param description The {@link MediaDescription} for creating the {@link QueueItem} to be
-         *                    inserted.
-         */
-        public void onAddQueueItem(MediaDescription description) {
-        }
-
-        /**
-         * Called when a {@link MediaController} wants to add a {@link QueueItem} with the given
-         * {@link MediaDescription description} at the specified position in the play queue.
-         *
-         * @param description The {@link MediaDescription} for creating the {@link QueueItem} to be
-         *                    inserted.
-         * @param index The index at which the created {@link QueueItem} is to be inserted.
-         */
-        public void onAddQueueItem(MediaDescription description, int index) {
-        }
-
-        /**
-         * Called when a {@link MediaController} wants to remove the first occurrence of the
-         * specified {@link QueueItem} with the given {@link MediaDescription description}
-         * in the play queue.
-         *
-         * @param description The {@link MediaDescription} for denoting the {@link QueueItem} to be
-         *                    removed.
-         */
-        public void onRemoveQueueItem(MediaDescription description) {
-        }
-
-        /**
-         * Called when a {@link MediaController} wants to remove a {@link QueueItem} at the
-         * specified position in the play queue.
-         *
-         * @param index The index of the element to be removed.
-         */
-        public void onRemoveQueueItemAt(int index) {
-        }
     }
 
     /**
@@ -1297,22 +1151,6 @@
         }
 
         @Override
-        public void onRepeatMode(int repeatMode) {
-            MediaSession session = mMediaSession.get();
-            if (session != null) {
-                session.dispatchRepeatMode(repeatMode);
-            }
-        }
-
-        @Override
-        public void onShuffleMode(boolean enabled) {
-            MediaSession session = mMediaSession.get();
-            if (session != null) {
-                session.dispatchShuffleMode(enabled);
-            }
-        }
-
-        @Override
         public void onCustomAction(String action, Bundle args) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
@@ -1321,38 +1159,6 @@
         }
 
         @Override
-        public void onAddQueueItem(MediaDescription description) {
-            MediaSession session = mMediaSession.get();
-            if (session != null) {
-                session.dispatchAddQueueItem(description);
-            }
-        }
-
-        @Override
-        public void onAddQueueItemAt(MediaDescription description, int index) {
-            MediaSession session = mMediaSession.get();
-            if (session != null) {
-                session.dispatchAddQueueItem(description, index);
-            }
-        }
-
-        @Override
-        public void onRemoveQueueItem(MediaDescription description) {
-            MediaSession session = mMediaSession.get();
-            if (session != null) {
-                session.dispatchRemoveQueueItem(description);
-            }
-        }
-
-        @Override
-        public void onRemoveQueueItemAt(int index) {
-            MediaSession session = mMediaSession.get();
-            if (session != null) {
-                session.dispatchRemoveQueueItemAt(index);
-            }
-        }
-
-        @Override
         public void onAdjustVolume(int direction) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
@@ -1376,7 +1182,7 @@
      */
     public static final class QueueItem implements Parcelable {
         /**
-         * This id is reserved. No items can be explicitly asigned this id.
+         * This id is reserved. No items can be explicitly assigned this id.
          */
         public static final int UNKNOWN_ID = -1;
 
@@ -1485,15 +1291,9 @@
         private static final int MSG_REWIND = 17;
         private static final int MSG_SEEK_TO = 18;
         private static final int MSG_RATE = 19;
-        private static final int MSG_REPEAT_MODE = 20;
-        private static final int MSG_SHUFFLE_MODE = 21;
-        private static final int MSG_CUSTOM_ACTION = 22;
-        private static final int MSG_ADJUST_VOLUME = 23;
-        private static final int MSG_SET_VOLUME = 24;
-        private static final int MSG_ADD_QUEUE_ITEM = 25;
-        private static final int MSG_ADD_QUEUE_ITEM_AT = 26;
-        private static final int MSG_REMOVE_QUEUE_ITEM = 27;
-        private static final int MSG_REMOVE_QUEUE_ITEM_AT = 28;
+        private static final int MSG_CUSTOM_ACTION = 20;
+        private static final int MSG_ADJUST_VOLUME = 21;
+        private static final int MSG_SET_VOLUME = 22;
 
         private MediaSession.Callback mCallback;
 
@@ -1582,33 +1382,15 @@
                 case MSG_RATE:
                     mCallback.onSetRating((Rating) msg.obj);
                     break;
-                case MSG_REPEAT_MODE:
-                    mCallback.onSetRepeatMode(msg.arg1);
-                    break;
-                case MSG_SHUFFLE_MODE:
-                    mCallback.onSetShuffleModeEnabled((boolean) msg.obj);
-                    break;
                 case MSG_CUSTOM_ACTION:
                     mCallback.onCustomAction((String) msg.obj, msg.getData());
                     break;
-                case MSG_ADD_QUEUE_ITEM:
-                    mCallback.onAddQueueItem((MediaDescription) msg.obj);
-                    break;
-                case MSG_ADD_QUEUE_ITEM_AT:
-                    mCallback.onAddQueueItem((MediaDescription) msg.obj, msg.arg1);
-                    break;
-                case MSG_REMOVE_QUEUE_ITEM:
-                    mCallback.onRemoveQueueItem((MediaDescription) msg.obj);
-                    break;
-                case MSG_REMOVE_QUEUE_ITEM_AT:
-                    mCallback.onRemoveQueueItemAt(msg.arg1);
-                    break;
                 case MSG_ADJUST_VOLUME:
                     synchronized (mLock) {
                         vp = mVolumeProvider;
                     }
                     if (vp != null) {
-                        vp.onAdjustVolume(msg.arg1);
+                        vp.onAdjustVolume((int) msg.obj);
                     }
                     break;
                 case MSG_SET_VOLUME:
@@ -1616,7 +1398,7 @@
                         vp = mVolumeProvider;
                     }
                     if (vp != null) {
-                        vp.onSetVolumeTo(msg.arg1);
+                        vp.onSetVolumeTo((int) msg.obj);
                     }
                     break;
             }
diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java
index 1ea6109..8283c8b 100644
--- a/media/java/android/media/session/PlaybackState.java
+++ b/media/java/android/media/session/PlaybackState.java
@@ -45,8 +45,7 @@
             ACTION_SKIP_TO_PREVIOUS, ACTION_SKIP_TO_NEXT, ACTION_FAST_FORWARD, ACTION_SET_RATING,
             ACTION_SEEK_TO, ACTION_PLAY_PAUSE, ACTION_PLAY_FROM_MEDIA_ID, ACTION_PLAY_FROM_SEARCH,
             ACTION_SKIP_TO_QUEUE_ITEM, ACTION_PLAY_FROM_URI, ACTION_PREPARE,
-            ACTION_PREPARE_FROM_MEDIA_ID, ACTION_PREPARE_FROM_SEARCH, ACTION_PREPARE_FROM_URI,
-            ACTION_SET_REPEAT_MODE, ACTION_SET_SHUFFLE_MODE_ENABLED})
+            ACTION_PREPARE_FROM_MEDIA_ID, ACTION_PREPARE_FROM_SEARCH, ACTION_PREPARE_FROM_URI})
     @Retention(RetentionPolicy.SOURCE)
     public @interface Actions {}
 
@@ -177,20 +176,6 @@
     public static final long ACTION_PREPARE_FROM_URI = 1 << 17;
 
     /**
-     * Indicates this session supports the set repeat mode command.
-     *
-     * @see Builder#setActions(long)
-     */
-    public static final long ACTION_SET_REPEAT_MODE = 1 << 18;
-
-    /**
-     * Indicates this session supports the set shuffle mode enabled command.
-     *
-     * @see Builder#setActions(long)
-     */
-    public static final long ACTION_SET_SHUFFLE_MODE_ENABLED = 1 << 19;
-
-    /**
      * @hide
      */
     @IntDef({STATE_NONE, STATE_STOPPED, STATE_PAUSED, STATE_PLAYING, STATE_FAST_FORWARDING,
@@ -296,30 +281,6 @@
      */
     public final static long PLAYBACK_POSITION_UNKNOWN = -1;
 
-    /**
-     * @hide
-     */
-    @IntDef({REPEAT_MODE_NONE, REPEAT_MODE_ONE, REPEAT_MODE_ALL})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface RepeatMode {}
-    /**
-     * Use this value with {@link MediaController.TransportControls#setRepeatMode}
-     * to indicate that the playback will be stopped at the end of the playing media list.
-     */
-    public final static int REPEAT_MODE_NONE = 0;
-
-    /**
-     * Use this value with {@link MediaController.TransportControls#setRepeatMode}
-     * to indicate that the playback of the current playing media item will be repeated.
-     */
-    public final static int REPEAT_MODE_ONE = 1;
-
-    /**
-     * Use this value with {@link MediaController.TransportControls#setRepeatMode}
-     * to indicate that the playback of the playing media list will be repeated.
-     */
-    public final static int REPEAT_MODE_ALL = 2;
-
     private final int mState;
     private final long mPosition;
     private final long mBufferedPosition;
@@ -466,8 +427,6 @@
      * <li> {@link PlaybackState#ACTION_PREPARE_FROM_MEDIA_ID}</li>
      * <li> {@link PlaybackState#ACTION_PREPARE_FROM_SEARCH}</li>
      * <li> {@link PlaybackState#ACTION_PREPARE_FROM_URI}</li>
-     * <li> {@link PlaybackState#ACTION_SET_REPEAT_MODE}</li>
-     * <li> {@link PlaybackState#ACTION_SET_SHUFFLE_MODE_ENABLED}</li>
      * </ul>
      */
     @Actions
@@ -1002,8 +961,6 @@
          * <li> {@link PlaybackState#ACTION_PREPARE_FROM_MEDIA_ID}</li>
          * <li> {@link PlaybackState#ACTION_PREPARE_FROM_SEARCH}</li>
          * <li> {@link PlaybackState#ACTION_PREPARE_FROM_URI}</li>
-         * <li> {@link PlaybackState#ACTION_SET_REPEAT_MODE}</li>
-         * <li> {@link PlaybackState#ACTION_SET_SHUFFLE_MODE_ENABLED}</li>
          * </ul>
          *
          * @param actions The set of actions allowed.
diff --git a/media/java/android/service/media/IMediaBrowserService.aidl b/media/java/android/service/media/IMediaBrowserService.aidl
index e95154f..84f41f6 100644
--- a/media/java/android/service/media/IMediaBrowserService.aidl
+++ b/media/java/android/service/media/IMediaBrowserService.aidl
@@ -19,10 +19,8 @@
 
     void addSubscriptionDeprecated(String uri, IMediaBrowserServiceCallbacks callbacks);
     void removeSubscriptionDeprecated(String uri, IMediaBrowserServiceCallbacks callbacks);
-    void getMediaItem(String uri, in ResultReceiver cb, IMediaBrowserServiceCallbacks callbacks);
-    void search(String query, in Bundle extras, in ResultReceiver cb,
-            IMediaBrowserServiceCallbacks callbacks);
 
+    void getMediaItem(String uri, in ResultReceiver cb, IMediaBrowserServiceCallbacks callbacks);
     void addSubscription(String uri, in IBinder token, in Bundle options,
             IMediaBrowserServiceCallbacks callbacks);
     void removeSubscription(String uri, in IBinder token, IMediaBrowserServiceCallbacks callbacks);
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index d372efb..b52906d 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -89,15 +89,8 @@
      */
     public static final String KEY_MEDIA_ITEM = "media_item";
 
-    /**
-     * A key for passing the list of MediaItems to the ResultReceiver in search.
-     * @hide
-     */
-    public static final String KEY_SEARCH_RESULTS = "search_results";
-
     private static final int RESULT_FLAG_OPTION_NOT_HANDLED = 1 << 0;
     private static final int RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED = 1 << 1;
-    private static final int RESULT_FLAG_ON_SEARCH_NOT_IMPLEMENTED = 1 << 2;
 
     private static final int RESULT_ERROR = -1;
     private static final int RESULT_OK = 0;
@@ -105,7 +98,7 @@
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag=true, value = { RESULT_FLAG_OPTION_NOT_HANDLED,
-            RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED, RESULT_FLAG_ON_SEARCH_NOT_IMPLEMENTED })
+            RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED })
     private @interface ResultFlags { }
 
     private final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap<>();
@@ -137,7 +130,6 @@
      *
      * @see #onLoadChildren
      * @see #onLoadItem
-     * @see #onSearch
      */
     public class Result<T> {
         private Object mDebug;
@@ -330,23 +322,6 @@
                 }
             });
         }
-
-        @Override
-        public void search(final String query, Bundle extras, ResultReceiver receiver,
-                final IMediaBrowserServiceCallbacks callbacks) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    final IBinder b = callbacks.asBinder();
-                    ConnectionRecord connection = mConnections.get(b);
-                    if (connection == null) {
-                        Log.w(TAG, "search for callback that isn't registered query=" + query);
-                        return;
-                    }
-                    performSearch(query, extras, connection, receiver);
-                }
-            });
-        }
     }
 
     @Override
@@ -472,32 +447,6 @@
     }
 
     /**
-     * Called to get the search result.
-     * <p>
-     * Implementations must call {@link Result#sendResult result.sendResult}. If
-     * the search will be an expensive operation {@link Result#detach result.detach}
-     * may be called before returning from this function, and then {@link Result#sendResult
-     * result.sendResult} called when the search has been completed.
-     * </p><p>
-     * In case there are no search results, call {@link Result#sendResult} with an empty list.
-     * In case there are some errors happened, call {@link Result#sendResult result.sendResult}
-     * with {@code null}, which will invoke {@link MediaBrowser.SearchCallback#onError}.
-     * </p><p>
-     * The default implementation will invoke {@link MediaBrowser.SearchCallback#onError}.
-     * </p>
-     *
-     * @param query The search query sent from the media browser. It contains keywords separated
-     *            by space.
-     * @param extras The bundle of service-specific arguments sent from the media browser.
-     * @param result The {@link Result} to send the search result.
-     */
-    public void onSearch(@NonNull String query, Bundle extras,
-            Result<List<MediaBrowser.MediaItem>> result) {
-        result.setFlags(RESULT_FLAG_ON_SEARCH_NOT_IMPLEMENTED);
-        result.sendResult(null);
-    }
-
-    /**
      * Call to set the media session.
      * <p>
      * This should be called as soon as possible during the service's startup.
@@ -545,16 +494,16 @@
      * media browser service when connecting and retrieving the root id for browsing, or null if
      * none. The contents of this bundle may affect the information returned when browsing.
      *
-     * @throws IllegalStateException If this method is called outside of {@link #onLoadChildren},
-     *             {@link #onLoadItem} or {@link #onSearch}.
+     * @throws IllegalStateException If this method is called outside of {@link #onLoadChildren} or
+     *             {@link #onLoadItem}.
      * @see MediaBrowserService.BrowserRoot#EXTRA_RECENT
      * @see MediaBrowserService.BrowserRoot#EXTRA_OFFLINE
      * @see MediaBrowserService.BrowserRoot#EXTRA_SUGGESTED
      */
     public final Bundle getBrowserRootHints() {
         if (mCurConnection == null) {
-            throw new IllegalStateException("This should be called inside of onLoadChildren,"
-                    + " onLoadItem or onSearch methods");
+            throw new IllegalStateException("This should be called inside of onLoadChildren or"
+                    + " onLoadItem methods");
         }
         return mCurConnection.rootHints == null ? null : new Bundle(mCurConnection.rootHints);
     }
@@ -771,34 +720,6 @@
         }
     }
 
-    private void performSearch(String query, Bundle extras, final ConnectionRecord connection,
-            final ResultReceiver receiver) {
-        final Result<List<MediaBrowser.MediaItem>> result =
-                new Result<List<MediaBrowser.MediaItem>>(query) {
-            @Override
-            void onResultSent(List<MediaBrowser.MediaItem> items, @ResultFlags int flag) {
-                if ((flag & RESULT_FLAG_ON_SEARCH_NOT_IMPLEMENTED) != 0
-                        || items == null) {
-                    receiver.send(RESULT_ERROR, null);
-                    return;
-                }
-                Bundle bundle = new Bundle();
-                bundle.putParcelableArray(KEY_SEARCH_RESULTS,
-                        items.toArray(new MediaBrowser.MediaItem[0]));
-                receiver.send(RESULT_OK, bundle);
-            }
-        };
-
-        mCurConnection = connection;
-        onSearch(query, extras, result);
-        mCurConnection = null;
-
-        if (!result.isDone()) {
-            throw new IllegalStateException("onSearch must call detach() or sendResult()"
-                    + " before returning for query=" + query);
-        }
-    }
-
     /**
      * Contains information that the browser service needs to send to the client
      * when first connected.
diff --git a/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/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index c565373..28bd23c 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -143,10 +143,10 @@
 
         @Override
         public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds,
-                Rect animatingBounds, boolean fromImeAdjustement) {
+                Rect animatingBounds, boolean fromImeAdjustement, int displayRotation) {
             mHandler.post(() -> {
                 mTouchHandler.onMovementBoundsChanged(insetBounds, normalBounds, animatingBounds,
-                        fromImeAdjustement);
+                        fromImeAdjustement, displayRotation);
             });
         }
 
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 ac06703..982b808 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -221,6 +221,13 @@
     }
 
     @Override
+    protected void onStop() {
+        super.onStop();
+
+        cancelDelayedFinish();
+    }
+
+    @Override
     protected void onDestroy() {
         super.onDestroy();
 
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index 127296cd..67255d3 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -316,7 +316,8 @@
      * Animates the PiP from the expanded state to the normal state after the menu is hidden.
      */
     void animateToUnexpandedState(Rect normalBounds, float savedSnapFraction,
-            Rect normalMovementBounds, Rect currentMovementBounds, boolean minimized) {
+            Rect normalMovementBounds, Rect currentMovementBounds, boolean minimized,
+            boolean immediate) {
         if (savedSnapFraction < 0f) {
             // If there are no saved snap fractions, then just use the current bounds
             savedSnapFraction = mSnapAlgorithm.getSnapFraction(new Rect(mBounds),
@@ -326,7 +327,11 @@
         if (minimized) {
             normalBounds = getClosestMinimizedBounds(normalBounds, normalMovementBounds);
         }
-        resizeAndAnimatePipUnchecked(normalBounds, SHRINK_STACK_FROM_MENU_DURATION);
+        if (immediate) {
+            movePip(normalBounds);
+        } else {
+            resizeAndAnimatePipUnchecked(normalBounds, SHRINK_STACK_FROM_MENU_DURATION);
+        }
     }
 
     /**
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 3f26fdd..fbf7ff2 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -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();
@@ -89,6 +90,11 @@
     private Rect mExpandedMovementBounds = new Rect();
     private int mExpandedShortestEdgeSize;
 
+    // Used to workaround an issue where the WM rotation happens before we are notified, allowing
+    // us to send stale bounds
+    private int mDeferResizeToNormalBoundsUntilRotation = -1;
+    private int mDisplayRotation;
+
     private Handler mHandler = new Handler();
     private Runnable mShowDismissAffordance = new Runnable() {
         @Override
@@ -216,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
@@ -250,7 +261,7 @@
     }
 
     public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds, Rect animatingBounds,
-            boolean fromImeAdjustement) {
+            boolean fromImeAdjustement, int displayRotation) {
         // Re-calculate the expanded bounds
         mNormalBounds = normalBounds;
         Rect normalMovementBounds = new Rect();
@@ -304,7 +315,17 @@
         // above
         mNormalMovementBounds = normalMovementBounds;
         mExpandedMovementBounds = expandedMovementBounds;
+        mDisplayRotation = displayRotation;
         updateMovementBounds(mMenuState);
+
+        // If we have a deferred resize, apply it now
+        if (mDeferResizeToNormalBoundsUntilRotation == displayRotation) {
+            mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction,
+                    mNormalMovementBounds, mMovementBounds, mIsMinimized,
+                    true /* immediate */);
+            mSavedSnapFraction = -1f;
+            mDeferResizeToNormalBoundsUntilRotation = -1;
+        }
     }
 
     private void onRegistrationChanged(boolean isRegistered) {
@@ -474,11 +495,34 @@
             // Try and restore the PiP to the closest edge, using the saved snap fraction
             // if possible
             if (resize) {
-                Rect normalBounds = new Rect(mNormalBounds);
-                mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction,
-                        mNormalMovementBounds, mMovementBounds, mIsMinimized);
+                // This is a very special case: when the menu is expanded and visible, navigating to
+                // another activity can trigger auto-enter PiP, and if the revealed activity has a
+                // forced rotation set, then the controller will get updated with the new rotation
+                // of the display. However, at the same time, SystemUI will try to hide the menu by
+                // creating an animation to the normal bounds which are now stale.  In such a case
+                // we defer the animation to the normal bounds until after the next
+                // onMovementBoundsChanged() call to get the bounds in the new orientation
+                if (mDeferResizeToNormalBoundsUntilRotation == -1) {
+                    try {
+                        int displayRotation = mPinnedStackController.getDisplayRotation();
+                        if (mDisplayRotation != displayRotation) {
+                            mDeferResizeToNormalBoundsUntilRotation = displayRotation;
+                        }
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Could not get display rotation from controller");
+                    }
+                }
+
+                if (mDeferResizeToNormalBoundsUntilRotation == -1) {
+                    Rect normalBounds = new Rect(mNormalBounds);
+                    mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction,
+                            mNormalMovementBounds, mMovementBounds, mIsMinimized,
+                            false /* immediate */);
+                    mSavedSnapFraction = -1f;
+                }
+            } else {
+                mSavedSnapFraction = -1f;
             }
-            mSavedSnapFraction = -1f;
         }
         mMenuState = menuState;
         updateMovementBounds(menuState);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index f2f0d7a..657f08b 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -178,7 +178,7 @@
 
         @Override
         public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds,
-                Rect animatingBounds, boolean fromImeAdjustement) {
+                Rect animatingBounds, boolean fromImeAdjustement, int displayRotation) {
             mHandler.post(() -> {
                 mDefaultPipBounds.set(normalBounds);
             });
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 677642e..cc91753 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -1115,6 +1115,10 @@
         mNotificationInflater.setInflateExceptionHandler(inflateExceptionHandler);
     }
 
+    public void setNeedsRedaction(boolean needsRedaction) {
+        mNotificationInflater.setRedactAmbient(needsRedaction);
+    }
+
     public interface ExpansionLogger {
         public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 8f160dc..6098565 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -428,6 +428,9 @@
             mAmbientChild.animate().cancel();
             removeView(mAmbientChild);
         }
+        if (child == null) {
+            return;
+        }
         addView(child);
         mAmbientChild = child;
         mAmbientWrapper = NotificationViewWrapper.wrap(getContext(), child,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java
index fee24b7..802925a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java
@@ -227,6 +227,7 @@
                 if (mShouldShowMenu
                         && !NotificationStackScrollLayout.isPinnedHeadsUp(view)
                         && !mParent.areGutsExposed()
+                        && !mParent.isDark()
                         && (mCheckForDrag == null || !mHandler.hasCallbacks(mCheckForDrag))) {
                     // Only show the menu if we're not a heads up view and guts aren't exposed.
                     mCheckForDrag = new CheckForDrag();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
index 73eecbb..2e34f24 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
@@ -21,6 +21,8 @@
 import android.service.notification.StatusBarNotification;
 import android.util.Log;
 import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
 import android.widget.RemoteViews;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -49,6 +51,7 @@
     private RemoteViews.OnClickHandler mRemoteViewClickHandler;
     private boolean mIsChildInGroup;
     private InflationExceptionHandler mInflateExceptionHandler;
+    private boolean mRedactAmbient;
 
     public NotificationInflater(ExpandableNotificationRow row) {
         mRow = row;
@@ -92,6 +95,21 @@
         mRemoteViewClickHandler = remoteViewClickHandler;
     }
 
+    public void setRedactAmbient(boolean redactAmbient) {
+        if (mRedactAmbient != redactAmbient) {
+            mRedactAmbient = redactAmbient;
+            if (mRow.getEntry() == null) {
+                return;
+            }
+            try {
+                inflateNotificationViews(FLAG_REINFLATE_AMBIENT_VIEW);
+            } catch (InflationException e) {
+                mInflateExceptionHandler.handleInflationException(
+                        mRow.getStatusBarNotification(), e);
+            }
+        }
+    }
+
     public void inflateNotificationViews() throws InflationException {
         inflateNotificationViews(FLAG_REINFLATE_ALL);
     }
@@ -123,6 +141,8 @@
             Notification.Builder builder, Context packageContext) {
         NotificationData.Entry entry = mRow.getEntry();
         NotificationContentView privateLayout = mRow.getPrivateLayout();
+        NotificationContentView publicLayout = mRow.getPublicLayout();
+
         boolean isLowPriority = mIsLowPriority && !mIsChildInGroup;
         if ((reInflateFlags & FLAG_REINFLATE_CONTENT_VIEW) != 0) {
             final RemoteViews newContentView = createContentView(builder,
@@ -190,7 +210,6 @@
         }
 
         if ((reInflateFlags & FLAG_REINFLATE_PUBLIC_VIEW) != 0) {
-            NotificationContentView publicLayout = mRow.getPublicLayout();
             final RemoteViews newPublicNotification
                     = builder.makePublicContentView();
             if (!compareRemoteViews(newPublicNotification, entry.cachedPublicContentView)) {
@@ -209,18 +228,24 @@
         }
 
         if ((reInflateFlags & FLAG_REINFLATE_AMBIENT_VIEW) != 0) {
-            final RemoteViews newAmbientNotification
-                    = builder.makeAmbientNotification();
-            if (!compareRemoteViews(newAmbientNotification, entry.cachedAmbientContentView)) {
+            final RemoteViews newAmbientNotification = mRedactAmbient
+                    ? builder.makePublicAmbientNotification()
+                    : builder.makeAmbientNotification();
+            NotificationContentView newParent = mRedactAmbient ? publicLayout : privateLayout;
+            NotificationContentView otherParent = !mRedactAmbient ? publicLayout : privateLayout;
+
+            if (newParent.getAmbientChild() == null ||
+                    !compareRemoteViews(newAmbientNotification, entry.cachedAmbientContentView)) {
                 View ambientContentView = newAmbientNotification.apply(
                         packageContext,
-                        privateLayout,
+                        newParent,
                         mRemoteViewClickHandler);
                 ambientContentView.setIsRootNamespace(true);
-                privateLayout.setAmbientChild(ambientContentView);
+                newParent.setAmbientChild(ambientContentView);
+                otherParent.setAmbientChild(null);
             } else {
                 newAmbientNotification.reapply(packageContext,
-                        privateLayout.getAmbientChild(),
+                        newParent.getAmbientChild(),
                         mRemoteViewClickHandler);
             }
             entry.cachedAmbientContentView = newAmbientNotification;
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 39060fc..79191f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -49,10 +49,8 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.IntentSender;
-import android.content.pm.ActivityInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.database.ContentObserver;
@@ -141,7 +139,6 @@
 import com.android.systemui.fragments.FragmentHostManager;
 import com.android.systemui.fragments.PluginFragmentListener;
 import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.pip.phone.PipManager;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
@@ -1814,6 +1811,7 @@
                 updatePublicContentView(ent, ent.notification);
             }
             ent.row.setSensitive(sensitive, deviceSensitive);
+            ent.row.setNeedsRedaction(needsRedaction(ent));
             if (mGroupManager.isChildInGroupWithSummary(ent.row.getStatusBarNotification())) {
                 ExpandableNotificationRow summary = mGroupManager.getGroupSummary(
                         ent.row.getStatusBarNotification());
@@ -1902,6 +1900,21 @@
         mNotificationIconAreaController.updateNotificationIcons(mNotificationData);
     }
 
+    /** @return true if the entry needs redaction when on the lockscreen. */
+    private boolean needsRedaction(Entry ent) {
+        int userId = ent.notification.getUserId();
+
+        boolean currentUserWantsRedaction = !userAllowsPrivateNotificationsInPublic(mCurrentUserId);
+        boolean notiUserWantsRedaction = !userAllowsPrivateNotificationsInPublic(userId);
+        boolean redactedLockscreen = currentUserWantsRedaction || notiUserWantsRedaction;
+
+        boolean notificationRequestsRedaction =
+                ent.notification.getNotification().visibility == Notification.VISIBILITY_PRIVATE;
+        boolean userForcesRedaction = packageHasVisibilityOverride(ent.notification.getKey());
+
+        return userForcesRedaction || notificationRequestsRedaction && redactedLockscreen;
+    }
+
     /**
      * Disable QS if device not provisioned.
      * If the user switcher is simple then disable QS during setup because
@@ -5862,6 +5875,9 @@
                 }
 
                 final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+                if (row.isDark()) {
+                    return false;
+                }
                 bindGuts(row, item);
                 NotificationGuts guts = row.getGuts();
 
@@ -6163,6 +6179,7 @@
             }
         }
 
+        row.setNeedsRedaction(needsRedaction(entry));
         boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey());
         row.setIsLowPriority(isLowPriority);
         // bind the click event to the content area
@@ -6527,7 +6544,6 @@
         NotificationData.Entry entry = new NotificationData.Entry(sbn);
         Dependency.get(LeakDetector.class).trackInstance(entry);
         entry.createIcons(mContext, sbn);
-
         // Construct the expanded view.
         inflateViews(entry, mStackScroller);
         return entry;
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 c77820b..2cd14e9 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 1c2a6aa..d43fa01 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1230,6 +1230,20 @@
      */
     int[] mDeviceIdleTempWhitelist = new int[0];
 
+    static final class PendingTempWhitelist {
+        final int targetUid;
+        final long duration;
+        final String tag;
+
+        PendingTempWhitelist(int _targetUid, long _duration, String _tag) {
+            targetUid = _targetUid;
+            duration = _duration;
+            tag = _tag;
+        }
+    }
+
+    final SparseArray<PendingTempWhitelist> mPendingTempWhitelist = new SparseArray<>();
+
     /**
      * Information about and control over application operations
      */
@@ -1688,6 +1702,7 @@
     static final int NOTIFY_VR_SLEEPING_MSG = 65;
     static final int SERVICE_FOREGROUND_TIMEOUT_MSG = 66;
     static final int DISPATCH_PENDING_INTENT_CANCEL_MSG = 67;
+    static final int PUSH_TEMP_WHITELIST_UI_MSG = 68;
     static final int START_USER_SWITCH_FG_MSG = 712;
 
     static final int FIRST_ACTIVITY_STACK_MSG = 100;
@@ -1921,6 +1936,9 @@
             case DISPATCH_UIDS_CHANGED_UI_MSG: {
                 dispatchUidsChanged();
             } break;
+            case PUSH_TEMP_WHITELIST_UI_MSG: {
+                pushTempWhitelist();
+            } break;
             }
         }
     }
@@ -6493,7 +6511,8 @@
             // This is the first appearance of the uid, report it now!
             if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
                     "Creating new process uid: " + uidRec);
-            if (Arrays.binarySearch(mDeviceIdleTempWhitelist, UserHandle.getAppId(proc.uid)) >= 0) {
+            if (Arrays.binarySearch(mDeviceIdleTempWhitelist, UserHandle.getAppId(proc.uid)) >= 0
+                    || mPendingTempWhitelist.indexOfKey(proc.uid) >= 0) {
                 uidRec.setWhitelist = uidRec.curWhitelist = true;
             }
             uidRec.updateHasInternetPermission();
@@ -7487,43 +7506,6 @@
         }
     }
 
-    /**
-     * Whitelists {@code targetUid} to temporarily bypass Power Save mode.
-     */
-    void tempWhitelistAppForPowerSave(int callerPid, int callerUid, int targetUid, long duration) {
-        if (DEBUG_WHITELISTS) {
-            Slog.d(TAG, "tempWhitelistAppForPowerSave(" + callerPid + ", " + callerUid + ", "
-                    + targetUid + ", " + duration + ")");
-        }
-
-        if (checkPermission(CHANGE_DEVICE_IDLE_TEMP_WHITELIST, callerPid, callerUid)
-                != PackageManager.PERMISSION_GRANTED) {
-            synchronized (mPidsSelfLocked) {
-                final ProcessRecord pr = mPidsSelfLocked.get(callerPid);
-                if (pr == null) {
-                    Slog.w(TAG, "tempWhitelistAppForPowerSave() no ProcessRecord for pid "
-                            + callerPid);
-                    return;
-                }
-                if (!pr.whitelistManager) {
-                    if (DEBUG_WHITELISTS) {
-                        Slog.d(TAG, "tempWhitelistAppForPowerSave() for target " + targetUid
-                                + ": pid " + callerPid + " is not allowed");
-                    }
-                    return;
-                }
-            }
-        }
-
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mLocalDeviceIdleController.addPowerSaveTempWhitelistAppDirect(targetUid, duration,
-                    true, "pe from uid:" + callerUid);
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
     @Override
     public void cancelIntentSender(IIntentSender sender) {
         if (!(sender instanceof PendingIntentRecord)) {
@@ -7863,7 +7845,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);
@@ -8412,7 +8401,8 @@
     boolean isOnDeviceIdleWhitelistLocked(int uid) {
         final int appId = UserHandle.getAppId(uid);
         return Arrays.binarySearch(mDeviceIdleWhitelist, appId) >= 0
-                || Arrays.binarySearch(mDeviceIdleTempWhitelist, appId) >= 0;
+                || Arrays.binarySearch(mDeviceIdleTempWhitelist, appId) >= 0
+                || mPendingTempWhitelist.indexOfKey(uid) >= 0;
     }
 
     private ProviderInfo getProviderInfoLocked(String authority, int userHandle, int pmFlags) {
@@ -15588,6 +15578,18 @@
             }
             pw.println("  mDeviceIdleWhitelist=" + Arrays.toString(mDeviceIdleWhitelist));
             pw.println("  mDeviceIdleTempWhitelist=" + Arrays.toString(mDeviceIdleTempWhitelist));
+            if (mPendingTempWhitelist.size() > 0) {
+                pw.println("  mPendingTempWhitelist:");
+                for (int i = 0; i < mPendingTempWhitelist.size(); i++) {
+                    PendingTempWhitelist ptw = mPendingTempWhitelist.valueAt(i);
+                    pw.print("    ");
+                    UserHandle.formatUid(pw, ptw.targetUid);
+                    pw.print(": ");
+                    TimeUtils.formatDuration(ptw.duration, pw);
+                    pw.print(" ");
+                    pw.println(ptw.tag);
+                }
+            }
         }
         if (dumpPackage == null) {
             pw.println("  mWakefulness="
@@ -17929,8 +17931,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
@@ -17951,7 +17952,7 @@
             ComponentName res;
             try {
                 res = mServices.startServiceLocked(caller, service,
-                        resolvedType, id, notification, callingPid, callingUid,
+                        resolvedType, callingPid, callingUid,
                         requireForeground, callingPackage, userId);
             } finally {
                 Binder.restoreCallingIdentity(origId);
@@ -17970,7 +17971,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);
             }
@@ -22700,6 +22701,80 @@
         enqueueUidChangeLocked(uidRec, uid, UidRecord.CHANGE_IDLE);
     }
 
+    /**
+     * Whitelists {@code targetUid} to temporarily bypass Power Save mode.
+     */
+    void tempWhitelistForPendingIntentLocked(int callerPid, int callerUid, int targetUid,
+            long duration, String tag) {
+        if (DEBUG_WHITELISTS) {
+            Slog.d(TAG, "tempWhitelistForPendingIntentLocked(" + callerPid + ", " + callerUid + ", "
+                    + targetUid + ", " + duration + ")");
+        }
+
+        synchronized (mPidsSelfLocked) {
+            final ProcessRecord pr = mPidsSelfLocked.get(callerPid);
+            if (pr == null) {
+                Slog.w(TAG, "tempWhitelistForPendingIntentLocked() no ProcessRecord for pid "
+                        + callerPid);
+                return;
+            }
+            if (!pr.whitelistManager) {
+                if (checkPermission(CHANGE_DEVICE_IDLE_TEMP_WHITELIST, callerPid, callerUid)
+                        != PackageManager.PERMISSION_GRANTED) {
+                    if (DEBUG_WHITELISTS) {
+                        Slog.d(TAG, "tempWhitelistForPendingIntentLocked() for target " + targetUid
+                                + ": pid " + callerPid + " is not allowed");
+                    }
+                    return;
+                }
+            }
+        }
+
+        tempWhitelistUidLocked(targetUid, duration, tag);
+    }
+
+    /**
+     * Whitelists {@code targetUid} to temporarily bypass Power Save mode.
+     */
+    void tempWhitelistUidLocked(int targetUid, long duration, String tag) {
+        mPendingTempWhitelist.put(targetUid, new PendingTempWhitelist(targetUid, duration, tag));
+        setUidTempWhitelistStateLocked(targetUid, true);
+        mUiHandler.obtainMessage(PUSH_TEMP_WHITELIST_UI_MSG).sendToTarget();
+    }
+
+    void pushTempWhitelist() {
+        final int N;
+        final PendingTempWhitelist[] list;
+
+        // First copy out the pending changes...  we need to leave them in the map for now,
+        // in case someone needs to check what is coming up while we don't have the lock held.
+        synchronized(this) {
+            N = mPendingTempWhitelist.size();
+            list = new PendingTempWhitelist[N];
+            for (int i = 0; i < N; i++) {
+                list[i] = mPendingTempWhitelist.valueAt(i);
+            }
+        }
+
+        // Now safely dispatch changes to device idle controller.
+        for (int i = 0; i < N; i++) {
+            PendingTempWhitelist ptw = list[i];
+            mLocalDeviceIdleController.addPowerSaveTempWhitelistAppDirect(ptw.targetUid,
+                    ptw.duration, true, ptw.tag);
+        }
+
+        // And now we can safely remove them from the map.
+        synchronized(this) {
+            for (int i = 0; i < N; i++) {
+                PendingTempWhitelist ptw = list[i];
+                int index = mPendingTempWhitelist.indexOfKey(ptw.targetUid);
+                if (index >= 0 && mPendingTempWhitelist.valueAt(index) == ptw) {
+                    mPendingTempWhitelist.removeAt(index);
+                }
+            }
+        }
+    }
+
     final void setAppIdTempWhitelistStateLocked(int appId, boolean onWhitelist) {
         boolean changed = false;
         for (int i=mActiveUids.size()-1; i>=0; i--) {
@@ -22714,6 +22789,15 @@
         }
     }
 
+    final void setUidTempWhitelistStateLocked(int uid, boolean onWhitelist) {
+        boolean changed = false;
+        final UidRecord uidRec = mActiveUids.get(uid);
+        if (uidRec != null && uidRec.curWhitelist != onWhitelist) {
+            uidRec.curWhitelist = onWhitelist;
+            updateOomAdjLocked();
+        }
+    }
+
     final void trimApplications() {
         synchronized (this) {
             int i;
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/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index 2881787..494aaa7 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -314,7 +314,8 @@
             builder.setPackageName(info.launchedActivity.packageName);
             builder.setType(type);
             builder.addTaggedData(FIELD_CLASS_NAME, info.launchedActivity.info.name);
-            if (info.launchedActivity.launchedFromPackage != null) {
+            final boolean isInstantApp = info.launchedActivity.info.applicationInfo.isInstantApp();
+            if (isInstantApp && info.launchedActivity.launchedFromPackage != null) {
                 builder.addTaggedData(APP_TRANSITION_CALLING_PACKAGE_NAME,
                         info.launchedActivity.launchedFromPackage);
             }
@@ -323,8 +324,7 @@
                         info.launchedActivity.info.launchToken);
                 info.launchedActivity.info.launchToken = null;
             }
-            builder.addTaggedData(APP_TRANSITION_IS_EPHEMERAL,
-                    info.launchedActivity.info.applicationInfo.isInstantApp() ? 1 : 0);
+            builder.addTaggedData(APP_TRANSITION_IS_EPHEMERAL, isInstantApp ? 1 : 0);
             builder.addTaggedData(APP_TRANSITION_DEVICE_UPTIME_SECONDS,
                     mCurrentTransitionDeviceUptime);
             builder.addTaggedData(APP_TRANSITION_DELAY_MS, mCurrentTransitionDelayMs);
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 17c7dde..7cdddc0 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;
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/AppErrorDialog.java b/services/core/java/com/android/server/am/AppErrorDialog.java
index 02ec075..c9c1d00 100644
--- a/services/core/java/com/android/server/am/AppErrorDialog.java
+++ b/services/core/java/com/android/server/am/AppErrorDialog.java
@@ -148,18 +148,7 @@
 
     private final Handler mHandler = new Handler() {
         public void handleMessage(Message msg) {
-            final int result = msg.what;
-
-            synchronized (mService) {
-                if (mProc != null && mProc.crashDialog == AppErrorDialog.this) {
-                    mProc.crashDialog = null;
-                }
-            }
-            mResult.set(result);
-
-            // Make sure we don't have time timeout still hanging around.
-            removeMessages(TIMEOUT);
-
+            setResult(msg.what);
             dismiss();
         }
     };
@@ -168,11 +157,23 @@
     public void dismiss() {
         if (!mResult.mHasResult) {
             // We are dismissing and the result has not been set...go ahead and set.
-            mResult.set(FORCE_QUIT);
+            setResult(FORCE_QUIT);
         }
         super.dismiss();
     }
 
+    private void setResult(int result) {
+        synchronized (mService) {
+            if (mProc != null && mProc.crashDialog == AppErrorDialog.this) {
+                mProc.crashDialog = null;
+            }
+        }
+        mResult.set(result);
+
+        // Make sure we don't have time timeout still hanging around.
+        mHandler.removeMessages(TIMEOUT);
+    }
+
     @Override
     public void onClick(View v) {
         switch (v.getId()) {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index baa71d7..349180f 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -155,8 +155,6 @@
 
     static final int BROADCAST_INTENT_MSG = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG;
     static final int BROADCAST_TIMEOUT_MSG = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG + 1;
-    static final int SCHEDULE_TEMP_WHITELIST_MSG
-            = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG + 2;
 
     final BroadcastHandler mHandler;
 
@@ -178,13 +176,6 @@
                         broadcastTimeoutLocked(true);
                     }
                 } break;
-                case SCHEDULE_TEMP_WHITELIST_MSG: {
-                    DeviceIdleController.LocalService dic = mService.mLocalDeviceIdleController;
-                    if (dic != null) {
-                        dic.addPowerSaveTempWhitelistAppDirect(UserHandle.getAppId(msg.arg1),
-                                msg.arg2, true, (String)msg.obj);
-                    }
-                } break;
             }
         }
     }
@@ -789,12 +780,11 @@
         if (r.intent.getAction() != null) {
             b.append(r.intent.getAction());
         } else if (r.intent.getComponent() != null) {
-            b.append(r.intent.getComponent().flattenToShortString());
+            r.intent.getComponent().appendShortString(b);
         } else if (r.intent.getData() != null) {
             b.append(r.intent.getData());
         }
-        mHandler.obtainMessage(SCHEDULE_TEMP_WHITELIST_MSG, uid, (int)duration, b.toString())
-                .sendToTarget();
+        mService.tempWhitelistUidLocked(uid, duration, b.toString());
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index c697f28..a580d4b 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -237,14 +237,6 @@
         if (intent != null) intent.setDefusable(true);
         if (options != null) options.setDefusable(true);
 
-        if (whitelistDuration > 0 && !canceled) {
-            // Must call before acquiring the lock. It's possible the method return before sending
-            // the intent due to some validations inside the lock, in which case the UID shouldn't
-            // be whitelisted, but since the whitelist is temporary, that would be ok.
-            owner.tempWhitelistAppForPowerSave(Binder.getCallingPid(), Binder.getCallingUid(), uid,
-                    whitelistDuration);
-        }
-
         synchronized (owner) {
             final ActivityContainer activityContainer = (ActivityContainer)container;
             if (activityContainer != null && activityContainer.mParentActivity != null &&
@@ -279,6 +271,22 @@
                     resolvedType = key.requestResolvedType;
                 }
 
+                if (whitelistDuration > 0) {
+                    StringBuilder tag = new StringBuilder(64);
+                    tag.append("pendingintent:");
+                    UserHandle.formatUid(tag, Binder.getCallingUid());
+                    tag.append(":");
+                    if (finalIntent.getAction() != null) {
+                        tag.append(finalIntent.getAction());
+                    } else if (finalIntent.getComponent() != null) {
+                        finalIntent.getComponent().appendShortString(tag);
+                    } else if (finalIntent.getData() != null) {
+                        tag.append(finalIntent.getData());
+                    }
+                    owner.tempWhitelistForPendingIntentLocked(Binder.getCallingPid(),
+                            Binder.getCallingUid(), uid, whitelistDuration, tag.toString());
+                }
+
                 final long origId = Binder.clearCallingIdentity();
 
                 boolean sendFinish = finishedReceiver != null;
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/job/GrantedUriPermissions.java b/services/core/java/com/android/server/job/GrantedUriPermissions.java
new file mode 100644
index 0000000..e413d8d
--- /dev/null
+++ b/services/core/java/com/android/server/job/GrantedUriPermissions.java
@@ -0,0 +1,156 @@
+/*
+ * 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.job;
+
+import android.app.IActivityManager;
+import android.content.ClipData;
+import android.content.ContentProvider;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+public class GrantedUriPermissions {
+    private final int mGrantFlags;
+    private final int mSourceUserId;
+    private final String mTag;
+    private final IBinder mPermissionOwner;
+    private final ArrayList<Uri> mUris = new ArrayList<>();
+
+    private GrantedUriPermissions(IActivityManager am, int grantFlags, int uid, String tag)
+            throws RemoteException {
+        mGrantFlags = grantFlags;
+        mSourceUserId = UserHandle.getUserId(uid);
+        mTag = tag;
+        mPermissionOwner = am.newUriPermissionOwner("job: " + tag);
+    }
+
+    public void revoke(IActivityManager am) {
+        for (int i = mUris.size()-1; i >= 0; i--) {
+            try {
+                am.revokeUriPermissionFromOwner(mPermissionOwner, mUris.get(i),
+                        mGrantFlags, mSourceUserId);
+            } catch (RemoteException e) {
+            }
+        }
+        mUris.clear();
+    }
+
+    public static boolean checkGrantFlags(int grantFlags) {
+        return (grantFlags & (Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+                |Intent.FLAG_GRANT_READ_URI_PERMISSION)) != 0;
+    }
+
+    public static GrantedUriPermissions createFromIntent(IActivityManager am, Intent intent,
+            int sourceUid, String targetPackage, int targetUserId, String tag) {
+        int grantFlags = intent.getFlags();
+        if (!checkGrantFlags(grantFlags)) {
+            return null;
+        }
+
+        GrantedUriPermissions perms = null;
+
+        Uri data = intent.getData();
+        if (data != null) {
+            perms = grantUri(am, data, sourceUid, targetPackage, targetUserId, grantFlags, tag,
+                    perms);
+        }
+
+        ClipData clip = intent.getClipData();
+        if (clip != null) {
+            perms = grantClip(am, clip, sourceUid, targetPackage, targetUserId, grantFlags, tag,
+                    perms);
+        }
+
+        return perms;
+    }
+
+    public static GrantedUriPermissions createFromClip(IActivityManager am, ClipData clip,
+            int sourceUid, String targetPackage, int targetUserId, int grantFlags, String tag) {
+        if (!checkGrantFlags(grantFlags)) {
+            return null;
+        }
+        GrantedUriPermissions perms = null;
+        if (clip != null) {
+            perms = grantClip(am, clip, sourceUid, targetPackage, targetUserId, grantFlags,
+                    tag, perms);
+        }
+        return perms;
+    }
+
+    private static GrantedUriPermissions grantClip(IActivityManager am, ClipData clip,
+            int sourceUid, String targetPackage, int targetUserId, int grantFlags, String tag,
+            GrantedUriPermissions curPerms) {
+        final int N = clip.getItemCount();
+        for (int i = 0; i < N; i++) {
+            curPerms = grantItem(am, clip.getItemAt(i), sourceUid, targetPackage, targetUserId,
+                    grantFlags, tag, curPerms);
+        }
+        return curPerms;
+    }
+
+    private static GrantedUriPermissions grantUri(IActivityManager am, Uri uri,
+            int sourceUid, String targetPackage, int targetUserId, int grantFlags, String tag,
+            GrantedUriPermissions curPerms) {
+        try {
+            int sourceUserId = ContentProvider.getUserIdFromUri(uri,
+                    UserHandle.getUserId(sourceUid));
+            uri = ContentProvider.getUriWithoutUserId(uri);
+            if (curPerms == null) {
+                curPerms = new GrantedUriPermissions(am, grantFlags, sourceUid, tag);
+            }
+            am.grantUriPermissionFromOwner(curPerms.mPermissionOwner, sourceUid, targetPackage,
+                    uri, grantFlags, sourceUserId, targetUserId);
+            curPerms.mUris.add(uri);
+        } catch (RemoteException e) {
+            Slog.e("JobScheduler", "AM dead");
+        }
+        return curPerms;
+    }
+
+    private static GrantedUriPermissions grantItem(IActivityManager am, ClipData.Item item,
+            int sourceUid, String targetPackage, int targetUserId, int grantFlags, String tag,
+            GrantedUriPermissions curPerms) {
+        if (item.getUri() != null) {
+            curPerms = grantUri(am, item.getUri(), sourceUid, targetPackage, targetUserId,
+                    grantFlags, tag, curPerms);
+        }
+        Intent intent = item.getIntent();
+        if (intent != null && intent.getData() != null) {
+            curPerms = grantUri(am, intent.getData(), sourceUid, targetPackage, targetUserId,
+                    grantFlags, tag, curPerms);
+        }
+        return curPerms;
+    }
+
+    // Dumpsys infrastructure
+    public void dump(PrintWriter pw, String prefix) {
+        pw.print(prefix); pw.print("mGrantFlags=0x"); pw.print(Integer.toHexString(mGrantFlags));
+        pw.print(" mSourceUserId="); pw.println(mSourceUserId);
+        pw.print(prefix); pw.print("mTag="); pw.println(mTag);
+        pw.print(prefix); pw.print("mPermissionOwner="); pw.println(mPermissionOwner);
+        for (int i = 0; i < mUris.size(); i++) {
+            pw.print(prefix); pw.print("#"); pw.print(i); pw.print(": ");
+            pw.println(mUris.get(i));
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index d01de3c..c8bfa34 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -601,7 +601,7 @@
                 // Fast path: we are adding work to an existing job, and the JobInfo is not
                 // changing.  We can just directly enqueue this work in to the job.
                 if (toCancel.getJob().equals(job)) {
-                    toCancel.enqueueWorkLocked(work);
+                    toCancel.enqueueWorkLocked(ActivityManager.getService(), work);
                     return JobScheduler.RESULT_SUCCESS;
                 }
             }
@@ -625,7 +625,7 @@
             }
             if (work != null) {
                 // If work has been supplied, enqueue it into the new job.
-                jobStatus.enqueueWorkLocked(work);
+                jobStatus.enqueueWorkLocked(ActivityManager.getService(), work);
             }
             startTrackingJobLocked(jobStatus, toCancel);
             mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
@@ -758,7 +758,7 @@
                     final JobStatus executing = jsc.getRunningJob();
                     if (executing != null
                             && (executing.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0) {
-                        jsc.cancelExecutingJob(JobParameters.REASON_DEVICE_IDLE);
+                        jsc.cancelExecutingJobLocked(JobParameters.REASON_DEVICE_IDLE);
                     }
                 }
             } else {
@@ -921,7 +921,7 @@
     private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
             boolean writeBack) {
         // Deal with any remaining work items in the old job.
-        jobStatus.stopTrackingJobLocked(incomingJob);
+        jobStatus.stopTrackingJobLocked(ActivityManager.getService(), incomingJob);
 
         // Remove from store as well as controllers.
         final boolean removed = mJobs.remove(jobStatus, writeBack);
@@ -939,7 +939,7 @@
             JobServiceContext jsc = mActiveServices.get(i);
             final JobStatus executing = jsc.getRunningJob();
             if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
-                jsc.cancelExecutingJob(reason);
+                jsc.cancelExecutingJobLocked(reason);
                 return true;
             }
         }
@@ -1071,9 +1071,16 @@
         if (DEBUG) {
             Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
         }
+
+        // If the job wants to be rescheduled, we first need to make the next upcoming
+        // job so we can transfer any appropriate state over from the previous job when
+        // we stop it.
+        final JobStatus rescheduledJob = needsReschedule
+                ? getRescheduleJobForFailureLocked(jobStatus) : null;
+
         // Do not write back immediately if this is a periodic job. The job may get lost if system
         // shuts down before it is added back.
-        if (!stopTrackingJobLocked(jobStatus, null, !jobStatus.getJob().isPeriodic())) {
+        if (!stopTrackingJobLocked(jobStatus, rescheduledJob, !jobStatus.getJob().isPeriodic())) {
             if (DEBUG) {
                 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
             }
@@ -1082,18 +1089,14 @@
             mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
             return;
         }
-        // Note: there is a small window of time in here where, when rescheduling a job,
-        // we will stop monitoring its content providers.  This should be fixed by stopping
-        // the old job after scheduling the new one, but since we have no lock held here
-        // that may cause ordering problems if the app removes jobStatus while in here.
-        if (needsReschedule) {
-            JobStatus rescheduled = getRescheduleJobForFailureLocked(jobStatus);
+
+        if (rescheduledJob != null) {
             try {
-                rescheduled.prepareLocked(ActivityManager.getService());
+                rescheduledJob.prepareLocked(ActivityManager.getService());
             } catch (SecurityException e) {
-                Slog.w(TAG, "Unable to regrant job permissions for " + rescheduled);
+                Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledJob);
             }
-            startTrackingJobLocked(rescheduled, jobStatus);
+            startTrackingJobLocked(rescheduledJob, jobStatus);
         } else if (jobStatus.getJob().isPeriodic()) {
             JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
             try {
@@ -1561,7 +1564,7 @@
                         Slog.d(TAG, "preempting job: " + mActiveServices.get(i).getRunningJob());
                     }
                     // preferredUid will be set to uid of currently running job.
-                    mActiveServices.get(i).preemptExecutingJob();
+                    mActiveServices.get(i).preemptExecutingJobLocked();
                     preservePreferredUid = true;
                 } else {
                     final JobStatus pendingJob = contextIdToJobMap[i];
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index c7ef0e2..9144966 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -44,8 +44,6 @@
 import com.android.internal.app.IBatteryStats;
 import com.android.server.job.controllers.JobStatus;
 
-import java.util.concurrent.atomic.AtomicBoolean;
-
 /**
  * Handles client binding and lifecycle of a job. Jobs execute one at a time on an instance of this
  * class.
@@ -56,19 +54,15 @@
  * job lands, and again when it is complete.
  * - Cancelling is trickier, because there are also interactions from the client. It's possible
  * the {@link com.android.server.job.JobServiceContext.JobServiceHandler} tries to process a
- * {@link #MSG_CANCEL} after the client has already finished. This is handled by having
- * {@link com.android.server.job.JobServiceContext.JobServiceHandler#handleCancelH} check whether
+ * {@link #doCancelLocked(int)} after the client has already finished. This is handled by having
+ * {@link com.android.server.job.JobServiceContext.JobServiceHandler#handleCancelLocked} check whether
  * the context is still valid.
- * To mitigate this, tearing down the context removes all messages from the handler, including any
- * tardy {@link #MSG_CANCEL}s. Additionally, we avoid sending duplicate onStopJob()
+ * To mitigate this, we avoid sending duplicate onStopJob()
  * calls to the client after they've specified jobFinished().
  */
 public class JobServiceContext extends IJobCallback.Stub implements ServiceConnection {
     private static final boolean DEBUG = JobSchedulerService.DEBUG;
     private static final String TAG = "JobServiceContext";
-    /** Define the maximum # of jobs allowed to run on a service at once. */
-    private static final int defaultMaxActiveJobsPerService =
-            ActivityManager.isLowRamDeviceStatic() ? 1 : 3;
     /** Amount of time a job is allowed to execute for before being considered timed-out. */
     private static final long EXECUTING_TIMESLICE_MILLIS = 10 * 60 * 1000;  // 10mins.
     /** Amount of time the JobScheduler waits for the initial service launch+bind. */
@@ -90,14 +84,6 @@
     // Messages that result from interactions with the client service.
     /** System timed out waiting for a response. */
     private static final int MSG_TIMEOUT = 0;
-    /** Received a callback from client. */
-    private static final int MSG_CALLBACK = 1;
-    /** Run through list and start any ready jobs.*/
-    private static final int MSG_SERVICE_BOUND = 2;
-    /** Cancel a job. */
-    private static final int MSG_CANCEL = 3;
-    /** Shutdown the job. Used when the client crashes and we can't die gracefully.*/
-    private static final int MSG_SHUTDOWN_EXECUTION = 4;
 
     public static final int NO_PREFERRED_UID = -1;
 
@@ -115,7 +101,7 @@
     private JobParameters mParams;
     @VisibleForTesting
     int mVerb;
-    private AtomicBoolean mCancelled = new AtomicBoolean();
+    private boolean mCancelled;
 
     /**
      * All the information maintained about the job currently being executed.
@@ -245,14 +231,12 @@
     }
 
     /** Called externally when a job that was scheduled for execution should be cancelled. */
-    void cancelExecutingJob(int reason) {
-        mCallbackHandler.obtainMessage(MSG_CANCEL, reason, 0 /* unused */).sendToTarget();
+    void cancelExecutingJobLocked(int reason) {
+        doCancelLocked(reason);
     }
 
-    void preemptExecutingJob() {
-        Message m = mCallbackHandler.obtainMessage(MSG_CANCEL);
-        m.arg1 = JobParameters.REASON_PREEMPT;
-        m.sendToTarget();
+    void preemptExecutingJobLocked() {
+        doCancelLocked(JobParameters.REASON_PREEMPT);
     }
 
     int getPreferredUid() {
@@ -273,59 +257,54 @@
 
     @Override
     public void jobFinished(int jobId, boolean reschedule) {
-        if (!verifyCallingUid()) {
-            return;
-        }
-        mCallbackHandler.obtainMessage(MSG_CALLBACK, jobId, reschedule ? 1 : 0)
-                .sendToTarget();
+        doCallback(reschedule);
     }
 
     @Override
     public void acknowledgeStopMessage(int jobId, boolean reschedule) {
-        if (!verifyCallingUid()) {
-            return;
-        }
-        mCallbackHandler.obtainMessage(MSG_CALLBACK, jobId, reschedule ? 1 : 0)
-                .sendToTarget();
+        doCallback(reschedule);
     }
 
     @Override
     public void acknowledgeStartMessage(int jobId, boolean ongoing) {
-        if (!verifyCallingUid()) {
-            return;
-        }
-        mCallbackHandler.obtainMessage(MSG_CALLBACK, jobId, ongoing ? 1 : 0).sendToTarget();
+        doCallback(ongoing);
     }
 
     @Override
     public JobWorkItem dequeueWork(int jobId) {
-        if (!verifyCallingUid()) {
-            throw new SecurityException("Bad calling uid: " + Binder.getCallingUid());
-        }
-        JobWorkItem work = null;
-        boolean stillWorking = false;
-        synchronized (mLock) {
-            if (mRunningJob != null) {
-                work = mRunningJob.dequeueWorkLocked();
-                stillWorking = mRunningJob.hasExecutingWorkLocked();
+        final int callingUid = Binder.getCallingUid();
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                if (!verifyCallingUidLocked(callingUid)) {
+                    throw new SecurityException("Bad calling uid: " + callingUid);
+                }
+
+                final JobWorkItem work = mRunningJob.dequeueWorkLocked();
+                if (work == null && !mRunningJob.hasExecutingWorkLocked()) {
+                    // This will finish the job.
+                    doCallbackLocked(false);
+                }
+                return work;
             }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
-        if (work == null && !stillWorking) {
-            jobFinished(jobId, false);
-        }
-        return work;
     }
 
     @Override
     public boolean completeWork(int jobId, int workId) {
-        if (!verifyCallingUid()) {
-            throw new SecurityException("Bad calling uid: " + Binder.getCallingUid());
-        }
-        synchronized (mLock) {
-            if (mRunningJob != null) {
-                return mRunningJob.completeWorkLocked(workId);
+        final int callingUid = Binder.getCallingUid();
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                if (!verifyCallingUidLocked(callingUid)) {
+                    throw new SecurityException("Bad calling uid: " + callingUid);
+                }
+                return mRunningJob.completeWorkLocked(ActivityManager.getService(), workId);
             }
-            return false;
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
     }
 
@@ -344,20 +323,20 @@
             // looper and at this point we can't get any binder callbacks from the client. Better
             // safe than sorry.
             runningJob = mRunningJob;
-        }
-        if (runningJob == null || !name.equals(runningJob.getServiceComponent())) {
-            mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget();
-            return;
-        }
-        this.service = IJobService.Stub.asInterface(service);
-        final PowerManager pm =
-                (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
-        PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
-                runningJob.getTag());
-        wl.setWorkSource(new WorkSource(runningJob.getSourceUid()));
-        wl.setReferenceCounted(false);
-        wl.acquire();
-        synchronized (mLock) {
+
+            if (runningJob == null || !name.equals(runningJob.getServiceComponent())) {
+                closeAndCleanupJobLocked(true /* needsReschedule */);
+                return;
+            }
+            this.service = IJobService.Stub.asInterface(service);
+            final PowerManager pm =
+                    (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+            PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+                    runningJob.getTag());
+            wl.setWorkSource(new WorkSource(runningJob.getSourceUid()));
+            wl.setReferenceCounted(false);
+            wl.acquire();
+
             // We use a new wakelock instance per job.  In rare cases there is a race between
             // teardown following job completion/cancellation and new job service spin-up
             // such that if we simply assign mWakeLock to be the new instance, we orphan
@@ -369,14 +348,16 @@
                 mWakeLock.release();
             }
             mWakeLock = wl;
+            doServiceBoundLocked();
         }
-        mCallbackHandler.obtainMessage(MSG_SERVICE_BOUND).sendToTarget();
     }
 
     /** If the client service crashes we reschedule this job and clean up. */
     @Override
     public void onServiceDisconnected(ComponentName name) {
-        mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget();
+        synchronized (mLock) {
+            closeAndCleanupJobLocked(true /* needsReschedule */);
+        }
     }
 
     /**
@@ -384,22 +365,18 @@
      * whether the client exercising the callback is the client we expect.
      * @return True if the binder calling is coming from the client we expect.
      */
-    private boolean verifyCallingUid() {
-        synchronized (mLock) {
-            if (mRunningJob == null || Binder.getCallingUid() != mRunningJob.getUid()) {
-                if (DEBUG) {
-                    Slog.d(TAG, "Stale callback received, ignoring.");
-                }
-                return false;
+    private boolean verifyCallingUidLocked(final int callingUid) {
+        if (mRunningJob == null || callingUid != mRunningJob.getUid()) {
+            if (DEBUG) {
+                Slog.d(TAG, "Stale callback received, ignoring.");
             }
-            return true;
+            return false;
         }
+        return true;
     }
 
     /**
-     * Handles the lifecycle of the JobService binding/callbacks, etc. The convention within this
-     * class is to append 'H' to each function name that can only be called on this handler. This
-     * isn't strictly necessary because all of these functions are private, but helps clarity.
+     * Scheduling of async messages (basically timeouts at this point).
      */
     private class JobServiceHandler extends Handler {
         JobServiceHandler(Looper looper) {
@@ -409,296 +386,280 @@
         @Override
         public void handleMessage(Message message) {
             switch (message.what) {
-                case MSG_SERVICE_BOUND:
-                    doServiceBound();
-                    break;
-                case MSG_CALLBACK:
-                    doCallback(message.arg2);
-                    break;
-                case MSG_CANCEL:
-                    doCancel(message.arg1);
-                    break;
                 case MSG_TIMEOUT:
                     synchronized (mLock) {
-                        handleOpTimeoutH();
+                        handleOpTimeoutLocked();
                     }
                     break;
-                case MSG_SHUTDOWN_EXECUTION:
-                    closeAndCleanupJobH(true /* needsReschedule */);
-                    break;
                 default:
                     Slog.e(TAG, "Unrecognised message: " + message);
             }
         }
+    }
 
-        void doServiceBound() {
+    void doServiceBoundLocked() {
+        removeOpTimeOutLocked();
+        handleServiceBoundLocked();
+    }
+
+    void doCallback(boolean reschedule) {
+        final int callingUid = Binder.getCallingUid();
+        final long ident = Binder.clearCallingIdentity();
+        try {
             synchronized (mLock) {
-                removeOpTimeOutLocked();
-                handleServiceBoundH();
-            }
-        }
-
-        void doCallback(int arg2) {
-            synchronized (mLock) {
-                if (DEBUG) {
-                    Slog.d(TAG, "MSG_CALLBACK of : " + mRunningJob
-                            + " v:" + VERB_STRINGS[mVerb]);
-                }
-                removeOpTimeOutLocked();
-
-                if (mVerb == VERB_STARTING) {
-                    final boolean workOngoing = arg2 == 1;
-                    handleStartedH(workOngoing);
-                } else if (mVerb == VERB_EXECUTING ||
-                        mVerb == VERB_STOPPING) {
-                    final boolean reschedule = arg2 == 1;
-                    handleFinishedH(reschedule);
-                } else {
-                    if (DEBUG) {
-                        Slog.d(TAG, "Unrecognised callback: " + mRunningJob);
-                    }
-                }
-            }
-        }
-
-        void doCancel(int arg1) {
-            synchronized (mLock) {
-                if (mVerb == VERB_FINISHED) {
-                    if (DEBUG) {
-                        Slog.d(TAG,
-                                "Trying to process cancel for torn-down context, ignoring.");
-                    }
+                if (!verifyCallingUidLocked(callingUid)) {
                     return;
                 }
-                mParams.setStopReason(arg1);
-                if (arg1 == JobParameters.REASON_PREEMPT) {
-                    mPreferredUid = mRunningJob != null ? mRunningJob.getUid() :
-                            NO_PREFERRED_UID;
-                }
-                handleCancelH();
+                doCallbackLocked(reschedule);
             }
-
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
+    }
 
-        /** Start the job on the service. */
-        private void handleServiceBoundH() {
+    void doCallbackLocked(boolean reschedule) {
+        if (DEBUG) {
+            Slog.d(TAG, "doCallback of : " + mRunningJob
+                    + " v:" + VERB_STRINGS[mVerb]);
+        }
+        removeOpTimeOutLocked();
+
+        if (mVerb == VERB_STARTING) {
+            handleStartedLocked(reschedule);
+        } else if (mVerb == VERB_EXECUTING ||
+                mVerb == VERB_STOPPING) {
+            handleFinishedLocked(reschedule);
+        } else {
             if (DEBUG) {
-                Slog.d(TAG, "MSG_SERVICE_BOUND for " + mRunningJob.toShortString());
-            }
-            if (mVerb != VERB_BINDING) {
-                Slog.e(TAG, "Sending onStartJob for a job that isn't pending. "
-                        + VERB_STRINGS[mVerb]);
-                closeAndCleanupJobH(false /* reschedule */);
-                return;
-            }
-            if (mCancelled.get()) {
-                if (DEBUG) {
-                    Slog.d(TAG, "Job cancelled while waiting for bind to complete. "
-                            + mRunningJob);
-                }
-                closeAndCleanupJobH(true /* reschedule */);
-                return;
-            }
-            try {
-                mVerb = VERB_STARTING;
-                scheduleOpTimeOutLocked();
-                service.startJob(mParams);
-            } catch (Exception e) {
-                // We catch 'Exception' because client-app malice or bugs might induce a wide
-                // range of possible exception-throw outcomes from startJob() and its handling
-                // of the client's ParcelableBundle extras.
-                Slog.e(TAG, "Error sending onStart message to '" +
-                        mRunningJob.getServiceComponent().getShortClassName() + "' ", e);
+                Slog.d(TAG, "Unrecognised callback: " + mRunningJob);
             }
         }
+    }
 
-        /**
-         * State behaviours.
-         * VERB_STARTING   -> Successful start, change job to VERB_EXECUTING and post timeout.
-         *     _PENDING    -> Error
-         *     _EXECUTING  -> Error
-         *     _STOPPING   -> Error
-         */
-        private void handleStartedH(boolean workOngoing) {
-            switch (mVerb) {
-                case VERB_STARTING:
-                    mVerb = VERB_EXECUTING;
-                    if (!workOngoing) {
-                        // Job is finished already so fast-forward to handleFinished.
-                        handleFinishedH(false);
-                        return;
-                    }
-                    if (mCancelled.get()) {
-                        if (DEBUG) {
-                            Slog.d(TAG, "Job cancelled while waiting for onStartJob to complete.");
-                        }
-                        // Cancelled *while* waiting for acknowledgeStartMessage from client.
-                        handleCancelH();
-                        return;
-                    }
-                    scheduleOpTimeOutLocked();
-                    break;
-                default:
-                    Slog.e(TAG, "Handling started job but job wasn't starting! Was "
-                            + VERB_STRINGS[mVerb] + ".");
-                    return;
+    void doCancelLocked(int arg1) {
+        if (mVerb == VERB_FINISHED) {
+            if (DEBUG) {
+                Slog.d(TAG,
+                        "Trying to process cancel for torn-down context, ignoring.");
             }
+            return;
         }
-
-        /**
-         * VERB_EXECUTING  -> Client called jobFinished(), clean up and notify done.
-         *     _STOPPING   -> Successful finish, clean up and notify done.
-         *     _STARTING   -> Error
-         *     _PENDING    -> Error
-         */
-        private void handleFinishedH(boolean reschedule) {
-            switch (mVerb) {
-                case VERB_EXECUTING:
-                case VERB_STOPPING:
-                    closeAndCleanupJobH(reschedule);
-                    break;
-                default:
-                    Slog.e(TAG, "Got an execution complete message for a job that wasn't being" +
-                            "executed. Was " + VERB_STRINGS[mVerb] + ".");
-            }
+        mParams.setStopReason(arg1);
+        if (arg1 == JobParameters.REASON_PREEMPT) {
+            mPreferredUid = mRunningJob != null ? mRunningJob.getUid() :
+                    NO_PREFERRED_UID;
         }
+        handleCancelLocked();
+    }
 
-        /**
-         * A job can be in various states when a cancel request comes in:
-         * VERB_BINDING    -> Cancelled before bind completed. Mark as cancelled and wait for
-         *                    {@link #onServiceConnected(android.content.ComponentName, android.os.IBinder)}
-         *     _STARTING   -> Mark as cancelled and wait for
-         *                    {@link JobServiceContext#acknowledgeStartMessage(int, boolean)}
-         *     _EXECUTING  -> call {@link #sendStopMessageH}}, but only if there are no callbacks
-         *                      in the message queue.
-         *     _ENDING     -> No point in doing anything here, so we ignore.
-         */
-        private void handleCancelH() {
-            if (JobSchedulerService.DEBUG) {
-                Slog.d(TAG, "Handling cancel for: " + mRunningJob.getJobId() + " "
-                        + VERB_STRINGS[mVerb]);
-            }
-            switch (mVerb) {
-                case VERB_BINDING:
-                case VERB_STARTING:
-                    mCancelled.set(true);
-                    break;
-                case VERB_EXECUTING:
-                    if (hasMessages(MSG_CALLBACK)) {
-                        // If the client has called jobFinished, ignore this cancel.
-                        return;
-                    }
-                    sendStopMessageH();
-                    break;
-                case VERB_STOPPING:
-                    // Nada.
-                    break;
-                default:
-                    Slog.e(TAG, "Cancelling a job without a valid verb: " + mVerb);
-                    break;
-            }
+    /** Start the job on the service. */
+    private void handleServiceBoundLocked() {
+        if (DEBUG) {
+            Slog.d(TAG, "handleServiceBound for " + mRunningJob.toShortString());
         }
-
-        /** Process MSG_TIMEOUT here. */
-        private void handleOpTimeoutH() {
-            switch (mVerb) {
-                case VERB_BINDING:
-                    Slog.e(TAG, "Time-out while trying to bind " + mRunningJob.toShortString() +
-                            ", dropping.");
-                    closeAndCleanupJobH(false /* needsReschedule */);
-                    break;
-                case VERB_STARTING:
-                    // Client unresponsive - wedged or failed to respond in time. We don't really
-                    // know what happened so let's log it and notify the JobScheduler
-                    // FINISHED/NO-RETRY.
-                    Slog.e(TAG, "No response from client for onStartJob '" +
-                            mRunningJob.toShortString());
-                    closeAndCleanupJobH(false /* needsReschedule */);
-                    break;
-                case VERB_STOPPING:
-                    // At least we got somewhere, so fail but ask the JobScheduler to reschedule.
-                    Slog.e(TAG, "No response from client for onStopJob, '" +
-                            mRunningJob.toShortString());
-                    closeAndCleanupJobH(true /* needsReschedule */);
-                    break;
-                case VERB_EXECUTING:
-                    // Not an error - client ran out of time.
-                    Slog.i(TAG, "Client timed out while executing (no jobFinished received)." +
-                            " sending onStop. "  + mRunningJob.toShortString());
-                    mParams.setStopReason(JobParameters.REASON_TIMEOUT);
-                    sendStopMessageH();
-                    break;
-                default:
-                    Slog.e(TAG, "Handling timeout for an invalid job state: " +
-                            mRunningJob.toShortString() + ", dropping.");
-                    closeAndCleanupJobH(false /* needsReschedule */);
-            }
+        if (mVerb != VERB_BINDING) {
+            Slog.e(TAG, "Sending onStartJob for a job that isn't pending. "
+                    + VERB_STRINGS[mVerb]);
+            closeAndCleanupJobLocked(false /* reschedule */);
+            return;
         }
-
-        /**
-         * Already running, need to stop. Will switch {@link #mVerb} from VERB_EXECUTING ->
-         * VERB_STOPPING.
-         */
-        private void sendStopMessageH() {
-            removeOpTimeOutLocked();
-            if (mVerb != VERB_EXECUTING) {
-                Slog.e(TAG, "Sending onStopJob for a job that isn't started. " + mRunningJob);
-                closeAndCleanupJobH(false /* reschedule */);
-                return;
+        if (mCancelled) {
+            if (DEBUG) {
+                Slog.d(TAG, "Job cancelled while waiting for bind to complete. "
+                        + mRunningJob);
             }
-            try {
-                mVerb = VERB_STOPPING;
-                scheduleOpTimeOutLocked();
-                service.stopJob(mParams);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Error sending onStopJob to client.", e);
-                // The job's host app apparently crashed during the job, so we should reschedule.
-                closeAndCleanupJobH(true /* reschedule */);
-            }
+            closeAndCleanupJobLocked(true /* reschedule */);
+            return;
         }
+        try {
+            mVerb = VERB_STARTING;
+            scheduleOpTimeOutLocked();
+            service.startJob(mParams);
+        } catch (Exception e) {
+            // We catch 'Exception' because client-app malice or bugs might induce a wide
+            // range of possible exception-throw outcomes from startJob() and its handling
+            // of the client's ParcelableBundle extras.
+            Slog.e(TAG, "Error sending onStart message to '" +
+                    mRunningJob.getServiceComponent().getShortClassName() + "' ", e);
+        }
+    }
 
-        /**
-         * The provided job has finished, either by calling
-         * {@link android.app.job.JobService#jobFinished(android.app.job.JobParameters, boolean)}
-         * or from acknowledging the stop message we sent. Either way, we're done tracking it and
-         * we want to clean up internally.
-         */
-        private void closeAndCleanupJobH(boolean reschedule) {
-            final JobStatus completedJob;
-            synchronized (mLock) {
-                if (mVerb == VERB_FINISHED) {
+    /**
+     * State behaviours.
+     * VERB_STARTING   -> Successful start, change job to VERB_EXECUTING and post timeout.
+     *     _PENDING    -> Error
+     *     _EXECUTING  -> Error
+     *     _STOPPING   -> Error
+     */
+    private void handleStartedLocked(boolean workOngoing) {
+        switch (mVerb) {
+            case VERB_STARTING:
+                mVerb = VERB_EXECUTING;
+                if (!workOngoing) {
+                    // Job is finished already so fast-forward to handleFinished.
+                    handleFinishedLocked(false);
                     return;
                 }
-                completedJob = mRunningJob;
-                mJobPackageTracker.noteInactive(completedJob);
-                try {
-                    mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(),
-                            mRunningJob.getSourceUid());
-                } catch (RemoteException e) {
-                    // Whatever.
+                if (mCancelled) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Job cancelled while waiting for onStartJob to complete.");
+                    }
+                    // Cancelled *while* waiting for acknowledgeStartMessage from client.
+                    handleCancelLocked();
+                    return;
                 }
-                if (mWakeLock != null) {
-                    mWakeLock.release();
-                }
-                mContext.unbindService(JobServiceContext.this);
-                mWakeLock = null;
-                mRunningJob = null;
-                mParams = null;
-                mVerb = VERB_FINISHED;
-                mCancelled.set(false);
-                service = null;
-                mAvailable = true;
-                removeOpTimeOutLocked();
-                removeMessages(MSG_CALLBACK);
-                removeMessages(MSG_SERVICE_BOUND);
-                removeMessages(MSG_CANCEL);
-                removeMessages(MSG_SHUTDOWN_EXECUTION);
-                mCompletedListener.onJobCompletedLocked(completedJob, reschedule);
-            }
+                scheduleOpTimeOutLocked();
+                break;
+            default:
+                Slog.e(TAG, "Handling started job but job wasn't starting! Was "
+                        + VERB_STRINGS[mVerb] + ".");
+                return;
         }
     }
 
     /**
+     * VERB_EXECUTING  -> Client called jobFinished(), clean up and notify done.
+     *     _STOPPING   -> Successful finish, clean up and notify done.
+     *     _STARTING   -> Error
+     *     _PENDING    -> Error
+     */
+    private void handleFinishedLocked(boolean reschedule) {
+        switch (mVerb) {
+            case VERB_EXECUTING:
+            case VERB_STOPPING:
+                closeAndCleanupJobLocked(reschedule);
+                break;
+            default:
+                Slog.e(TAG, "Got an execution complete message for a job that wasn't being" +
+                        "executed. Was " + VERB_STRINGS[mVerb] + ".");
+        }
+    }
+
+    /**
+     * A job can be in various states when a cancel request comes in:
+     * VERB_BINDING    -> Cancelled before bind completed. Mark as cancelled and wait for
+     *                    {@link #onServiceConnected(android.content.ComponentName, android.os.IBinder)}
+     *     _STARTING   -> Mark as cancelled and wait for
+     *                    {@link JobServiceContext#acknowledgeStartMessage(int, boolean)}
+     *     _EXECUTING  -> call {@link #sendStopMessageLocked}}, but only if there are no callbacks
+     *                      in the message queue.
+     *     _ENDING     -> No point in doing anything here, so we ignore.
+     */
+    private void handleCancelLocked() {
+        if (JobSchedulerService.DEBUG) {
+            Slog.d(TAG, "Handling cancel for: " + mRunningJob.getJobId() + " "
+                    + VERB_STRINGS[mVerb]);
+        }
+        switch (mVerb) {
+            case VERB_BINDING:
+            case VERB_STARTING:
+                mCancelled = true;
+                break;
+            case VERB_EXECUTING:
+                sendStopMessageLocked();
+                break;
+            case VERB_STOPPING:
+                // Nada.
+                break;
+            default:
+                Slog.e(TAG, "Cancelling a job without a valid verb: " + mVerb);
+                break;
+        }
+    }
+
+    /** Process MSG_TIMEOUT here. */
+    private void handleOpTimeoutLocked() {
+        switch (mVerb) {
+            case VERB_BINDING:
+                Slog.e(TAG, "Time-out while trying to bind " + mRunningJob.toShortString() +
+                        ", dropping.");
+                closeAndCleanupJobLocked(false /* needsReschedule */);
+                break;
+            case VERB_STARTING:
+                // Client unresponsive - wedged or failed to respond in time. We don't really
+                // know what happened so let's log it and notify the JobScheduler
+                // FINISHED/NO-RETRY.
+                Slog.e(TAG, "No response from client for onStartJob '" +
+                        mRunningJob.toShortString());
+                closeAndCleanupJobLocked(false /* needsReschedule */);
+                break;
+            case VERB_STOPPING:
+                // At least we got somewhere, so fail but ask the JobScheduler to reschedule.
+                Slog.e(TAG, "No response from client for onStopJob, '" +
+                        mRunningJob.toShortString());
+                closeAndCleanupJobLocked(true /* needsReschedule */);
+                break;
+            case VERB_EXECUTING:
+                // Not an error - client ran out of time.
+                Slog.i(TAG, "Client timed out while executing (no jobFinished received)." +
+                        " sending onStop. "  + mRunningJob.toShortString());
+                mParams.setStopReason(JobParameters.REASON_TIMEOUT);
+                sendStopMessageLocked();
+                break;
+            default:
+                Slog.e(TAG, "Handling timeout for an invalid job state: " +
+                        mRunningJob.toShortString() + ", dropping.");
+                closeAndCleanupJobLocked(false /* needsReschedule */);
+        }
+    }
+
+    /**
+     * Already running, need to stop. Will switch {@link #mVerb} from VERB_EXECUTING ->
+     * VERB_STOPPING.
+     */
+    private void sendStopMessageLocked() {
+        removeOpTimeOutLocked();
+        if (mVerb != VERB_EXECUTING) {
+            Slog.e(TAG, "Sending onStopJob for a job that isn't started. " + mRunningJob);
+            closeAndCleanupJobLocked(false /* reschedule */);
+            return;
+        }
+        try {
+            mVerb = VERB_STOPPING;
+            scheduleOpTimeOutLocked();
+            service.stopJob(mParams);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Error sending onStopJob to client.", e);
+            // The job's host app apparently crashed during the job, so we should reschedule.
+            closeAndCleanupJobLocked(true /* reschedule */);
+        }
+    }
+
+    /**
+     * The provided job has finished, either by calling
+     * {@link android.app.job.JobService#jobFinished(android.app.job.JobParameters, boolean)}
+     * or from acknowledging the stop message we sent. Either way, we're done tracking it and
+     * we want to clean up internally.
+     */
+    private void closeAndCleanupJobLocked(boolean reschedule) {
+        final JobStatus completedJob;
+        if (mVerb == VERB_FINISHED) {
+            return;
+        }
+        completedJob = mRunningJob;
+        mJobPackageTracker.noteInactive(completedJob);
+        try {
+            mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(),
+                    mRunningJob.getSourceUid());
+        } catch (RemoteException e) {
+            // Whatever.
+        }
+        if (mWakeLock != null) {
+            mWakeLock.release();
+        }
+        mContext.unbindService(JobServiceContext.this);
+        mWakeLock = null;
+        mRunningJob = null;
+        mParams = null;
+        mVerb = VERB_FINISHED;
+        mCancelled = false;
+        service = null;
+        mAvailable = true;
+        removeOpTimeOutLocked();
+        mCompletedListener.onJobCompletedLocked(completedJob, reschedule);
+    }
+
+    /**
      * Called when sending a message to the client, over whose execution we have no control. If
      * we haven't received a response in a certain amount of time, we want to give up and carry
      * on with life.
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index e8cc078..1ab66b9 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -26,7 +26,6 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Binder;
-import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -35,6 +34,8 @@
 import android.util.Slog;
 import android.util.TimeUtils;
 
+import com.android.server.job.GrantedUriPermissions;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -103,7 +104,7 @@
 
     final String tag;
 
-    private IBinder permissionOwner;
+    private GrantedUriPermissions uriPerms;
     private boolean prepared;
 
     /**
@@ -284,12 +285,17 @@
                 earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis);
     }
 
-    public void enqueueWorkLocked(JobWorkItem work) {
+    public void enqueueWorkLocked(IActivityManager am, JobWorkItem work) {
         if (pendingWork == null) {
             pendingWork = new ArrayList<>();
         }
         work.setWorkId(nextPendingWorkId);
         nextPendingWorkId++;
+        if (work.getIntent() != null
+                && GrantedUriPermissions.checkGrantFlags(work.getIntent().getFlags())) {
+            work.setGrants(GrantedUriPermissions.createFromIntent(am, work.getIntent(), sourceUid,
+                    sourcePackageName, sourceUserId, toShortString()));
+        }
         pendingWork.add(work);
     }
 
@@ -311,12 +317,20 @@
         return executingWork != null && executingWork.size() > 0;
     }
 
-    public boolean completeWorkLocked(int workId) {
+    private static void ungrantWorkItem(IActivityManager am, JobWorkItem work) {
+        if (work.getGrants() != null) {
+            ((GrantedUriPermissions)work.getGrants()).revoke(am);
+        }
+    }
+
+    public boolean completeWorkLocked(IActivityManager am, int workId) {
         if (executingWork != null) {
             final int N = executingWork.size();
             for (int i = 0; i < N; i++) {
-                if (executingWork.get(i).getWorkId() == workId) {
+                JobWorkItem work = executingWork.get(i);
+                if (work.getWorkId() == workId) {
                     executingWork.remove(i);
+                    ungrantWorkItem(am, work);
                     return true;
                 }
             }
@@ -324,15 +338,36 @@
         return false;
     }
 
-    public void stopTrackingJobLocked(JobStatus incomingJob) {
+    private static void ungrantWorkList(IActivityManager am, ArrayList<JobWorkItem> list) {
+        if (list != null) {
+            final int N = list.size();
+            for (int i = 0; i < N; i++) {
+                ungrantWorkItem(am, list.get(i));
+            }
+        }
+    }
+
+    public void stopTrackingJobLocked(IActivityManager am, JobStatus incomingJob) {
         if (incomingJob != null) {
-            // We are replacing with a new job -- transfer the work!
-            incomingJob.pendingWork = pendingWork;
+            // We are replacing with a new job -- transfer the work!  We do any executing
+            // work first, since that was originally at the front of the pending work.
+            if (executingWork != null && executingWork.size() > 0) {
+                incomingJob.pendingWork = executingWork;
+            }
+            if (incomingJob.pendingWork == null) {
+                incomingJob.pendingWork = pendingWork;
+            } else if (pendingWork != null && pendingWork.size() > 0) {
+                incomingJob.pendingWork.addAll(pendingWork);
+            }
             pendingWork = null;
+            executingWork = null;
             incomingJob.nextPendingWorkId = nextPendingWorkId;
         } else {
             // We are completely stopping the job...  need to clean up work.
-            // XXX remove perms when that is impl.
+            ungrantWorkList(am, pendingWork);
+            pendingWork = null;
+            ungrantWorkList(am, executingWork);
+            executingWork = null;
         }
     }
 
@@ -344,10 +379,8 @@
         prepared = true;
         final ClipData clip = job.getClipData();
         if (clip != null) {
-            final int N = clip.getItemCount();
-            for (int i = 0; i < N; i++) {
-                grantItemLocked(am, clip.getItemAt(i), sourceUid, sourcePackageName, sourceUserId);
-            }
+            uriPerms = GrantedUriPermissions.createFromClip(am, clip, sourceUid, sourcePackageName,
+                    sourceUserId, job.getClipGrantFlags(), toShortString());
         }
     }
 
@@ -357,14 +390,9 @@
             return;
         }
         prepared = false;
-        if (permissionOwner != null) {
-            final ClipData clip = job.getClipData();
-            if (clip != null) {
-                final int N = clip.getItemCount();
-                for (int i = 0; i < N; i++) {
-                    revokeItemLocked(am, clip.getItemAt(i));
-                }
-            }
+        if (uriPerms != null) {
+            uriPerms.revoke(am);
+            uriPerms = null;
         }
     }
 
@@ -372,57 +400,6 @@
         return prepared;
     }
 
-    private final void grantUriLocked(IActivityManager am, Uri uri, int sourceUid,
-            String targetPackage, int targetUserId) {
-        try {
-            int sourceUserId = ContentProvider.getUserIdFromUri(uri,
-                    UserHandle.getUserId(sourceUid));
-            uri = ContentProvider.getUriWithoutUserId(uri);
-            if (permissionOwner == null) {
-                permissionOwner = am.newUriPermissionOwner("job: " + toShortString());
-            }
-            am.grantUriPermissionFromOwner(permissionOwner, sourceUid, targetPackage,
-                    uri, job.getClipGrantFlags(), sourceUserId, targetUserId);
-        } catch (RemoteException e) {
-            Slog.e("JobScheduler", "AM dead");
-        }
-    }
-
-    private final void grantItemLocked(IActivityManager am, ClipData.Item item, int sourceUid,
-            String targetPackage, int targetUserId) {
-        if (item.getUri() != null) {
-            grantUriLocked(am, item.getUri(), sourceUid, targetPackage, targetUserId);
-        }
-        Intent intent = item.getIntent();
-        if (intent != null && intent.getData() != null) {
-            grantUriLocked(am, intent.getData(), sourceUid, targetPackage, targetUserId);
-        }
-    }
-
-    private final void revokeUriLocked(IActivityManager am, Uri uri) {
-        int userId = ContentProvider.getUserIdFromUri(uri,
-                UserHandle.getUserId(Binder.getCallingUid()));
-        long ident = Binder.clearCallingIdentity();
-        try {
-            uri = ContentProvider.getUriWithoutUserId(uri);
-            am.revokeUriPermissionFromOwner(permissionOwner, uri,
-                    job.getClipGrantFlags(), userId);
-        } catch (RemoteException e) {
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    private final void revokeItemLocked(IActivityManager am, ClipData.Item item) {
-        if (item.getUri() != null) {
-            revokeUriLocked(am, item.getUri());
-        }
-        Intent intent = item.getIntent();
-        if (intent != null && intent.getData() != null) {
-            revokeUriLocked(am, intent.getData());
-        }
-    }
-
     public JobInfo getJob() {
         return job;
     }
@@ -833,6 +810,15 @@
         }
     }
 
+    private void dumpJobWorkItem(PrintWriter pw, String prefix, JobWorkItem work, int index) {
+        pw.print(prefix); pw.print("  #"); pw.print(index); pw.print(": #");
+        pw.print(work.getWorkId()); pw.print(" "); pw.println(work.getIntent());
+        if (work.getGrants() != null) {
+            pw.print(prefix); pw.println("  URI grants:");
+            ((GrantedUriPermissions)work.getGrants()).dump(pw, prefix + "    ");
+        }
+    }
+
     // Dumpsys infrastructure
     public void dump(PrintWriter pw, String prefix, boolean full) {
         pw.print(prefix); UserHandle.formatUid(pw, callingUid);
@@ -898,6 +884,10 @@
                 job.getClipData().toShortString(b);
                 pw.println(b);
             }
+            if (uriPerms != null) {
+                pw.print(prefix); pw.println("  Granted URI permissions:");
+                uriPerms.dump(pw, prefix + "  ");
+            }
             if (job.getNetworkType() != JobInfo.NETWORK_TYPE_NONE) {
                 pw.print(prefix); pw.print("  Network type: "); pw.println(job.getNetworkType());
             }
@@ -950,17 +940,13 @@
         if (pendingWork != null && pendingWork.size() > 0) {
             pw.print(prefix); pw.println("Pending work:");
             for (int i = 0; i < pendingWork.size(); i++) {
-                JobWorkItem work = pendingWork.get(i);
-                pw.print(prefix); pw.print("  #"); pw.print(i); pw.print(": #");
-                pw.print(work.getWorkId()); pw.print(" "); pw.println(work.getIntent());
+                dumpJobWorkItem(pw, prefix, pendingWork.get(i), i);
             }
         }
         if (executingWork != null && executingWork.size() > 0) {
             pw.print(prefix); pw.println("Executing work:");
             for (int i = 0; i < executingWork.size(); i++) {
-                JobWorkItem work = executingWork.get(i);
-                pw.print(prefix); pw.print("  #"); pw.print(i); pw.print(": #");
-                pw.print(work.getWorkId()); pw.print(" "); pw.println(work.getIntent());
+                dumpJobWorkItem(pw, prefix, executingWork.get(i), i);
             }
         }
         pw.print(prefix); pw.print("Earliest run time: ");
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 53a8092..ee348cf 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -101,8 +101,6 @@
     private ParceledListSlice mQueue;
     private CharSequence mQueueTitle;
     private int mRatingType;
-    private int mRepeatMode;
-    private boolean mShuffleModeEnabled;
     // End TransportPerformer fields
 
     // Volume handling fields
@@ -622,47 +620,6 @@
         }
     }
 
-    private void pushRepeatModeUpdate() {
-        synchronized (mLock) {
-            if (mDestroyed) {
-                return;
-            }
-            for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
-                ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
-                try {
-                    holder.mCallback.onRepeatModeChanged(mRepeatMode);
-                } catch (DeadObjectException e) {
-                    mControllerCallbackHolders.remove(i);
-                    logCallbackException("Removed dead callback in pushRepeatModeUpdate",
-                            holder, e);
-                } catch (RemoteException e) {
-                    logCallbackException("unexpected exception in pushRepeatModeUpdate", holder, e);
-                }
-            }
-        }
-    }
-
-    private void pushShuffleModeUpdate() {
-        synchronized (mLock) {
-            if (mDestroyed) {
-                return;
-            }
-            for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
-                ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
-                try {
-                    holder.mCallback.onShuffleModeChanged(mShuffleModeEnabled);
-                } catch (DeadObjectException e) {
-                    mControllerCallbackHolders.remove(i);
-                    logCallbackException("Removed dead callback in pushShuffleModeUpdate",
-                            holder, e);
-                } catch (RemoteException e) {
-                    logCallbackException("unexpected exception in pushShuffleModeUpdate",
-                            holder, e);
-                }
-            }
-        }
-    }
-
     private void pushSessionDestroyed() {
         synchronized (mLock) {
             // This is the only method that may be (and can only be) called
@@ -887,30 +844,6 @@
         }
 
         @Override
-        public void setRepeatMode(int repeatMode) {
-            boolean changed;
-            synchronized (mLock) {
-                changed = mRepeatMode != repeatMode;
-                mRepeatMode = repeatMode;
-            }
-            if (changed) {
-                mHandler.post(MessageHandler.MSG_UPDATE_REPEAT_MODE);
-            }
-        }
-
-        @Override
-        public void setShuffleModeEnabled(boolean enabled) {
-            boolean changed;
-            synchronized (mLock) {
-                changed = mShuffleModeEnabled != enabled;
-                mShuffleModeEnabled = enabled;
-            }
-            if (changed) {
-                mHandler.post(MessageHandler.MSG_UPDATE_SHUFFLE_MODE);
-            }
-        }
-
-        @Override
         public void setCurrentVolume(int volume) {
             mCurrentVolume = volume;
             mHandler.post(MessageHandler.MSG_UPDATE_VOLUME);
@@ -1126,54 +1059,6 @@
             }
         }
 
-        public void repeatMode(int repeatMode) {
-            try {
-                mCb.onRepeatMode(repeatMode);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Remote failure in repeatMode.", e);
-            }
-        }
-
-        public void shuffleMode(boolean enabled) {
-            try {
-                mCb.onShuffleMode(enabled);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Remote failure in shuffleMode.", e);
-            }
-        }
-
-        public void addQueueItem(MediaDescription description) {
-            try {
-                mCb.onAddQueueItem(description);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Remote failure in addQueueItem.", e);
-            }
-        }
-
-        public void addQueueItemAt(MediaDescription description, int index) {
-            try {
-                mCb.onAddQueueItemAt(description, index);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Remote failure in addQueueItemAt.", e);
-            }
-        }
-
-        public void removeQueueItem(MediaDescription description) {
-            try {
-                mCb.onRemoveQueueItem(description);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Remote failure in removeQueueItem.", e);
-            }
-        }
-
-        public void removeQueueItemAt(int index) {
-            try {
-                mCb.onRemoveQueueItemAt(index);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Remote failure in removeQueueItem.", e);
-            }
-        }
-
         public void adjustVolume(int direction) {
             try {
                 mCb.onAdjustVolume(direction);
@@ -1410,25 +1295,13 @@
         }
 
         @Override
-        public void repeatMode(int repeatMode) {
-            updateCallingPackage();
-            mSessionCb.repeatMode(repeatMode);
-        }
-
-        @Override
-        public void shuffleMode(boolean enabled) throws RemoteException {
-            updateCallingPackage();
-            mSessionCb.shuffleMode(enabled);
-        }
-
-
-        @Override
         public void sendCustomAction(String action, Bundle args)
                 throws RemoteException {
             updateCallingPackage();
             mSessionCb.sendCustomAction(action, args);
         }
 
+
         @Override
         public MediaMetadata getMetadata() {
             synchronized (mLock) {
@@ -1449,30 +1322,6 @@
         }
 
         @Override
-        public void addQueueItem(MediaDescription description) {
-            updateCallingPackage();
-            mSessionCb.addQueueItem(description);
-        }
-
-        @Override
-        public void addQueueItemAt(MediaDescription description, int index) {
-            updateCallingPackage();
-            mSessionCb.addQueueItemAt(description, index);
-        }
-
-        @Override
-        public void removeQueueItem(MediaDescription description) {
-            updateCallingPackage();
-            mSessionCb.removeQueueItem(description);
-        }
-
-        @Override
-        public void removeQueueItemAt(int index) {
-            updateCallingPackage();
-            mSessionCb.removeQueueItemAt(index);
-        }
-
-        @Override
         public CharSequence getQueueTitle() {
             return mQueueTitle;
         }
@@ -1490,16 +1339,6 @@
         }
 
         @Override
-        public int getRepeatMode() {
-            return mRepeatMode;
-        }
-
-        @Override
-        public boolean isShuffleModeEnabled() {
-            return mShuffleModeEnabled;
-        }
-
-        @Override
         public boolean isTransportControlEnabled() {
             return MediaSessionRecord.this.isTransportControlEnabled();
         }
@@ -1524,9 +1363,7 @@
         private static final int MSG_SEND_EVENT = 6;
         private static final int MSG_UPDATE_SESSION_STATE = 7;
         private static final int MSG_UPDATE_VOLUME = 8;
-        private static final int MSG_UPDATE_REPEAT_MODE = 9;
-        private static final int MSG_UPDATE_SHUFFLE_MODE = 10;
-        private static final int MSG_DESTROYED = 11;
+        private static final int MSG_DESTROYED = 9;
 
         public MessageHandler(Looper looper) {
             super(looper);
@@ -1558,12 +1395,6 @@
                 case MSG_UPDATE_VOLUME:
                     pushVolumeUpdate();
                     break;
-                case MSG_UPDATE_REPEAT_MODE:
-                    pushRepeatModeUpdate();
-                    break;
-                case MSG_UPDATE_SHUFFLE_MODE:
-                    pushShuffleModeUpdate();
-                    break;
                 case MSG_DESTROYED:
                     pushSessionDestroyed();
             }
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/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index cc3948e..8a6a940 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);
                     }
                 }
             });
@@ -3229,8 +3240,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);
+            }
         }
     }
 
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/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 95b4903..312c310 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -13202,7 +13202,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) {
             }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 4f29bfa..bcb4121 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -222,9 +222,10 @@
 import android.view.animation.Animation;
 import android.view.animation.AnimationSet;
 import android.view.animation.AnimationUtils;
+import android.view.inputmethod.InputMethodManagerInternal;
 import android.widget.ImageView;
-
 import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.internal.policy.IShortcutService;
@@ -279,6 +280,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 +409,7 @@
     PowerManager mPowerManager;
     ActivityManagerInternal mActivityManagerInternal;
     InputManagerInternal mInputManagerInternal;
+    InputMethodManagerInternal mInputMethodManagerInternal;
     DreamManagerInternal mDreamManagerInternal;
     PowerManagerInternal mPowerManagerInternal;
     IStatusBarService mStatusBarService;
@@ -494,6 +497,9 @@
     WindowState mLastInputMethodWindow = null;
     WindowState mLastInputMethodTargetWindow = null;
 
+    @GuardedBy("mLock")
+    private boolean mDismissImeOnBackKeyPressed;
+
     // FIXME This state is shared between the input reader and handler thread.
     // Technically it's broken and buggy but it has been like this for many years
     // and we have not yet seen any problems.  Someday we'll rewrite this logic
@@ -1396,6 +1402,27 @@
                 case SHORT_PRESS_POWER_GO_HOME:
                     launchHomeFromHotKey(true /* awakenFromDreams */, false /*respectKeyguard*/);
                     break;
+                case SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME: {
+                    final boolean dismissImeOnBackKeyPressed;
+                    // We can be here on both the main thread (via mHandler) and native callback
+                    // thread (from interceptPowerKeyUp via WindowManagerCallbacks).
+                    synchronized (mLock) {
+                        dismissImeOnBackKeyPressed = mDismissImeOnBackKeyPressed;
+                    }
+                    if (dismissImeOnBackKeyPressed) {
+                        if (mInputMethodManagerInternal == null) {
+                            mInputMethodManagerInternal =
+                                    LocalServices.getService(InputMethodManagerInternal.class);
+                        }
+                        if (mInputMethodManagerInternal != null) {
+                            mInputMethodManagerInternal.hideCurrentInputMethod();
+                        }
+                    } else {
+                        launchHomeFromHotKey(true /* awakenFromDreams */,
+                                false /*respectKeyguard*/);
+                    }
+                    break;
+                }
             }
         }
     }
@@ -7955,6 +7982,13 @@
     }
 
     @Override
+    public void setDismissImeOnBackKeyPressed(boolean newValue) {
+        synchronized (mLock) {
+            mDismissImeOnBackKeyPressed = newValue;
+        }
+    }
+
+    @Override
     public int getInputMethodWindowVisibleHeightLw() {
         return mDockBottom - mCurBottom;
     }
@@ -8170,6 +8204,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/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index 1684878..82416ec 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -26,7 +26,6 @@
 import android.content.pm.ParceledListSlice;
 import android.content.res.Resources;
 import android.graphics.Point;
-import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Handler;
 import android.os.IBinder;
@@ -123,6 +122,13 @@
                 mSnapAlgorithm.setMinimized(isMinimized);
             });
         }
+
+        @Override
+        public int getDisplayRotation() {
+            synchronized (mService.mWindowMap) {
+                return mDisplayInfo.rotation;
+            }
+        }
     }
 
     /**
@@ -221,22 +227,26 @@
      * @return the size of the PIP based on the given {@param aspectRatio}.
      */
     Size getSize(float aspectRatio) {
-        return mSnapAlgorithm.getSizeForAspectRatio(aspectRatio, mMinSize,
-                mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
+        synchronized (mService.mWindowMap) {
+            return mSnapAlgorithm.getSizeForAspectRatio(aspectRatio, mMinSize,
+                    mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
+        }
     }
 
     /**
      * @return the default bounds to show the PIP when there is no active PIP.
      */
     Rect getDefaultBounds() {
-        final Rect insetBounds = new Rect();
-        getInsetBounds(insetBounds);
+        synchronized (mService.mWindowMap) {
+            final Rect insetBounds = new Rect();
+            getInsetBounds(insetBounds);
 
-        final Rect defaultBounds = new Rect();
-        final Size size = getSize(mDefaultAspectRatio);
-        Gravity.apply(mDefaultStackGravity, size.getWidth(), size.getHeight(), insetBounds,
-                0, mIsImeShowing ? mImeHeight : 0, defaultBounds);
-        return defaultBounds;
+            final Rect defaultBounds = new Rect();
+            final Size size = getSize(mDefaultAspectRatio);
+            Gravity.apply(mDefaultStackGravity, size.getWidth(), size.getHeight(), insetBounds,
+                    0, mIsImeShowing ? mImeHeight : 0, defaultBounds);
+            return defaultBounds;
+        }
     }
 
     /**
@@ -254,42 +264,44 @@
      * new orientation of the device if necessary.
      */
     boolean onTaskStackBoundsChanged(Rect targetBounds, Rect outBounds) {
-        final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
-        if (mDisplayInfo.equals(displayInfo)) {
-            // We are already in the right orientation, ignore
-            outBounds.setEmpty();
-            return false;
-        } else if (targetBounds.isEmpty()) {
-            // The stack is null, we are just initializing the stack, so just store the display info
-            // and ignore
+        synchronized (mService.mWindowMap) {
+            final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
+            if (mDisplayInfo.equals(displayInfo)) {
+                // We are already in the right orientation, ignore
+                outBounds.setEmpty();
+                return false;
+            } else if (targetBounds.isEmpty()) {
+                // The stack is null, we are just initializing the stack, so just store the display
+                // info and ignore
+                mDisplayInfo.copyFrom(displayInfo);
+                outBounds.setEmpty();
+                return false;
+            }
+
+            mTmpRect.set(targetBounds);
+            final Rect postChangeStackBounds = mTmpRect;
+
+            // Calculate the snap fraction of the current stack along the old movement bounds
+            final Rect preChangeMovementBounds = getMovementBounds(postChangeStackBounds);
+            final float snapFraction = mSnapAlgorithm.getSnapFraction(postChangeStackBounds,
+                    preChangeMovementBounds);
             mDisplayInfo.copyFrom(displayInfo);
-            outBounds.setEmpty();
-            return false;
+
+            // Calculate the stack bounds in the new orientation to the same same fraction along the
+            // rotated movement bounds.
+            final Rect postChangeMovementBounds = getMovementBounds(postChangeStackBounds,
+                    false /* adjustForIme */);
+            mSnapAlgorithm.applySnapFraction(postChangeStackBounds, postChangeMovementBounds,
+                    snapFraction);
+            if (mIsMinimized) {
+                applyMinimizedOffset(postChangeStackBounds, postChangeMovementBounds);
+            }
+
+            notifyMovementBoundsChanged(false /* fromImeAdjustment */);
+
+            outBounds.set(postChangeStackBounds);
+            return true;
         }
-
-        mTmpRect.set(targetBounds);
-        final Rect postChangeStackBounds = mTmpRect;
-
-        // Calculate the snap fraction of the current stack along the old movement bounds
-        final Rect preChangeMovementBounds = getMovementBounds(postChangeStackBounds);
-        final float snapFraction = mSnapAlgorithm.getSnapFraction(postChangeStackBounds,
-                preChangeMovementBounds);
-        mDisplayInfo.copyFrom(displayInfo);
-
-        // Calculate the stack bounds in the new orientation to the same same fraction along the
-        // rotated movement bounds.
-        final Rect postChangeMovementBounds = getMovementBounds(postChangeStackBounds,
-                false /* adjustForIme */);
-        mSnapAlgorithm.applySnapFraction(postChangeStackBounds, postChangeMovementBounds,
-                snapFraction);
-        if (mIsMinimized) {
-            applyMinimizedOffset(postChangeStackBounds, postChangeMovementBounds);
-        }
-
-        notifyMovementBoundsChanged(false /* fromImeAdjustment */);
-
-        outBounds.set(postChangeStackBounds);
-        return true;
     }
 
     /**
@@ -378,25 +390,27 @@
      * Notifies listeners that the PIP movement bounds have changed.
      */
     private void notifyMovementBoundsChanged(boolean fromImeAdjustement) {
-        if (mPinnedStackListener != null) {
-            try {
-                final Rect insetBounds = new Rect();
-                getInsetBounds(insetBounds);
-                final Rect normalBounds = getDefaultBounds();
-                if (isValidPictureInPictureAspectRatio(mAspectRatio)) {
-                    transformBoundsToAspectRatio(normalBounds, mAspectRatio);
+        synchronized (mService.mWindowMap) {
+            if (mPinnedStackListener != null) {
+                try {
+                    final Rect insetBounds = new Rect();
+                    getInsetBounds(insetBounds);
+                    final Rect normalBounds = getDefaultBounds();
+                    if (isValidPictureInPictureAspectRatio(mAspectRatio)) {
+                        transformBoundsToAspectRatio(normalBounds, mAspectRatio);
+                    }
+                    final Rect animatingBounds = mTmpAnimatingBoundsRect;
+                    final TaskStack pinnedStack = mDisplayContent.getStackById(PINNED_STACK_ID);
+                    if (pinnedStack != null) {
+                        pinnedStack.getAnimationOrCurrentBounds(animatingBounds);
+                    } else {
+                        animatingBounds.set(normalBounds);
+                    }
+                    mPinnedStackListener.onMovementBoundsChanged(insetBounds, normalBounds,
+                            animatingBounds, fromImeAdjustement, mDisplayInfo.rotation);
+                } catch (RemoteException e) {
+                    Slog.e(TAG_WM, "Error delivering actions changed event.", e);
                 }
-                final Rect animatingBounds = mTmpAnimatingBoundsRect;
-                final TaskStack pinnedStack = mDisplayContent.getStackById(PINNED_STACK_ID);
-                if (pinnedStack != null) {
-                    pinnedStack.getAnimationOrCurrentBounds(animatingBounds);
-                } else {
-                    animatingBounds.set(normalBounds);
-                }
-                mPinnedStackListener.onMovementBoundsChanged(insetBounds, normalBounds,
-                        animatingBounds, fromImeAdjustement);
-            } catch (RemoteException e) {
-                Slog.e(TAG_WM, "Error delivering actions changed event.", e);
             }
         }
     }
@@ -405,11 +419,13 @@
      * @return the bounds on the screen that the PIP can be visible in.
      */
     private void getInsetBounds(Rect outRect) {
-        mService.mPolicy.getStableInsetsLw(mDisplayInfo.rotation, mDisplayInfo.logicalWidth,
-                mDisplayInfo.logicalHeight, mTmpInsets);
-        outRect.set(mTmpInsets.left + mScreenEdgeInsets.x, mTmpInsets.top + mScreenEdgeInsets.y,
-                mDisplayInfo.logicalWidth - mTmpInsets.right - mScreenEdgeInsets.x,
-                mDisplayInfo.logicalHeight - mTmpInsets.bottom - mScreenEdgeInsets.y);
+        synchronized (mService.mWindowMap) {
+            mService.mPolicy.getStableInsetsLw(mDisplayInfo.rotation, mDisplayInfo.logicalWidth,
+                    mDisplayInfo.logicalHeight, mTmpInsets);
+            outRect.set(mTmpInsets.left + mScreenEdgeInsets.x, mTmpInsets.top + mScreenEdgeInsets.y,
+                    mDisplayInfo.logicalWidth - mTmpInsets.right - mScreenEdgeInsets.x,
+                    mDisplayInfo.logicalHeight - mTmpInsets.bottom - mScreenEdgeInsets.y);
+        }
     }
 
     /**
@@ -417,7 +433,9 @@
      *         controller.
      */
     private Rect getMovementBounds(Rect stackBounds) {
-        return getMovementBounds(stackBounds, true /* adjustForIme */);
+        synchronized (mService.mWindowMap) {
+            return getMovementBounds(stackBounds, true /* adjustForIme */);
+        }
     }
 
     /**
@@ -425,23 +443,27 @@
      *         controller.
      */
     private Rect getMovementBounds(Rect stackBounds, boolean adjustForIme) {
-        final Rect movementBounds = new Rect();
-        getInsetBounds(movementBounds);
+        synchronized (mService.mWindowMap) {
+            final Rect movementBounds = new Rect();
+            getInsetBounds(movementBounds);
 
-        // Apply the movement bounds adjustments based on the current state
-        mSnapAlgorithm.getMovementBounds(stackBounds, movementBounds, movementBounds,
-                (adjustForIme && mIsImeShowing) ? mImeHeight : 0);
-        return movementBounds;
+            // Apply the movement bounds adjustments based on the current state
+            mSnapAlgorithm.getMovementBounds(stackBounds, movementBounds, movementBounds,
+                    (adjustForIme && mIsImeShowing) ? mImeHeight : 0);
+            return movementBounds;
+        }
     }
 
     /**
      * Applies the minimized offsets to the given stack bounds.
      */
     private void applyMinimizedOffset(Rect stackBounds, Rect movementBounds) {
-        mTmpDisplaySize.set(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
-        mService.getStableInsetsLocked(mDisplayContent.getDisplayId(), mStableInsets);
-        mSnapAlgorithm.applyMinimizedOffset(stackBounds, movementBounds, mTmpDisplaySize,
-                mStableInsets);
+        synchronized (mService.mWindowMap) {
+            mTmpDisplaySize.set(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
+            mService.getStableInsetsLocked(mDisplayContent.getDisplayId(), mStableInsets);
+            mSnapAlgorithm.applyMinimizedOffset(stackBounds, movementBounds, mTmpDisplaySize,
+                    mStableInsets);
+        }
     }
 
     /**
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/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/am/AppErrorDialogTest.java b/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
new file mode 100644
index 0000000..243c1b3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.am;
+
+import android.content.Context;
+import android.os.Handler;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.server.AppOpsService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+
+/**
+ * runtest -c com.android.server.am.AppErrorDialogTest frameworks-services
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class AppErrorDialogTest {
+
+    private Context mContext;
+    private ActivityManagerService mService;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mService = new ActivityManagerService(new ActivityManagerService.Injector() {
+            @Override
+            public AppOpsService getAppOpsService(File file, Handler handler) {
+                return null;
+            }
+
+            @Override
+            public Handler getUiHandler(ActivityManagerService service) {
+                return null;
+            }
+
+            @Override
+            public boolean isNetworkRestrictedForUid(int uid) {
+                return false;
+            }
+        });
+    }
+
+    @Test
+    @UiThreadTest
+    public void testCreateWorks() throws Exception {
+        AppErrorDialog.Data data = new AppErrorDialog.Data();
+        data.proc = new ProcessRecord(null, mContext.getApplicationInfo(), "name", 12345);
+        data.result = new AppErrorResult();
+
+        AppErrorDialog dialog = new AppErrorDialog(mContext, mService, data);
+
+        dialog.create();
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
index eadece2..8f2f34e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -71,17 +71,17 @@
           "primaryCpuAbiString", "secondaryCpuAbiString",
           "cpuAbiOverrideString", 0, 0, 0, "parentPackageName", null, 0,
           null, null);
-      pri.removedUsers = new int[] {1, 2, 3, 4, 5};
-      pri.populateUsers(pri.removedUsers, setting);
+      pri.populateUsers(new int[] {1, 2, 3, 4, 5}, setting);
       assertNotNull(pri.broadcastUsers);
-      assertEquals(pri.removedUsers.length, pri.broadcastUsers.length);
+      assertEquals(5, pri.broadcastUsers.length);
 
       // Exclude a user
       pri.broadcastUsers = null;
-      setting.setInstantApp(true, 4);
-      pri.populateUsers(pri.removedUsers, setting);
+      final int EXCLUDED_USER_ID = 4;
+      setting.setInstantApp(true, EXCLUDED_USER_ID);
+      pri.populateUsers(new int[] {1, 2, 3, EXCLUDED_USER_ID, 5}, setting);
       assertNotNull(pri.broadcastUsers);
-      assertEquals(pri.removedUsers.length - 1, pri.broadcastUsers.length);
+      assertEquals(5 - 1, pri.broadcastUsers.length);
 
       // TODO: test that sendApplicationHiddenForUser() actually fills in
       // broadcastUsers
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 397aa00..3d51c4c 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -4393,7 +4393,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 +4406,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}
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/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/wifi/java/android/net/wifi/aware/DiscoverySession.java b/wifi/java/android/net/wifi/aware/DiscoverySession.java
index 82b3792..bf5c42b8 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);