Merge branch 'master' of https://googleplex-android.googlesource.com/_direct/platform/frameworks/base
diff --git a/api/current.txt b/api/current.txt
index 1973da5..76c3e81 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -16270,21 +16270,12 @@
   }
 
   public final class NetworkCapabilities implements android.os.Parcelable {
-    ctor public NetworkCapabilities();
     ctor public NetworkCapabilities(android.net.NetworkCapabilities);
-    method public void addNetworkCapability(int);
-    method public void addTransportType(int);
     method public int describeContents();
     method public int getLinkDownstreamBandwidthKbps();
     method public int getLinkUpstreamBandwidthKbps();
-    method public java.util.Collection<java.lang.Integer> getNetworkCapabilities();
-    method public java.util.Collection<java.lang.Integer> getTransportTypes();
     method public boolean hasCapability(int);
     method public boolean hasTransport(int);
-    method public void removeNetworkCapability(int);
-    method public void removeTransportType(int);
-    method public void setLinkDownstreamBandwidthKbps(int);
-    method public void setLinkUpstreamBandwidthKbps(int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
     field public static final int NET_CAPABILITY_CBS = 5; // 0x5
@@ -16358,7 +16349,15 @@
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
-    field public final android.net.NetworkCapabilities networkCapabilities;
+  }
+
+  public static class NetworkRequest.Builder {
+    ctor public NetworkRequest.Builder();
+    method public android.net.NetworkRequest.Builder addCapability(int);
+    method public android.net.NetworkRequest.Builder addTransportType(int);
+    method public android.net.NetworkRequest build();
+    method public android.net.NetworkRequest.Builder removeCapability(int);
+    method public android.net.NetworkRequest.Builder removeTransportType(int);
   }
 
   public class ParseException extends java.lang.RuntimeException {
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 27402668..ff90e789 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -429,6 +429,11 @@
      */
     public final static int INVALID_NET_ID = 0;
 
+    /**
+     * @hide
+     */
+    public final static int REQUEST_ID_UNSET = 0;
+
     private final IConnectivityManager mService;
 
     private final String mPackageName;
@@ -883,8 +888,8 @@
      * @hide
      */
     public static void maybeMarkCapabilitiesRestricted(NetworkCapabilities nc) {
-        for (Integer capability : nc.getNetworkCapabilities()) {
-            switch (capability.intValue()) {
+        for (int capability : nc.getCapabilities()) {
+            switch (capability) {
                 case NetworkCapabilities.NET_CAPABILITY_CBS:
                 case NetworkCapabilities.NET_CAPABILITY_DUN:
                 case NetworkCapabilities.NET_CAPABILITY_EIMS:
@@ -903,7 +908,7 @@
         }
         // All the capabilities are typically provided by restricted networks.
         // Conclude that this network is restricted.
-        nc.removeNetworkCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+        nc.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
     }
 
     private NetworkCapabilities networkCapabilitiesForFeature(int networkType, String feature) {
@@ -927,15 +932,14 @@
                 return null;
             }
             NetworkCapabilities netCap = new NetworkCapabilities();
-            netCap.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
-            netCap.addNetworkCapability(cap);
+            netCap.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR).addCapability(cap);
             maybeMarkCapabilitiesRestricted(netCap);
             return netCap;
         } else if (networkType == TYPE_WIFI) {
             if ("p2p".equals(feature)) {
                 NetworkCapabilities netCap = new NetworkCapabilities();
                 netCap.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
-                netCap.addNetworkCapability(NetworkCapabilities.NET_CAPABILITY_WIFI_P2P);
+                netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_WIFI_P2P);
                 maybeMarkCapabilitiesRestricted(netCap);
                 return netCap;
             }
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 35274f1..fe96287 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -44,6 +44,9 @@
     private static final String TAG = "NetworkCapabilities";
     private static final boolean DBG = false;
 
+    /**
+     * @hide
+     */
     public NetworkCapabilities() {
     }
 
@@ -154,58 +157,64 @@
      * Multiple capabilities may be applied sequentially.  Note that when searching
      * for a network to satisfy a request, all capabilities requested must be satisfied.
      *
-     * @param networkCapability the {@code NetworkCapabilities.NET_CAPABILITY_*} to be added.
+     * @param capability the {@code NetworkCapabilities.NET_CAPABILITY_*} to be added.
+     * @return This NetworkCapability to facilitate chaining.
+     * @hide
      */
-    public void addNetworkCapability(int networkCapability) {
-        if (networkCapability < MIN_NET_CAPABILITY ||
-                networkCapability > MAX_NET_CAPABILITY) {
+    public NetworkCapabilities addCapability(int capability) {
+        if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) {
             throw new IllegalArgumentException("NetworkCapability out of range");
         }
-        mNetworkCapabilities |= 1 << networkCapability;
+        mNetworkCapabilities |= 1 << capability;
+        return this;
     }
 
     /**
      * Removes (if found) the given capability from this {@code NetworkCapability} instance.
      *
-     * @param networkCapability the {@code NetworkCapabilities.NET_CAPABILTIY_*} to be removed.
+     * @param capability the {@code NetworkCapabilities.NET_CAPABILTIY_*} to be removed.
+     * @return This NetworkCapability to facilitate chaining.
+     * @hide
      */
-    public void removeNetworkCapability(int networkCapability) {
-        if (networkCapability < MIN_NET_CAPABILITY ||
-                networkCapability > MAX_NET_CAPABILITY) {
+    public NetworkCapabilities removeCapability(int capability) {
+        if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) {
             throw new IllegalArgumentException("NetworkCapability out of range");
         }
-        mNetworkCapabilities &= ~(1 << networkCapability);
+        mNetworkCapabilities &= ~(1 << capability);
+        return this;
     }
 
     /**
      * Gets all the capabilities set on this {@code NetworkCapability} instance.
      *
-     * @return a {@link Collection} of {@code NetworkCapabilities.NET_CAPABILITY_*} values
+     * @return an array of {@code NetworkCapabilities.NET_CAPABILITY_*} values
      *         for this instance.
+     * @hide
      */
-    public Collection<Integer> getNetworkCapabilities() {
+    public int[] getCapabilities() {
         return enumerateBits(mNetworkCapabilities);
     }
 
     /**
      * Tests for the presence of a capabilitity on this instance.
      *
-     * @param networkCapability the {@code NetworkCapabilities.NET_CAPABILITY_*} to be tested for.
+     * @param capability the {@code NetworkCapabilities.NET_CAPABILITY_*} to be tested for.
      * @return {@code true} if set on this instance.
      */
-    public boolean hasCapability(int networkCapability) {
-        if (networkCapability < MIN_NET_CAPABILITY ||
-                networkCapability > MAX_NET_CAPABILITY) {
+    public boolean hasCapability(int capability) {
+        if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) {
             return false;
         }
-        return ((mNetworkCapabilities & (1 << networkCapability)) != 0);
+        return ((mNetworkCapabilities & (1 << capability)) != 0);
     }
 
-    private Collection<Integer> enumerateBits(long val) {
-        ArrayList<Integer> result = new ArrayList<Integer>();
+    private int[] enumerateBits(long val) {
+        int size = Long.bitCount(val);
+        int[] result = new int[size];
+        int index = 0;
         int resource = 0;
         while (val > 0) {
-            if ((val & 1) == 1) result.add(resource);
+            if ((val & 1) == 1) result[index++] = resource;
             val = val >> 1;
             resource++;
         }
@@ -265,33 +274,40 @@
      * {@code NetworkCapabilities.NET_CAPABILITY_*} listed above.
      *
      * @param transportType the {@code NetworkCapabilities.TRANSPORT_*} to be added.
+     * @return This NetworkCapability to facilitate chaining.
+     * @hide
      */
-    public void addTransportType(int transportType) {
+    public NetworkCapabilities addTransportType(int transportType) {
         if (transportType < MIN_TRANSPORT || transportType > MAX_TRANSPORT) {
             throw new IllegalArgumentException("TransportType out of range");
         }
         mTransportTypes |= 1 << transportType;
+        return this;
     }
 
     /**
      * Removes (if found) the given transport from this {@code NetworkCapability} instance.
      *
      * @param transportType the {@code NetworkCapabilities.TRANSPORT_*} to be removed.
+     * @return This NetworkCapability to facilitate chaining.
+     * @hide
      */
-    public void removeTransportType(int transportType) {
+    public NetworkCapabilities removeTransportType(int transportType) {
         if (transportType < MIN_TRANSPORT || transportType > MAX_TRANSPORT) {
             throw new IllegalArgumentException("TransportType out of range");
         }
         mTransportTypes &= ~(1 << transportType);
+        return this;
     }
 
     /**
      * Gets all the transports set on this {@code NetworkCapability} instance.
      *
-     * @return a {@link Collection} of {@code NetworkCapabilities.TRANSPORT_*} values
+     * @return an array of {@code NetworkCapabilities.TRANSPORT_*} values
      *         for this instance.
+     * @hide
      */
-    public Collection<Integer> getTransportTypes() {
+    public int[] getTransportTypes() {
         return enumerateBits(mTransportTypes);
     }
 
@@ -340,6 +356,7 @@
      * fast backhauls and slow backhauls.
      *
      * @param upKbps the estimated first hop upstream (device to network) bandwidth.
+     * @hide
      */
     public void setLinkUpstreamBandwidthKbps(int upKbps) {
         mLinkUpBandwidthKbps = upKbps;
@@ -368,6 +385,7 @@
      * fast backhauls and slow backhauls.
      *
      * @param downKbps the estimated first hop downstream (network to device) bandwidth.
+     * @hide
      */
     public void setLinkDownstreamBandwidthKbps(int downKbps) {
         mLinkDownBandwidthKbps = downKbps;
@@ -464,24 +482,22 @@
         };
 
     public String toString() {
-        Collection<Integer> types = getTransportTypes();
-        String transports = (types.size() > 0 ? " Transports: " : "");
-        Iterator<Integer> i = types.iterator();
-        while (i.hasNext()) {
-            switch (i.next()) {
+        int[] types = getTransportTypes();
+        String transports = (types.length > 0 ? " Transports: " : "");
+        for (int i = 0; i < types.length;) {
+            switch (types[i]) {
                 case TRANSPORT_CELLULAR:    transports += "CELLULAR"; break;
                 case TRANSPORT_WIFI:        transports += "WIFI"; break;
                 case TRANSPORT_BLUETOOTH:   transports += "BLUETOOTH"; break;
                 case TRANSPORT_ETHERNET:    transports += "ETHERNET"; break;
             }
-            if (i.hasNext()) transports += "|";
+            if (++i < types.length) transports += "|";
         }
 
-        types = getNetworkCapabilities();
-        String capabilities = (types.size() > 0 ? " Capabilities: " : "");
-        i = types.iterator();
-        while (i.hasNext()) {
-            switch (i.next().intValue()) {
+        types = getCapabilities();
+        String capabilities = (types.length > 0 ? " Capabilities: " : "");
+        for (int i = 0; i < types.length; ) {
+            switch (types[i]) {
                 case NET_CAPABILITY_MMS:            capabilities += "MMS"; break;
                 case NET_CAPABILITY_SUPL:           capabilities += "SUPL"; break;
                 case NET_CAPABILITY_DUN:            capabilities += "DUN"; break;
@@ -497,7 +513,7 @@
                 case NET_CAPABILITY_INTERNET:       capabilities += "INTERNET"; break;
                 case NET_CAPABILITY_NOT_RESTRICTED: capabilities += "NOT_RESTRICTED"; break;
             }
-            if (i.hasNext()) capabilities += "&";
+            if (++i < types.length) capabilities += "&";
         }
 
         String upBand = ((mLinkUpBandwidthKbps > 0) ? " LinkUpBandwidth>=" +
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 47377e9..7911c72 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -22,19 +22,14 @@
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
- * Defines a request for a network, made by calling {@link ConnectivityManager#requestNetwork}
- * or {@link ConnectivityManager#listenForNetwork}.
- *
- * This token records the {@link NetworkCapabilities} used to make the request and identifies
- * the request.  It should be used to release the request via
- * {@link ConnectivityManager#releaseNetworkRequest} when the network is no longer desired.
+ * Defines a request for a network, made through {@link NetworkRequest.Builder} and used
+ * to request a network via {@link ConnectivityManager#requestNetwork} or listen for changes
+ * via {@link ConnectivityManager#listenForNetwork}.
  */
 public class NetworkRequest implements Parcelable {
     /**
-     * The {@link NetworkCapabilities} that define this request.  This should not be modified.
-     * The networkCapabilities of the request are set when
-     * {@link ConnectivityManager#requestNetwork} is called and the value is presented here
-     * as a convenient reminder of what was requested.
+     * The {@link NetworkCapabilities} that define this request.
+     * @hide
      */
     public final NetworkCapabilities networkCapabilities;
 
@@ -71,6 +66,95 @@
         this.legacyType = that.legacyType;
     }
 
+    /**
+     * Builder used to create {@link NetworkRequest} objects.  Specify the Network features
+     * needed in terms of {@link NetworkCapabilities} features
+     */
+    public static class Builder {
+        private final NetworkCapabilities mNetworkCapabilities = new NetworkCapabilities();
+
+        /**
+         * Default constructor for Builder.
+         */
+        public Builder() {}
+
+        /**
+         * Build {@link NetworkRequest} give the current set of capabilities.
+         */
+        public NetworkRequest build() {
+            return new NetworkRequest(mNetworkCapabilities, ConnectivityManager.TYPE_NONE,
+                    ConnectivityManager.REQUEST_ID_UNSET);
+        }
+
+        /**
+         * Add the given capability requirement to this builder.  These represent
+         * the requested network's required capabilities.  Note that when searching
+         * for a network to satisfy a request, all capabilities requested must be
+         * satisfied.  See {@link NetworkCapabilities} for {@code NET_CAPABILITIY_*}
+         * definitions.
+         *
+         * @param capability The {@code NetworkCapabilities.NET_CAPABILITY_*} to add.
+         * @return The builder to facilitate chaining
+         *         {@code builder.addCapability(...).addCapability();}.
+         */
+        public Builder addCapability(int capability) {
+            mNetworkCapabilities.addCapability(capability);
+            return this;
+        }
+
+        /**
+         * Removes (if found) the given capability from this builder instance.
+         *
+         * @param capability The {@code NetworkCapabilities.NET_CAPABILITY_*} to remove.
+         * @return The builder to facilitate chaining.
+         */
+        public Builder removeCapability(int capability) {
+            mNetworkCapabilities.removeCapability(capability);
+            return this;
+        }
+
+        /**
+         * Adds the given transport requirement to this builder.  These represent
+         * the set of allowed transports for the request.  Only networks using one
+         * of these transports will satisfy the request.  If no particular transports
+         * are required, none should be specified here.  See {@link NetworkCapabilities}
+         * for {@code TRANSPORT_*} definitions.
+         *
+         * @param transportType The {@code NetworkCapabilities.TRANSPORT_*} to add.
+         * @return The builder to facilitate chaining.
+         */
+        public Builder addTransportType(int transportType) {
+            mNetworkCapabilities.addTransportType(transportType);
+            return this;
+        }
+
+        /**
+         * Removes (if found) the given transport from this builder instance.
+         *
+         * @param transportType The {@code NetworkCapabilities.TRANSPORT_*} to remove.
+         * @return The builder to facilitate chaining.
+         */
+        public Builder removeTransportType(int transportType) {
+            mNetworkCapabilities.removeTransportType(transportType);
+            return this;
+        }
+
+        /**
+         * @hide
+         */
+        public Builder setLinkUpstreamBandwidthKbps(int upKbps) {
+            mNetworkCapabilities.setLinkUpstreamBandwidthKbps(upKbps);
+            return this;
+        }
+        /**
+         * @hide
+         */
+        public Builder setLinkDownstreamBandwidthKbps(int downKbps) {
+            mNetworkCapabilities.setLinkDownstreamBandwidthKbps(downKbps);
+            return this;
+        }
+    }
+
     // implement the Parcelable interface
     public int describeContents() {
         return 0;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index ba6b214..6280fde 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -2363,19 +2363,24 @@
         if (rctlr == null) {
             return false;
         }
-        IAudioService service = getService();
-        final RemoteController.OnClientUpdateListener l = rctlr.getUpdateListener();
-        final ComponentName listenerComponent = new ComponentName(mContext, l.getClass());
-        try {
-            int[] artworkDimensions = rctlr.getArtworkSize();
-            boolean reg = service.registerRemoteController(rctlr.getRcDisplay(),
-                    artworkDimensions[0]/*w*/, artworkDimensions[1]/*h*/,
-                    listenerComponent);
-            rctlr.setIsRegistered(reg);
-            return reg;
-        } catch (RemoteException e) {
-            Log.e(TAG, "Dead object in registerRemoteController " + e);
-            return false;
+        if (USE_SESSIONS) {
+            rctlr.startListeningToSessions();
+            return true;
+        } else {
+            IAudioService service = getService();
+            final RemoteController.OnClientUpdateListener l = rctlr.getUpdateListener();
+            final ComponentName listenerComponent = new ComponentName(mContext, l.getClass());
+            try {
+                int[] artworkDimensions = rctlr.getArtworkSize();
+                boolean reg = service.registerRemoteController(rctlr.getRcDisplay(),
+                        artworkDimensions[0]/* w */, artworkDimensions[1]/* h */,
+                        listenerComponent);
+                rctlr.setIsRegistered(reg);
+                return reg;
+            } catch (RemoteException e) {
+                Log.e(TAG, "Dead object in registerRemoteController " + e);
+                return false;
+            }
         }
     }
 
@@ -2388,12 +2393,16 @@
         if (rctlr == null) {
             return;
         }
-        IAudioService service = getService();
-        try {
-            service.unregisterRemoteControlDisplay(rctlr.getRcDisplay());
-            rctlr.setIsRegistered(false);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Dead object in unregisterRemoteControlDisplay " + e);
+        if (USE_SESSIONS) {
+            rctlr.stopListeningToSessions();
+        } else {
+            IAudioService service = getService();
+            try {
+                service.unregisterRemoteControlDisplay(rctlr.getRcDisplay());
+                rctlr.setIsRegistered(false);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Dead object in unregisterRemoteControlDisplay " + e);
+            }
         }
     }
 
diff --git a/media/java/android/media/RemoteController.java b/media/java/android/media/RemoteController.java
index 3711585..76c7299 100644
--- a/media/java/android/media/RemoteController.java
+++ b/media/java/android/media/RemoteController.java
@@ -19,11 +19,17 @@
 import android.app.ActivityManager;
 import android.app.PendingIntent;
 import android.app.PendingIntent.CanceledException;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.media.IRemoteControlDisplay;
 import android.media.MediaMetadataEditor;
+import android.media.session.MediaController;
+import android.media.session.MediaSession;
+import android.media.session.MediaSessionLegacyHelper;
+import android.media.session.MediaSessionManager;
+import android.media.session.PlaybackState;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -34,6 +40,7 @@
 import android.view.KeyEvent;
 
 import java.lang.ref.WeakReference;
+import java.util.List;
 
 /**
  * The RemoteController class is used to control media playback, display and update media metadata
@@ -56,6 +63,7 @@
     private final static int TRANSPORT_UNKNOWN = 0;
     private final static String TAG = "RemoteController";
     private final static boolean DEBUG = false;
+    private final static boolean USE_SESSIONS = true;
     private final static Object mGenLock = new Object();
     private final static Object mInfoLock = new Object();
     private final RcDisplay mRcd;
@@ -64,6 +72,11 @@
     private final int mMaxBitmapDimension;
     private MetadataEditor mMetadataEditor;
 
+    private MediaSessionManager mSessionManager;
+    private MediaSessionManager.SessionListener mSessionListener
+            = new TopTransportSessionListener();
+    private MediaController.Callback mSessionCb = new MediaControllerCallback();
+
     /**
      * Synchronized on mGenLock
      */
@@ -79,6 +92,8 @@
     private int mArtworkWidth = -1;
     private int mArtworkHeight = -1;
     private boolean mEnabled = true;
+    // synchronized on mInfoLock, for USE_SESSION apis.
+    private MediaController mCurrentSession;
 
     /**
      * Class constructor.
@@ -123,6 +138,8 @@
         mContext = context;
         mRcd = new RcDisplay(this);
         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        mSessionManager = (MediaSessionManager) context
+                .getSystemService(Context.MEDIA_SESSION_SERVICE);
 
         if (ActivityManager.isLowRamDeviceStatic()) {
             mMaxBitmapDimension = MAX_BITMAP_DIMENSION;
@@ -194,8 +211,15 @@
      * @hide
      */
     public String getRemoteControlClientPackageName() {
-        return mClientPendingIntentCurrent != null ?
-                mClientPendingIntentCurrent.getCreatorPackage() : null;
+        if (USE_SESSIONS) {
+            synchronized (mInfoLock) {
+                return mCurrentSession != null ? mCurrentSession.getSessionInfo().getPackageName()
+                        : null;
+            }
+        } else {
+            return mClientPendingIntentCurrent != null ?
+                    mClientPendingIntentCurrent.getCreatorPackage() : null;
+        }
     }
 
     /**
@@ -215,22 +239,38 @@
      * @see OnClientUpdateListener#onClientPlaybackStateUpdate(int, long, long, float)
      */
     public long getEstimatedMediaPosition() {
-        if (mLastPlaybackInfo != null) {
-            if (!RemoteControlClient.playbackPositionShouldMove(mLastPlaybackInfo.mState)) {
-                return mLastPlaybackInfo.mCurrentPosMs;
+        if (USE_SESSIONS) {
+            synchronized (mInfoLock) {
+                if (mCurrentSession != null) {
+                    PlaybackState state = mCurrentSession.getPlaybackState();
+                    if (state != null) {
+                        return state.getPosition();
+                    }
+                }
             }
-
-            // Take the current position at the time of state change and estimate.
-            final long thenPos = mLastPlaybackInfo.mCurrentPosMs;
-            if (thenPos < 0) {
-                return -1;
+        } else {
+            final PlaybackInfo lastPlaybackInfo;
+            synchronized (mInfoLock) {
+                lastPlaybackInfo = mLastPlaybackInfo;
             }
+            if (lastPlaybackInfo != null) {
+                if (!RemoteControlClient.playbackPositionShouldMove(lastPlaybackInfo.mState)) {
+                    return lastPlaybackInfo.mCurrentPosMs;
+                }
 
-            final long now = SystemClock.elapsedRealtime();
-            final long then = mLastPlaybackInfo.mStateChangeTimeMs;
-            final long sinceThen = now - then;
-            final long scaledSinceThen = (long) (sinceThen * mLastPlaybackInfo.mSpeed);
-            return thenPos + scaledSinceThen;
+                // Take the current position at the time of state change and
+                // estimate.
+                final long thenPos = lastPlaybackInfo.mCurrentPosMs;
+                if (thenPos < 0) {
+                    return -1;
+                }
+
+                final long now = SystemClock.elapsedRealtime();
+                final long then = lastPlaybackInfo.mStateChangeTimeMs;
+                final long sinceThen = now - then;
+                final long scaledSinceThen = (long) (sinceThen * lastPlaybackInfo.mSpeed);
+                return thenPos + scaledSinceThen;
+            }
         }
         return -1;
     }
@@ -267,30 +307,40 @@
         if (!KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
             throw new IllegalArgumentException("not a media key event");
         }
-        final PendingIntent pi;
-        synchronized(mInfoLock) {
-            if (!mIsRegistered) {
-                Log.e(TAG, "Cannot use sendMediaKeyEvent() from an unregistered RemoteController");
-                return false;
-            }
-            if (!mEnabled) {
-                Log.e(TAG, "Cannot use sendMediaKeyEvent() from a disabled RemoteController");
-                return false;
-            }
-            pi = mClientPendingIntentCurrent;
-        }
-        if (pi != null) {
-            Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
-            intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
-            try {
-                pi.send(mContext, 0, intent);
-            } catch (CanceledException e) {
-                Log.e(TAG, "Error sending intent for media button down: ", e);
+        if (USE_SESSIONS) {
+            synchronized (mInfoLock) {
+                if (mCurrentSession != null) {
+                    return mCurrentSession.dispatchMediaButtonEvent(keyEvent);
+                }
                 return false;
             }
         } else {
-            Log.i(TAG, "No-op when sending key click, no receiver right now");
-            return false;
+            final PendingIntent pi;
+            synchronized (mInfoLock) {
+                if (!mIsRegistered) {
+                    Log.e(TAG,
+                            "Cannot use sendMediaKeyEvent() from an unregistered RemoteController");
+                    return false;
+                }
+                if (!mEnabled) {
+                    Log.e(TAG, "Cannot use sendMediaKeyEvent() from a disabled RemoteController");
+                    return false;
+                }
+                pi = mClientPendingIntentCurrent;
+            }
+            if (pi != null) {
+                Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+                intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
+                try {
+                    pi.send(mContext, 0, intent);
+                } catch (CanceledException e) {
+                    Log.e(TAG, "Error sending intent for media button down: ", e);
+                    return false;
+                }
+            } else {
+                Log.i(TAG, "No-op when sending key click, no receiver right now");
+                return false;
+            }
         }
         return true;
     }
@@ -311,11 +361,19 @@
         if (timeMs < 0) {
             throw new IllegalArgumentException("illegal negative time value");
         }
-        final int genId;
-        synchronized (mGenLock) {
-            genId = mClientGenerationIdCurrent;
+        if (USE_SESSIONS) {
+            synchronized (mInfoLock) {
+                if (mCurrentSession != null) {
+                    mCurrentSession.getTransportControls().seekTo(timeMs);
+                }
+            }
+        } else {
+            final int genId;
+            synchronized (mGenLock) {
+                genId = mClientGenerationIdCurrent;
+            }
+            mAudioManager.setRemoteControlClientPlaybackPosition(genId, timeMs);
         }
-        mAudioManager.setRemoteControlClientPlaybackPosition(genId, timeMs);
         return true;
     }
 
@@ -430,7 +488,6 @@
         return editor;
     }
 
-
     /**
      * A class to read the metadata published by a {@link RemoteControlClient}, or send a
      * {@link RemoteControlClient} new values for keys that can be edited.
@@ -477,26 +534,41 @@
             if (!mMetadataChanged) {
                 return;
             }
-            final int genId;
-            synchronized(mGenLock) {
-                genId = mClientGenerationIdCurrent;
-            }
-            synchronized(mInfoLock) {
-                if (mEditorMetadata.containsKey(
-                        String.valueOf(MediaMetadataEditor.RATING_KEY_BY_USER))) {
-                    Rating rating = (Rating) getObject(
-                            MediaMetadataEditor.RATING_KEY_BY_USER, null);
-                    mAudioManager.updateRemoteControlClientMetadata(genId,
-                          MediaMetadataEditor.RATING_KEY_BY_USER,
-                          rating);
-                } else {
-                    Log.e(TAG, "no metadata to apply");
+            if (USE_SESSIONS) {
+                synchronized (mInfoLock) {
+                    if (mCurrentSession != null) {
+                        if (mEditorMetadata.containsKey(
+                                String.valueOf(MediaMetadataEditor.RATING_KEY_BY_USER))) {
+                            Rating rating = (Rating) getObject(
+                                    MediaMetadataEditor.RATING_KEY_BY_USER, null);
+                            if (rating != null) {
+                                mCurrentSession.getTransportControls().setRating(rating);
+                            }
+                        }
+                    }
                 }
-                // NOT setting mApplied to true as this type of MetadataEditor will be applied
-                // multiple times, whenever the user of a RemoteController needs to change the
-                // metadata (e.g. user changes the rating of a song more than once during playback)
-                mApplied = false;
+            } else {
+                final int genId;
+                synchronized(mGenLock) {
+                    genId = mClientGenerationIdCurrent;
+                }
+                synchronized(mInfoLock) {
+                    if (mEditorMetadata.containsKey(
+                            String.valueOf(MediaMetadataEditor.RATING_KEY_BY_USER))) {
+                        Rating rating = (Rating) getObject(
+                                MediaMetadataEditor.RATING_KEY_BY_USER, null);
+                        mAudioManager.updateRemoteControlClientMetadata(genId,
+                              MediaMetadataEditor.RATING_KEY_BY_USER,
+                              rating);
+                    } else {
+                        Log.e(TAG, "no metadata to apply");
+                    }
+                }
             }
+            // NOT setting mApplied to true as this type of MetadataEditor will be applied
+            // multiple times, whenever the user of a RemoteController needs to change the
+            // metadata (e.g. user changes the rating of a song more than once during playback)
+            mApplied = false;
         }
 
     }
@@ -649,6 +721,46 @@
         }
     }
 
+    /**
+     * This receives updates when the current session changes. This is
+     * registered to receive the updates on the handler thread so it can call
+     * directly into the appropriate methods.
+     */
+    private class MediaControllerCallback extends MediaController.Callback {
+        @Override
+        public void onPlaybackStateChanged(PlaybackState state) {
+            onNewPlaybackState(state);
+        }
+
+        @Override
+        public void onMetadataChanged(MediaMetadata metadata) {
+            onNewMediaMetadata(metadata);
+        }
+    }
+
+    /**
+     * Listens for changes to the active session stack and replaces the
+     * currently tracked session if it has changed.
+     */
+    private class TopTransportSessionListener extends MediaSessionManager.SessionListener {
+        @Override
+        public void onActiveSessionsChanged(List<MediaController> controllers) {
+            int size = controllers.size();
+            for (int i = 0; i < size; i++) {
+                MediaController controller = controllers.get(i);
+                long flags = controller.getFlags();
+                // We only care about sessions that handle transport controls,
+                // which will be true for apps using RCC
+                if ((flags & MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS) != 0) {
+                    updateController(controller);
+                    return;
+                }
+            }
+            updateController(null);
+        }
+
+    }
+
     //==================================================
     // Event handling
     private final EventHandler mEventHandler;
@@ -658,6 +770,8 @@
     private final static int MSG_NEW_METADATA       = 3; // msg always has non-null obj parameter
     private final static int MSG_CLIENT_CHANGE      = 4;
     private final static int MSG_DISPLAY_ENABLE     = 5;
+    private final static int MSG_NEW_PLAYBACK_STATE = 6;
+    private final static int MSG_NEW_MEDIA_METADATA = 7;
 
     private class EventHandler extends Handler {
 
@@ -686,12 +800,46 @@
                 case MSG_DISPLAY_ENABLE:
                     onDisplayEnable(msg.arg1 == 1);
                     break;
+                case MSG_NEW_PLAYBACK_STATE:
+                    // same as new playback info but using new apis
+                    onNewPlaybackState((PlaybackState) msg.obj);
+                    break;
+                case MSG_NEW_MEDIA_METADATA:
+                    onNewMediaMetadata((MediaMetadata) msg.obj);
+                    break;
                 default:
                     Log.e(TAG, "unknown event " + msg.what);
             }
         }
     }
 
+    /**
+     * @hide
+     */
+    void startListeningToSessions() {
+        final ComponentName listenerComponent = new ComponentName(mContext,
+                mOnClientUpdateListener.getClass());
+        mSessionManager.addActiveSessionsListener(mSessionListener, listenerComponent,
+                ActivityManager.getCurrentUser());
+        mSessionListener.onActiveSessionsChanged(mSessionManager
+                .getActiveSessions(listenerComponent));
+        if (DEBUG) {
+            Log.d(TAG, "Registered session listener with component " + listenerComponent
+                    + " for user " + ActivityManager.getCurrentUser());
+        }
+    }
+
+    /**
+     * @hide
+     */
+    void stopListeningToSessions() {
+        mSessionManager.removeActiveSessionsListener(mSessionListener);
+        if (DEBUG) {
+            Log.d(TAG, "Unregistered session listener for user "
+                    + ActivityManager.getCurrentUser());
+        }
+    }
+
     /** If the msg is already queued, replace it with this one. */
     private static final int SENDMSG_REPLACE = 0;
     /** If the msg is already queued, ignore this one and leave the old. */
@@ -713,6 +861,7 @@
         handler.sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delayMs);
     }
 
+    ///////////// These calls are used by the old APIs with RCC and RCD //////////////////////
     private void onNewPendingIntent(int genId, PendingIntent pi) {
         synchronized(mGenLock) {
             if (mClientGenerationIdCurrent != genId) {
@@ -848,6 +997,86 @@
         }
     }
 
+    ///////////// These calls are used by the new APIs with Sessions //////////////////////
+    private void updateController(MediaController controller) {
+        if (DEBUG) {
+            Log.d(TAG, "Updating controller to " + controller + " previous controller is "
+                    + mCurrentSession);
+        }
+        synchronized (mInfoLock) {
+            if (controller == null) {
+                if (mCurrentSession != null) {
+                    mCurrentSession.removeCallback(mSessionCb);
+                    mCurrentSession = null;
+                    sendMsg(mEventHandler, MSG_CLIENT_CHANGE, SENDMSG_REPLACE,
+                            0 /* genId */, 1 /* clearing */, null /* obj */, 0 /* delay */);
+                }
+            } else if (mCurrentSession == null
+                    || !controller.getSessionInfo().getId()
+                            .equals(mCurrentSession.getSessionInfo().getId())) {
+                if (mCurrentSession != null) {
+                    mCurrentSession.removeCallback(mSessionCb);
+                }
+                sendMsg(mEventHandler, MSG_CLIENT_CHANGE, SENDMSG_REPLACE,
+                        0 /* genId */, 0 /* clearing */, null /* obj */, 0 /* delay */);
+                mCurrentSession = controller;
+                mCurrentSession.addCallback(mSessionCb, mEventHandler);
+
+                PlaybackState state = controller.getPlaybackState();
+                sendMsg(mEventHandler, MSG_NEW_PLAYBACK_STATE, SENDMSG_REPLACE,
+                        0 /* genId */, 0, state /* obj */, 0 /* delay */);
+
+                MediaMetadata metadata = controller.getMetadata();
+                sendMsg(mEventHandler, MSG_NEW_MEDIA_METADATA, SENDMSG_REPLACE,
+                        0 /* arg1 */, 0 /* arg2 */, metadata /* obj */, 0 /* delay */);
+            }
+            // else same controller, no need to update
+        }
+    }
+
+    private void onNewPlaybackState(PlaybackState state) {
+        final OnClientUpdateListener l;
+        synchronized (mInfoLock) {
+            l = this.mOnClientUpdateListener;
+        }
+        if (l != null) {
+            int playstate = state == null ? RemoteControlClient.PLAYSTATE_NONE : PlaybackState
+                    .getRccStateFromState(state.getState());
+            if (state == null || state.getPosition() == PlaybackState.PLAYBACK_POSITION_UNKNOWN) {
+                l.onClientPlaybackStateUpdate(playstate);
+            } else {
+                l.onClientPlaybackStateUpdate(playstate, state.getLastPositionUpdateTime(),
+                        state.getPosition(), state.getPlaybackRate());
+            }
+            if (state != null) {
+                l.onClientTransportControlUpdate(PlaybackState.getRccControlFlagsFromActions(state
+                        .getActions()));
+            }
+        }
+    }
+
+    private void onNewMediaMetadata(MediaMetadata metadata) {
+        if (metadata == null) {
+            // RemoteController only handles non-null metadata
+            return;
+        }
+        final OnClientUpdateListener l;
+        final MetadataEditor metadataEditor;
+        // prepare the received Bundle to be used inside a MetadataEditor
+        synchronized(mInfoLock) {
+            l = mOnClientUpdateListener;
+            boolean canRate = mCurrentSession != null
+                    && mCurrentSession.getRatingType() != Rating.RATING_NONE;
+            long editableKeys = canRate ? MediaMetadataEditor.RATING_KEY_BY_USER : 0;
+            Bundle legacyMetadata = MediaSessionLegacyHelper.getOldMetadata(metadata);
+            mMetadataEditor = new MetadataEditor(legacyMetadata, editableKeys);
+            metadataEditor = mMetadataEditor;
+        }
+        if (l != null) {
+            l.onClientMetadataUpdate(metadataEditor);
+        }
+    }
+
     //==================================================
     private static class PlaybackInfo {
         int mState;
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index 7c03907..f0cd785 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -37,6 +37,7 @@
     boolean isTransportControlEnabled();
     void showRoutePicker();
     MediaSessionInfo getSessionInfo();
+    long getFlags();
 
     // These commands are for the TransportController
     void play();
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 57a0a54..5ca7daa 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -57,6 +57,7 @@
     private final Object mLock = new Object();
 
     private boolean mCbRegistered = false;
+    private MediaSessionInfo mInfo;
 
     private TransportControls mTransportController;
 
@@ -174,6 +175,21 @@
     }
 
     /**
+     * Get the flags for this session.
+     *
+     * @return The current set of flags for the session.
+     * @hide
+     */
+    public long getFlags() {
+        try {
+            return mSessionBinder.getFlags();
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Error calling getFlags.", e);
+        }
+        return 0;
+    }
+
+    /**
      * Adds a callback to receive updates from the Session. Updates will be
      * posted on the caller's thread.
      *
@@ -253,12 +269,14 @@
      * @hide
      */
     public MediaSessionInfo getSessionInfo() {
-        try {
-            return mSessionBinder.getSessionInfo();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error in getSessionInfo.", e);
+        if (mInfo == null) {
+            try {
+                mInfo = mSessionBinder.getSessionInfo();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error in getSessionInfo.", e);
+            }
         }
-        return null;
+        return mInfo;
     }
 
     /*
diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java
index 099f601..801844f 100644
--- a/media/java/android/media/session/MediaSessionLegacyHelper.java
+++ b/media/java/android/media/session/MediaSessionLegacyHelper.java
@@ -20,6 +20,10 @@
 import android.app.PendingIntent.CanceledException;
 import android.content.Context;
 import android.content.Intent;
+import android.media.MediaMetadata;
+import android.media.MediaMetadataEditor;
+import android.media.MediaMetadataRetriever;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.util.ArrayMap;
@@ -64,6 +68,88 @@
         return sInstance;
     }
 
+    public static Bundle getOldMetadata(MediaMetadata metadata) {
+        Bundle oldMetadata = new Bundle();
+        if (metadata.containsKey(MediaMetadata.METADATA_KEY_ALBUM)) {
+            oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_ALBUM),
+                    metadata.getString(MediaMetadata.METADATA_KEY_ALBUM));
+        }
+        if (metadata.containsKey(MediaMetadata.METADATA_KEY_ART)) {
+            oldMetadata.putParcelable(String.valueOf(MediaMetadataEditor.BITMAP_KEY_ARTWORK),
+                    metadata.getBitmap(MediaMetadata.METADATA_KEY_ART));
+        } else if (metadata.containsKey(MediaMetadata.METADATA_KEY_ALBUM_ART)) {
+            // Fall back to album art if the track art wasn't available
+            oldMetadata.putParcelable(String.valueOf(MediaMetadataEditor.BITMAP_KEY_ARTWORK),
+                    metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART));
+        }
+        if (metadata.containsKey(MediaMetadata.METADATA_KEY_ALBUM_ARTIST)) {
+            oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST),
+                    metadata.getString(MediaMetadata.METADATA_KEY_ALBUM_ARTIST));
+        }
+        if (metadata.containsKey(MediaMetadata.METADATA_KEY_ARTIST)) {
+            oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_ARTIST),
+                    metadata.getString(MediaMetadata.METADATA_KEY_ARTIST));
+        }
+        if (metadata.containsKey(MediaMetadata.METADATA_KEY_AUTHOR)) {
+            oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_AUTHOR),
+                    metadata.getString(MediaMetadata.METADATA_KEY_AUTHOR));
+        }
+        if (metadata.containsKey(MediaMetadata.METADATA_KEY_COMPILATION)) {
+            oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_COMPILATION),
+                    metadata.getString(MediaMetadata.METADATA_KEY_COMPILATION));
+        }
+        if (metadata.containsKey(MediaMetadata.METADATA_KEY_COMPOSER)) {
+            oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_COMPOSER),
+                    metadata.getString(MediaMetadata.METADATA_KEY_COMPOSER));
+        }
+        if (metadata.containsKey(MediaMetadata.METADATA_KEY_DATE)) {
+            oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_DATE),
+                    metadata.getString(MediaMetadata.METADATA_KEY_DATE));
+        }
+        if (metadata.containsKey(MediaMetadata.METADATA_KEY_DISC_NUMBER)) {
+            oldMetadata.putLong(String.valueOf(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER),
+                    metadata.getLong(MediaMetadata.METADATA_KEY_DISC_NUMBER));
+        }
+        if (metadata.containsKey(MediaMetadata.METADATA_KEY_DURATION)) {
+            oldMetadata.putLong(String.valueOf(MediaMetadataRetriever.METADATA_KEY_DURATION),
+                    metadata.getLong(MediaMetadata.METADATA_KEY_DURATION));
+        }
+        if (metadata.containsKey(MediaMetadata.METADATA_KEY_GENRE)) {
+            oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_GENRE),
+                    metadata.getString(MediaMetadata.METADATA_KEY_GENRE));
+        }
+        if (metadata.containsKey(MediaMetadata.METADATA_KEY_NUM_TRACKS)) {
+            oldMetadata.putLong(String.valueOf(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS),
+                    metadata.getLong(MediaMetadata.METADATA_KEY_NUM_TRACKS));
+        }
+        if (metadata.containsKey(MediaMetadata.METADATA_KEY_RATING)) {
+            oldMetadata.putParcelable(String.valueOf(MediaMetadataEditor.RATING_KEY_BY_OTHERS),
+                    metadata.getRating(MediaMetadata.METADATA_KEY_RATING));
+        }
+        if (metadata.containsKey(MediaMetadata.METADATA_KEY_USER_RATING)) {
+            oldMetadata.putParcelable(String.valueOf(MediaMetadataEditor.RATING_KEY_BY_USER),
+                    metadata.getRating(MediaMetadata.METADATA_KEY_USER_RATING));
+        }
+        if (metadata.containsKey(MediaMetadata.METADATA_KEY_TITLE)) {
+            oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_TITLE),
+                    metadata.getString(MediaMetadata.METADATA_KEY_TITLE));
+        }
+        if (metadata.containsKey(MediaMetadata.METADATA_KEY_TRACK_NUMBER)) {
+            oldMetadata.putLong(
+                    String.valueOf(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER),
+                    metadata.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER));
+        }
+        if (metadata.containsKey(MediaMetadata.METADATA_KEY_WRITER)) {
+            oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_WRITER),
+                    metadata.getString(MediaMetadata.METADATA_KEY_WRITER));
+        }
+        if (metadata.containsKey(MediaMetadata.METADATA_KEY_YEAR)) {
+            oldMetadata.putString(String.valueOf(MediaMetadataRetriever.METADATA_KEY_YEAR),
+                    metadata.getString(MediaMetadata.METADATA_KEY_YEAR));
+        }
+        return oldMetadata;
+    }
+
     public MediaSession getSession(PendingIntent pi) {
         SessionHolder holder = mSessions.get(pi);
         return holder == null ? null : holder.mSession;
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 291bfc8..8eceee8 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -96,10 +96,13 @@
     }
 
     /**
-     * Get a list of controllers for all ongoing sessions. This requires the
-     * android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by
-     * the calling app. You may also retrieve this list if your app is an
-     * enabled notification listener using the
+     * Get a list of controllers for all ongoing sessions. The controllers will
+     * be provided in priority order with the most important controller at index
+     * 0.
+     * <p>
+     * This requires the android.Manifest.permission.MEDIA_CONTENT_CONTROL
+     * permission be held by the calling app. You may also retrieve this list if
+     * your app is an enabled notification listener using the
      * {@link NotificationListenerService} APIs, in which case you must pass the
      * {@link ComponentName} of your enabled listener.
      *
@@ -239,7 +242,8 @@
         /**
          * Called when the list of active sessions has changed. This can be due
          * to a session being added or removed or the order of sessions
-         * changing.
+         * changing. The controllers will be provided in priority order with the
+         * most important controller at index 0.
          *
          * @param controllers The updated list of controllers for the user that
          *            changed.
diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java
index e09ac3f..3b3f249 100644
--- a/media/java/android/media/session/PlaybackState.java
+++ b/media/java/android/media/session/PlaybackState.java
@@ -432,6 +432,8 @@
                 return STATE_REWINDING;
             case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
                 return STATE_SKIPPING_TO_PREVIOUS;
+            case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
+                return STATE_SKIPPING_TO_NEXT;
             case RemoteControlClient.PLAYSTATE_STOPPED:
                 return STATE_STOPPED;
             default:
@@ -440,6 +442,41 @@
     }
 
     /**
+     * Get the {@link RemoteControlClient} state for the given
+     * {@link PlaybackState} state.
+     *
+     * @param state The state used by {@link PlaybackState}.
+     * @return The equivalent state used by {@link RemoteControlClient}.
+     * @hide
+     */
+    public static int getRccStateFromState(int state) {
+        switch (state) {
+            case STATE_BUFFERING:
+                return RemoteControlClient.PLAYSTATE_BUFFERING;
+            case STATE_ERROR:
+                return RemoteControlClient.PLAYSTATE_ERROR;
+            case STATE_FAST_FORWARDING:
+                return RemoteControlClient.PLAYSTATE_FAST_FORWARDING;
+            case STATE_NONE:
+                return RemoteControlClient.PLAYSTATE_NONE;
+            case STATE_PAUSED:
+                return RemoteControlClient.PLAYSTATE_PAUSED;
+            case STATE_PLAYING:
+                return RemoteControlClient.PLAYSTATE_PLAYING;
+            case STATE_REWINDING:
+                return RemoteControlClient.PLAYSTATE_REWINDING;
+            case STATE_SKIPPING_TO_PREVIOUS:
+                return RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS;
+            case STATE_SKIPPING_TO_NEXT:
+                return RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS;
+            case STATE_STOPPED:
+                return RemoteControlClient.PLAYSTATE_STOPPED;
+            default:
+                return -1;
+        }
+    }
+
+    /**
      * @hide
      */
     public static long getActionsFromRccControlFlags(int rccFlags) {
@@ -454,6 +491,21 @@
         return actions;
     }
 
+    /**
+     * @hide
+     */
+    public static int getRccControlFlagsFromActions(long actions) {
+        int rccFlags = 0;
+        long action = 1;
+        while (action <= actions && action < Integer.MAX_VALUE) {
+            if ((action & actions) != 0) {
+                rccFlags |= getRccFlagForAction(action);
+            }
+            action = action << 1;
+        }
+        return rccFlags;
+    }
+
     private static long getActionForRccFlag(int flag) {
         switch (flag) {
             case RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS:
@@ -480,6 +532,35 @@
         return 0;
     }
 
+    private static int getRccFlagForAction(long action) {
+        // We only care about the lower set of actions that can map to rcc
+        // flags.
+        int testAction = action < Integer.MAX_VALUE ? (int) action : 0;
+        switch (testAction) {
+            case (int) ACTION_SKIP_TO_PREVIOUS:
+                return RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS;
+            case (int) ACTION_REWIND:
+                return RemoteControlClient.FLAG_KEY_MEDIA_REWIND;
+            case (int) ACTION_PLAY:
+                return RemoteControlClient.FLAG_KEY_MEDIA_PLAY;
+            case (int) ACTION_PLAY_PAUSE:
+                return RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE;
+            case (int) ACTION_PAUSE:
+                return RemoteControlClient.FLAG_KEY_MEDIA_PAUSE;
+            case (int) ACTION_STOP:
+                return RemoteControlClient.FLAG_KEY_MEDIA_STOP;
+            case (int) ACTION_FAST_FORWARD:
+                return RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD;
+            case (int) ACTION_SKIP_TO_NEXT:
+                return RemoteControlClient.FLAG_KEY_MEDIA_NEXT;
+            case (int) ACTION_SEEK_TO:
+                return RemoteControlClient.FLAG_KEY_MEDIA_POSITION_UPDATE;
+            case (int) ACTION_SET_RATING:
+                return RemoteControlClient.FLAG_KEY_MEDIA_RATING;
+        }
+        return 0;
+    }
+
     public static final Parcelable.Creator<PlaybackState> CREATOR
             = new Parcelable.Creator<PlaybackState>() {
         @Override
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 9087726..657d5ec 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -630,8 +630,8 @@
         if (DBG) log("ConnectivityService starting up");
 
         NetworkCapabilities netCap = new NetworkCapabilities();
-        netCap.addNetworkCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
-        netCap.addNetworkCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+        netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+        netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
         mDefaultRequest = new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId());
         NetworkRequestInfo nri = new NetworkRequestInfo(null, mDefaultRequest, new Binder(),
                 NetworkRequestInfo.REQUEST);
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 835b094..9ae8aed 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -949,6 +949,11 @@
         }
 
         @Override
+        public long getFlags() {
+            return mFlags;
+        }
+
+        @Override
         public void play() throws RemoteException {
             mSessionCb.play();
         }