merge in lmp-release history after reset to 573c7b48dcc2364b4c07917271c3c6e523b704df
diff --git a/Android.mk b/Android.mk
index 23c4753..8a50ae84 100644
--- a/Android.mk
+++ b/Android.mk
@@ -320,9 +320,12 @@
 	media/java/android/media/IRemoteVolumeObserver.aidl \
 	media/java/android/media/IRingtonePlayer.aidl \
 	media/java/android/media/IVolumeController.aidl \
-	media/java/android/media/routeprovider/IRouteConnection.aidl \
-	media/java/android/media/routeprovider/IRouteProvider.aidl \
-	media/java/android/media/routeprovider/IRouteProviderCallback.aidl \
+	media/java/android/media/routing/IMediaRouteService.aidl \
+	media/java/android/media/routing/IMediaRouteClientCallback.aidl \
+	media/java/android/media/routing/IMediaRouter.aidl \
+	media/java/android/media/routing/IMediaRouterDelegate.aidl \
+	media/java/android/media/routing/IMediaRouterRoutingCallback.aidl \
+	media/java/android/media/routing/IMediaRouterStateCallback.aidl \
 	media/java/android/media/session/IActiveSessionsListener.aidl \
 	media/java/android/media/session/ISessionController.aidl \
 	media/java/android/media/session/ISessionControllerCallback.aidl \
@@ -333,12 +336,11 @@
 	media/java/android/media/tv/ITvInputHardware.aidl \
 	media/java/android/media/tv/ITvInputHardwareCallback.aidl \
 	media/java/android/media/tv/ITvInputManager.aidl \
+	media/java/android/media/tv/ITvInputManagerCallback.aidl \
 	media/java/android/media/tv/ITvInputService.aidl \
 	media/java/android/media/tv/ITvInputServiceCallback.aidl \
 	media/java/android/media/tv/ITvInputSession.aidl \
 	media/java/android/media/tv/ITvInputSessionCallback.aidl \
-	telecomm/java/com/android/internal/telecomm/ICallServiceLookupResponse.aidl \
-	telecomm/java/com/android/internal/telecomm/ICallServiceProvider.aidl \
 	telecomm/java/com/android/internal/telecomm/ICallVideoProvider.aidl \
 	telecomm/java/com/android/internal/telecomm/ICallVideoClient.aidl \
 	telecomm/java/com/android/internal/telecomm/IConnectionService.aidl \
@@ -359,10 +361,6 @@
 	telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl \
 	telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl \
 	telephony/java/com/android/internal/telephony/ITelephony.aidl \
-	telephony/java/com/android/internal/telephony/IThirdPartyCallListener.aidl \
-	telephony/java/com/android/internal/telephony/IThirdPartyCallProvider.aidl \
-	telephony/java/com/android/internal/telephony/IThirdPartyCallSendDtmfCallback.aidl \
-	telephony/java/com/android/internal/telephony/IThirdPartyCallService.aidl \
 	telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl \
 	telephony/java/com/android/internal/telephony/ISms.aidl \
 	telephony/java/com/android/internal/telephony/IWapPushManager.aidl \
@@ -527,6 +525,7 @@
 	frameworks/base/location/java/com/android/internal/location/ProviderRequest.aidl \
 	frameworks/base/media/java/android/media/MediaMetadata.aidl \
 	frameworks/base/media/java/android/media/Rating.aidl \
+	frameworks/base/media/java/android/media/routing/MediaRouteSelector.aidl \
 	frameworks/base/media/java/android/media/session/MediaSession.aidl \
 	frameworks/base/media/java/android/media/session/PlaybackState.aidl \
 	frameworks/base/telephony/java/android/telephony/ServiceState.aidl \
diff --git a/api/current.txt b/api/current.txt
index d283360..8076d47 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -23,6 +23,7 @@
     field public static final java.lang.String BIND_DEVICE_ADMIN = "android.permission.BIND_DEVICE_ADMIN";
     field public static final java.lang.String BIND_DREAM_SERVICE = "android.permission.BIND_DREAM_SERVICE";
     field public static final java.lang.String BIND_INPUT_METHOD = "android.permission.BIND_INPUT_METHOD";
+    field public static final java.lang.String BIND_MEDIA_ROUTE_SERVICE = "android.permission.BIND_MEDIA_ROUTE_SERVICE";
     field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE";
     field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE";
     field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE";
@@ -1367,6 +1368,7 @@
     field public static final int windowContentTransitions = 16843794; // 0x1010412
     field public static final int windowDisablePreview = 16843298; // 0x1010222
     field public static final int windowDrawsSystemBarBackgrounds = 16843858; // 0x1010452
+    field public static final int windowElevation = 16843922; // 0x1010492
     field public static final int windowEnableSplitTouch = 16843543; // 0x1010317
     field public static final int windowEnterAnimation = 16842932; // 0x10100b4
     field public static final int windowEnterTransition = 16843833; // 0x1010439
@@ -3308,6 +3310,7 @@
     method public android.view.LayoutInflater getLayoutInflater();
     method public android.app.LoaderManager getLoaderManager();
     method public java.lang.String getLocalClassName();
+    method public final android.media.session.MediaController getMediaController();
     method public android.view.MenuInflater getMenuInflater();
     method public final android.app.Activity getParent();
     method public android.content.Intent getParentActivityIntent();
@@ -3431,6 +3434,7 @@
     method public void setFinishOnTouchOutside(boolean);
     method public void setImmersive(boolean);
     method public void setIntent(android.content.Intent);
+    method public final void setMediaController(android.media.session.MediaController);
     method public boolean setMediaPlaying(boolean);
     method public final void setProgress(int);
     method public final void setProgressBarIndeterminate(boolean);
@@ -3509,6 +3513,7 @@
     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.RunningTaskInfo> getRunningTasks(int) throws java.lang.SecurityException;
+    method public boolean isInLockTaskMode();
     method public boolean isLowRamDevice();
     method public static boolean isRunningInTestHarness();
     method public static boolean isUserAMonkey();
@@ -7057,6 +7062,7 @@
     method public abstract java.io.File getFileStreamPath(java.lang.String);
     method public abstract java.io.File getFilesDir();
     method public abstract android.os.Looper getMainLooper();
+    method public abstract java.io.File getNoBackupFilesDir();
     method public abstract java.io.File getObbDir();
     method public abstract java.io.File[] getObbDirs();
     method public abstract java.lang.String getPackageCodePath();
@@ -7226,6 +7232,7 @@
     method public java.io.File getFileStreamPath(java.lang.String);
     method public java.io.File getFilesDir();
     method public android.os.Looper getMainLooper();
+    method public java.io.File getNoBackupFilesDir();
     method public java.io.File getObbDir();
     method public java.io.File[] getObbDirs();
     method public java.lang.String getPackageCodePath();
@@ -8351,6 +8358,36 @@
     field public int reqGlEsVersion;
   }
 
+  public class InstallSessionInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public android.graphics.Bitmap getIcon();
+    method public java.lang.String getInstallerPackageName();
+    method public java.lang.String getPackageName();
+    method public int getProgress();
+    method public int getSessionId();
+    method public java.lang.CharSequence getTitle();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator CREATOR;
+  }
+
+  public class InstallSessionParams implements android.os.Parcelable {
+    ctor public InstallSessionParams();
+    method public int describeContents();
+    method public void setDeltaSize(long);
+    method public void setIcon(android.graphics.Bitmap);
+    method public void setInstallLocation(int);
+    method public void setModeFullInstall();
+    method public void setModeInheritExisting();
+    method public void setOriginatingUri(android.net.Uri);
+    method public void setPackageName(java.lang.String);
+    method public void setProgressMax(int);
+    method public void setReferrerUri(android.net.Uri);
+    method public void setSignatures(android.content.pm.Signature[]);
+    method public void setTitle(java.lang.CharSequence);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator CREATOR;
+  }
+
   public class InstrumentationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
     ctor public InstrumentationInfo();
     ctor public InstrumentationInfo(android.content.pm.InstrumentationInfo);
@@ -8366,6 +8403,9 @@
     field public java.lang.String targetPackage;
   }
 
+  public class KeySet {
+  }
+
   public class LabeledIntent extends android.content.Intent {
     ctor public LabeledIntent(android.content.Intent, java.lang.String, int, int);
     ctor public LabeledIntent(android.content.Intent, java.lang.String, java.lang.CharSequence, int);
@@ -8415,6 +8455,9 @@
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
+    field public static final int INSTALL_LOCATION_AUTO = 0; // 0x0
+    field public static final int INSTALL_LOCATION_INTERNAL_ONLY = 1; // 0x1
+    field public static final int INSTALL_LOCATION_PREFER_EXTERNAL = 2; // 0x2
     field public static final int REQUESTED_PERMISSION_GRANTED = 2; // 0x2
     field public static final int REQUESTED_PERMISSION_REQUIRED = 1; // 0x1
     field public android.content.pm.ActivityInfo[] activities;
@@ -8422,6 +8465,7 @@
     field public android.content.pm.ConfigurationInfo[] configPreferences;
     field public long firstInstallTime;
     field public int[] gids;
+    field public int installLocation;
     field public android.content.pm.InstrumentationInfo[] instrumentation;
     field public long lastUpdateTime;
     field public java.lang.String packageName;
@@ -8435,10 +8479,52 @@
     field public java.lang.String sharedUserId;
     field public int sharedUserLabel;
     field public android.content.pm.Signature[] signatures;
+    field public java.lang.String[] splitNames;
     field public int versionCode;
     field public java.lang.String versionName;
   }
 
+  public class PackageInstaller {
+    method public int createSession(android.content.pm.InstallSessionParams) throws java.io.IOException;
+    method public java.util.List<android.content.pm.InstallSessionInfo> getActiveSessions();
+    method public android.content.pm.PackageInstaller.Session openSession(int);
+    method public void registerSessionObserver(android.content.pm.PackageInstaller.SessionObserver);
+    method public void uninstall(java.lang.String, android.content.pm.PackageInstaller.UninstallResultCallback);
+    method public void unregisterSessionObserver(android.content.pm.PackageInstaller.SessionObserver);
+  }
+
+  public static abstract class PackageInstaller.CommitResultCallback {
+    ctor public PackageInstaller.CommitResultCallback();
+    method public abstract void onFailure(java.lang.String);
+    method public void onFailureConflict(java.lang.String, java.lang.String);
+    method public void onFailureIncompatible(java.lang.String);
+    method public void onFailureInvalid(java.lang.String);
+    method public void onFailureStorage(java.lang.String);
+    method public abstract void onSuccess();
+  }
+
+  public static class PackageInstaller.Session implements java.io.Closeable {
+    method public void close();
+    method public void commit(android.content.pm.PackageInstaller.CommitResultCallback);
+    method public void destroy();
+    method public void fsync(java.io.OutputStream) throws java.io.IOException;
+    method public java.io.OutputStream openWrite(java.lang.String, long, long) throws java.io.IOException;
+    method public void setProgress(int);
+  }
+
+  public static abstract class PackageInstaller.SessionObserver {
+    ctor public PackageInstaller.SessionObserver();
+    method public abstract void onCreated(android.content.pm.InstallSessionInfo);
+    method public abstract void onFinalized(int, boolean);
+    method public abstract void onProgress(int, int);
+  }
+
+  public static abstract class PackageInstaller.UninstallResultCallback {
+    ctor public PackageInstaller.UninstallResultCallback();
+    method public abstract void onFailure(java.lang.String);
+    method public abstract void onSuccess();
+  }
+
   public class PackageItemInfo {
     ctor public PackageItemInfo();
     ctor public PackageItemInfo(android.content.pm.PackageItemInfo);
@@ -8501,8 +8587,10 @@
     method public abstract android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
     method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
+    method public abstract android.content.pm.PackageInstaller getInstaller();
     method public abstract java.lang.String getInstallerPackageName(java.lang.String);
     method public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public abstract android.content.pm.KeySet getKeySetByAlias(java.lang.String, java.lang.String);
     method public abstract android.content.Intent getLaunchIntentForPackage(java.lang.String);
     method public abstract android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
     method public abstract java.lang.String getNameForUid(int);
@@ -8521,12 +8609,15 @@
     method public abstract android.content.res.Resources getResourcesForApplication(android.content.pm.ApplicationInfo) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.res.Resources getResourcesForApplication(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.pm.ServiceInfo getServiceInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public abstract android.content.pm.KeySet getSigningKeySet(java.lang.String);
     method public abstract android.content.pm.FeatureInfo[] getSystemAvailableFeatures();
     method public abstract java.lang.String[] getSystemSharedLibraryNames();
     method public abstract java.lang.CharSequence getText(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public abstract boolean hasSystemFeature(java.lang.String);
     method public abstract boolean isSafeMode();
+    method public abstract boolean isSignedBy(java.lang.String, android.content.pm.KeySet);
+    method public abstract boolean isSignedByExactly(java.lang.String, android.content.pm.KeySet);
     method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
     method public abstract java.util.List<android.content.pm.ProviderInfo> queryContentProviders(java.lang.String, int, int);
     method public abstract java.util.List<android.content.pm.InstrumentationInfo> queryInstrumentation(java.lang.String, int);
@@ -8583,12 +8674,14 @@
     field public static final java.lang.String FEATURE_SCREEN_LANDSCAPE = "android.hardware.screen.landscape";
     field public static final java.lang.String FEATURE_SCREEN_PORTRAIT = "android.hardware.screen.portrait";
     field public static final java.lang.String FEATURE_SENSOR_ACCELEROMETER = "android.hardware.sensor.accelerometer";
+    field public static final java.lang.String FEATURE_SENSOR_AMBIENT_TEMPERATURE = "android.hardware.sensor.ambient_temperature";
     field public static final java.lang.String FEATURE_SENSOR_BAROMETER = "android.hardware.sensor.barometer";
     field public static final java.lang.String FEATURE_SENSOR_COMPASS = "android.hardware.sensor.compass";
     field public static final java.lang.String FEATURE_SENSOR_GYROSCOPE = "android.hardware.sensor.gyroscope";
     field public static final java.lang.String FEATURE_SENSOR_HEART_RATE = "android.hardware.sensor.heartrate";
     field public static final java.lang.String FEATURE_SENSOR_LIGHT = "android.hardware.sensor.light";
     field public static final java.lang.String FEATURE_SENSOR_PROXIMITY = "android.hardware.sensor.proximity";
+    field public static final java.lang.String FEATURE_SENSOR_RELATIVE_HUMIDITY = "android.hardware.sensor.relative_humidity";
     field public static final java.lang.String FEATURE_SENSOR_STEP_COUNTER = "android.hardware.sensor.stepcounter";
     field public static final java.lang.String FEATURE_SENSOR_STEP_DETECTOR = "android.hardware.sensor.stepdetector";
     field public static final java.lang.String FEATURE_SIP = "android.software.sip";
@@ -15871,12 +15964,249 @@
 
 }
 
+package android.media.routing {
+
+  public final class MediaRouteSelector implements android.os.Parcelable {
+    method public boolean containsProtocol(java.lang.Class<?>);
+    method public boolean containsProtocol(java.lang.String);
+    method public int describeContents();
+    method public android.os.Bundle getExtras();
+    method public int getOptionalFeatures();
+    method public java.util.List<java.lang.String> getOptionalProtocols();
+    method public int getRequiredFeatures();
+    method public java.util.List<java.lang.String> getRequiredProtocols();
+    method public java.lang.String getServicePackageName();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator CREATOR;
+  }
+
+  public static final class MediaRouteSelector.Builder {
+    ctor public MediaRouteSelector.Builder();
+    method public android.media.routing.MediaRouteSelector.Builder addOptionalProtocol(java.lang.Class<?>);
+    method public android.media.routing.MediaRouteSelector.Builder addOptionalProtocol(java.lang.String);
+    method public android.media.routing.MediaRouteSelector.Builder addRequiredProtocol(java.lang.Class<?>);
+    method public android.media.routing.MediaRouteSelector.Builder addRequiredProtocol(java.lang.String);
+    method public android.media.routing.MediaRouteSelector build();
+    method public android.media.routing.MediaRouteSelector.Builder setExtras(android.os.Bundle);
+    method public android.media.routing.MediaRouteSelector.Builder setOptionalFeatures(int);
+    method public android.media.routing.MediaRouteSelector.Builder setRequiredFeatures(int);
+    method public android.media.routing.MediaRouteSelector.Builder setServicePackageName(java.lang.String);
+  }
+
+  public abstract class MediaRouteService extends android.app.Service {
+    ctor public MediaRouteService();
+    method public android.media.routing.MediaRouter.ServiceMetadata getServiceMetadata();
+    method public android.os.IBinder onBind(android.content.Intent);
+    method public abstract android.media.routing.MediaRouteService.ClientSession onCreateClientSession(android.media.routing.MediaRouteService.ClientInfo);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.media.routing.MediaRouteService";
+  }
+
+  public static final class MediaRouteService.ClientInfo {
+    method public java.lang.String getPackageName();
+    method public int getUid();
+  }
+
+  public static abstract class MediaRouteService.ClientSession {
+    ctor public MediaRouteService.ClientSession();
+    method public abstract boolean onConnect(android.media.routing.MediaRouter.ConnectionRequest, android.media.routing.MediaRouteService.ConnectionCallback);
+    method public abstract void onDisconnect();
+    method public void onPauseStream();
+    method public void onRelease();
+    method public void onResumeStream();
+    method public abstract boolean onStartDiscovery(android.media.routing.MediaRouter.DiscoveryRequest, android.media.routing.MediaRouteService.DiscoveryCallback);
+    method public abstract void onStopDiscovery();
+  }
+
+  public final class MediaRouteService.ConnectionCallback {
+    method public void onConnected(android.media.routing.MediaRouter.ConnectionInfo);
+    method public void onConnectionFailed(int, java.lang.CharSequence, android.os.Bundle);
+    method public void onDisconnected();
+  }
+
+  public final class MediaRouteService.DiscoveryCallback {
+    method public void onDestinationFound(android.media.routing.MediaRouter.DestinationInfo, java.util.List<android.media.routing.MediaRouter.RouteInfo>);
+    method public void onDestinationLost(android.media.routing.MediaRouter.DestinationInfo);
+    method public void onDiscoveryFailed(int, java.lang.CharSequence, android.os.Bundle);
+  }
+
+  public final class MediaRouter {
+    ctor public MediaRouter(android.content.Context);
+    method public void addSelector(android.media.routing.MediaRouteSelector);
+    method public void clearSelectors();
+    method public android.media.routing.MediaRouter.Delegate createDelegate();
+    method public android.media.routing.MediaRouter.ConnectionInfo getConnection();
+    method public int getConnectionState();
+    method public java.util.List<android.media.routing.MediaRouter.DestinationInfo> getDiscoveredDestinations();
+    method public java.util.List<android.media.routing.MediaRouter.RouteInfo> getDiscoveredRoutes(android.media.routing.MediaRouter.DestinationInfo);
+    method public int getDiscoveryState();
+    method public android.media.AudioAttributes getPreferredAudioAttributes();
+    method public android.view.Display getPreferredPresentationDisplay();
+    method public android.media.VolumeProvider getPreferredVolumeProvider();
+    method public android.media.routing.MediaRouter.DestinationInfo getSelectedDestination();
+    method public android.media.routing.MediaRouter.RouteInfo getSelectedRoute();
+    method public java.util.List<android.media.routing.MediaRouteSelector> getSelectors();
+    method public boolean isReleased();
+    method public void pauseStream();
+    method public void release();
+    method public void removeSelector(android.media.routing.MediaRouteSelector);
+    method public void resumeStream();
+    method public void setRoutingCallback(android.media.routing.MediaRouter.RoutingCallback, android.os.Handler);
+    field public static final int CONNECTION_ERROR_ABORTED = 1; // 0x1
+    field public static final int CONNECTION_ERROR_BARGED = 7; // 0x7
+    field public static final int CONNECTION_ERROR_BROKEN = 6; // 0x6
+    field public static final int CONNECTION_ERROR_BUSY = 4; // 0x4
+    field public static final int CONNECTION_ERROR_TIMEOUT = 5; // 0x5
+    field public static final int CONNECTION_ERROR_UNAUTHORIZED = 2; // 0x2
+    field public static final int CONNECTION_ERROR_UNKNOWN = 0; // 0x0
+    field public static final int CONNECTION_ERROR_UNREACHABLE = 3; // 0x3
+    field public static final int CONNECTION_FLAG_BARGE = 1; // 0x1
+    field public static final int CONNECTION_STATE_CONNECTED = 2; // 0x2
+    field public static final int CONNECTION_STATE_CONNECTING = 1; // 0x1
+    field public static final int CONNECTION_STATE_DISCONNECTED = 0; // 0x0
+    field public static final int DISCONNECTION_REASON_APPLICATION_REQUEST = 0; // 0x0
+    field public static final int DISCONNECTION_REASON_ERROR = 2; // 0x2
+    field public static final int DISCONNECTION_REASON_USER_REQUEST = 1; // 0x1
+    field public static final int DISCOVERY_ERROR_ABORTED = 1; // 0x1
+    field public static final int DISCOVERY_ERROR_NO_CONNECTIVITY = 2; // 0x2
+    field public static final int DISCOVERY_ERROR_UNKNOWN = 0; // 0x0
+    field public static final int DISCOVERY_FLAG_BACKGROUND = 1; // 0x1
+    field public static final int DISCOVERY_STATE_STARTED = 1; // 0x1
+    field public static final int DISCOVERY_STATE_STOPPED = 0; // 0x0
+    field public static final int ROUTE_FEATURE_LIVE_AUDIO = 1; // 0x1
+    field public static final int ROUTE_FEATURE_LIVE_VIDEO = 2; // 0x2
+  }
+
+  public static final class MediaRouter.ConnectionInfo {
+    method public android.media.AudioAttributes getAudioAttributes();
+    method public android.os.Bundle getExtras();
+    method public int getFeatures();
+    method public android.view.Display getPresentationDisplay();
+    method public android.os.IBinder getProtocolBinder(java.lang.String);
+    method public android.os.IBinder getProtocolBinder(int);
+    method public T getProtocolObject(java.lang.Class<T>);
+    method public java.util.List<java.lang.String> getProtocols();
+    method public android.media.routing.MediaRouter.RouteInfo getRoute();
+    method public android.media.VolumeProvider getVolumeProvider();
+  }
+
+  public static final class MediaRouter.ConnectionInfo.Builder {
+    ctor public MediaRouter.ConnectionInfo.Builder(android.media.routing.MediaRouter.RouteInfo);
+    method public android.media.routing.MediaRouter.ConnectionInfo build();
+    method public android.media.routing.MediaRouter.ConnectionInfo.Builder setAudioAttributes(android.media.AudioAttributes);
+    method public android.media.routing.MediaRouter.ConnectionInfo.Builder setExtras(android.os.Bundle);
+    method public android.media.routing.MediaRouter.ConnectionInfo.Builder setPresentationDisplay(android.view.Display);
+    method public android.media.routing.MediaRouter.ConnectionInfo.Builder setProtocolBinder(java.lang.String, android.os.IBinder);
+    method public android.media.routing.MediaRouter.ConnectionInfo.Builder setProtocolStub(java.lang.Class<?>, android.os.IInterface);
+    method public android.media.routing.MediaRouter.ConnectionInfo.Builder setVolumeProvider(android.media.VolumeProvider);
+  }
+
+  public static final class MediaRouter.ConnectionRequest {
+    method public android.os.Bundle getExtras();
+    method public int getFlags();
+    method public android.media.routing.MediaRouter.RouteInfo getRoute();
+    method public void setExtras(android.os.Bundle);
+    method public void setFlags(int);
+    method public void setRoute(android.media.routing.MediaRouter.RouteInfo);
+  }
+
+  public static final class MediaRouter.Delegate {
+    ctor public MediaRouter.Delegate();
+    method public void addStateCallback(android.media.routing.MediaRouter.StateCallback, android.os.Handler);
+    method public void connect(android.media.routing.MediaRouter.DestinationInfo, int);
+    method public void disconnect(int);
+    method public int getConnectionState();
+    method public java.util.List<android.media.routing.MediaRouter.DestinationInfo> getDiscoveredDestinations();
+    method public int getDiscoveryState();
+    method public android.media.routing.MediaRouter.DestinationInfo getSelectedDestination();
+    method public boolean isReleased();
+    method public void removeStateCallback(android.media.routing.MediaRouter.StateCallback);
+    method public void startDiscovery(int);
+    method public void stopDiscovery();
+  }
+
+  public static final class MediaRouter.DestinationInfo {
+    method public java.lang.CharSequence getDescription();
+    method public android.os.Bundle getExtras();
+    method public int getIconResourceId();
+    method public java.lang.String getId();
+    method public java.lang.CharSequence getName();
+    method public android.media.routing.MediaRouter.ServiceMetadata getServiceMetadata();
+    method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
+  }
+
+  public static final class MediaRouter.DestinationInfo.Builder {
+    ctor public MediaRouter.DestinationInfo.Builder(java.lang.String, android.media.routing.MediaRouter.ServiceMetadata, java.lang.CharSequence);
+    method public android.media.routing.MediaRouter.DestinationInfo build();
+    method public android.media.routing.MediaRouter.DestinationInfo.Builder setDescription(java.lang.CharSequence);
+    method public android.media.routing.MediaRouter.DestinationInfo.Builder setExtras(android.os.Bundle);
+    method public android.media.routing.MediaRouter.DestinationInfo.Builder setIconResourceId(int);
+  }
+
+  public static final class MediaRouter.DiscoveryRequest {
+    method public int getFlags();
+    method public java.util.List<android.media.routing.MediaRouteSelector> getSelectors();
+    method public void setFlags(int);
+    method public void setSelectors(java.util.List<android.media.routing.MediaRouteSelector>);
+  }
+
+  public static final class MediaRouter.RouteInfo {
+    method public android.media.routing.MediaRouter.DestinationInfo getDestination();
+    method public android.os.Bundle getExtras();
+    method public int getFeatures();
+    method public java.lang.String getId();
+    method public java.util.List<java.lang.String> getProtocols();
+    method public android.media.routing.MediaRouteSelector getSelector();
+  }
+
+  public static final class MediaRouter.RouteInfo.Builder {
+    ctor public MediaRouter.RouteInfo.Builder(java.lang.String, android.media.routing.MediaRouter.DestinationInfo, android.media.routing.MediaRouteSelector);
+    method public android.media.routing.MediaRouter.RouteInfo.Builder addProtocol(java.lang.Class<T>);
+    method public android.media.routing.MediaRouter.RouteInfo.Builder addProtocol(java.lang.String);
+    method public android.media.routing.MediaRouter.RouteInfo build();
+    method public android.media.routing.MediaRouter.RouteInfo.Builder setExtras(android.os.Bundle);
+    method public android.media.routing.MediaRouter.RouteInfo.Builder setFeatures(int);
+  }
+
+  public static abstract class MediaRouter.RoutingCallback extends android.media.routing.MediaRouter.StateCallback {
+    ctor public MediaRouter.RoutingCallback();
+    method public boolean onPrepareConnectionRequest(android.media.routing.MediaRouter.ConnectionRequest, android.media.routing.MediaRouter.DestinationInfo, java.util.List<android.media.routing.MediaRouter.RouteInfo>);
+    method public boolean onPrepareDiscoveryRequest(android.media.routing.MediaRouter.DiscoveryRequest, java.util.List<android.media.routing.MediaRouteSelector>);
+  }
+
+  public static final class MediaRouter.ServiceMetadata {
+    method public android.content.ComponentName getComponentName();
+    method public android.graphics.drawable.Drawable getIcon(android.content.pm.PackageManager);
+    method public java.lang.CharSequence getLabel(android.content.pm.PackageManager);
+    method public java.lang.String getPackageName();
+    method public android.content.pm.ServiceInfo getService();
+  }
+
+  public static abstract class MediaRouter.StateCallback {
+    ctor public MediaRouter.StateCallback();
+    method public void onConnected();
+    method public void onConnecting();
+    method public void onConnectionFailed(int, java.lang.CharSequence, android.os.Bundle);
+    method public void onConnectionStateChanged(int);
+    method public void onDestinationFound(android.media.routing.MediaRouter.DestinationInfo);
+    method public void onDestinationLost(android.media.routing.MediaRouter.DestinationInfo);
+    method public void onDisconnected();
+    method public void onDiscoveryFailed(int, java.lang.CharSequence, android.os.Bundle);
+    method public void onDiscoveryStarted();
+    method public void onDiscoveryStateChanged(int);
+    method public void onDiscoveryStopped();
+    method public void onReleased();
+    method public void onSelectedDestinationChanged(android.media.routing.MediaRouter.DestinationInfo);
+  }
+
+}
+
 package android.media.session {
 
   public final class MediaController {
     method public void addCallback(android.media.session.MediaController.Callback);
     method public void addCallback(android.media.session.MediaController.Callback, android.os.Handler);
     method public void adjustVolumeBy(int, int);
+    method public android.media.routing.MediaRouter.Delegate createMediaRouterDelegate();
     method public boolean dispatchMediaButtonEvent(android.view.KeyEvent);
     method public static android.media.session.MediaController fromToken(android.media.session.MediaSession.Token);
     method public android.media.MediaMetadata getMetadata();
@@ -15931,6 +16261,7 @@
     method public void setActive(boolean);
     method public void setFlags(int);
     method public void setLaunchPendingIntent(android.app.PendingIntent);
+    method public void setMediaRouter(android.media.routing.MediaRouter);
     method public void setMetadata(android.media.MediaMetadata);
     method public void setPlaybackState(android.media.session.PlaybackState);
     method public void setPlaybackToLocal(int);
@@ -15978,19 +16309,14 @@
   }
 
   public final class PlaybackState implements android.os.Parcelable {
-    ctor public PlaybackState();
-    ctor public PlaybackState(android.media.session.PlaybackState);
     method public int describeContents();
     method public long getActions();
     method public long getBufferPosition();
     method public java.lang.CharSequence getErrorMessage();
-    method public float getPlaybackRate();
+    method public long getLastPositionUpdateTime();
+    method public float getPlaybackSpeed();
     method public long getPosition();
     method public int getState();
-    method public void setActions(long);
-    method public void setBufferPosition(long);
-    method public void setErrorMessage(java.lang.CharSequence);
-    method public void setState(int, long, float);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final long ACTION_FAST_FORWARD = 64L; // 0x40L
     field public static final long ACTION_PAUSE = 2L; // 0x2L
@@ -16005,6 +16331,7 @@
     field public static final android.os.Parcelable.Creator CREATOR;
     field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL
     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
     field public static final int STATE_FAST_FORWARDING = 4; // 0x4
     field public static final int STATE_NONE = 0; // 0x0
@@ -16016,6 +16343,17 @@
     field public static final int STATE_STOPPED = 1; // 0x1
   }
 
+  public static final class PlaybackState.Builder {
+    ctor public PlaybackState.Builder();
+    ctor public PlaybackState.Builder(android.media.session.PlaybackState);
+    method public android.media.session.PlaybackState build();
+    method public android.media.session.PlaybackState.Builder setActions(long);
+    method public android.media.session.PlaybackState.Builder setBufferPosition(long);
+    method public android.media.session.PlaybackState.Builder setErrorMessage(java.lang.CharSequence);
+    method public android.media.session.PlaybackState.Builder setState(int, long, float, long);
+    method public android.media.session.PlaybackState.Builder setState(int, long, float);
+  }
+
 }
 
 package android.media.tv {
@@ -16164,8 +16502,13 @@
   }
 
   public final class TvInputManager {
-    method public boolean getAvailability(java.lang.String);
+    method public int getInputState(java.lang.String);
     method public java.util.List<android.media.tv.TvInputInfo> getTvInputList();
+    method public void registerListener(android.media.tv.TvInputManager.TvInputListener, android.os.Handler);
+    method public void unregisterListener(android.media.tv.TvInputManager.TvInputListener);
+    field public static final int INPUT_STATE_CONNECTED = 0; // 0x0
+    field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1
+    field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2
     field public static final int VIDEO_UNAVAILABLE_REASON_BUFFERING = 3; // 0x3
     field public static final int VIDEO_UNAVAILABLE_REASON_TUNE = 1; // 0x1
     field public static final int VIDEO_UNAVAILABLE_REASON_UNKNOWN = 0; // 0x0
@@ -16174,7 +16517,7 @@
 
   public static abstract class TvInputManager.TvInputListener {
     ctor public TvInputManager.TvInputListener();
-    method public void onAvailabilityChanged(java.lang.String, boolean);
+    method public void onInputStateChanged(java.lang.String, int);
   }
 
   public abstract class TvInputService extends android.app.Service {
@@ -16268,8 +16611,8 @@
     method public void onChannelRetuned(java.lang.String, android.net.Uri);
     method public void onError(java.lang.String, int);
     method public void onTrackInfoChanged(java.lang.String, java.util.List<android.media.tv.TvTrackInfo>);
-    method public void onVideoSizeChanged(java.lang.String, int, int);
     method public void onVideoAvailable(java.lang.String);
+    method public void onVideoSizeChanged(java.lang.String, int, int);
     method public void onVideoUnavailable(java.lang.String, int);
   }
 
@@ -22696,7 +23039,7 @@
     method protected void onDisconnected();
     method protected abstract void onPrintJobQueued(android.printservice.PrintJob);
     method protected abstract void onRequestCancelPrintJob(android.printservice.PrintJob);
-    field public static final java.lang.String EXTRA_PRINTER_INFO = "android.intent.extra.print.PRINTER_INFO";
+    field public static final java.lang.String EXTRA_PRINTER_INFO = "android.intent.extra.print.EXTRA_PRINTER_INFO";
     field public static final java.lang.String EXTRA_PRINT_JOB_INFO = "android.intent.extra.print.PRINT_JOB_INFO";
     field public static final java.lang.String SERVICE_INTERFACE = "android.printservice.PrintService";
     field public static final java.lang.String SERVICE_META_DATA = "android.printservice";
@@ -24659,11 +25002,13 @@
     field public static final java.lang.String ACTION_APPLICATION_SETTINGS = "android.settings.APPLICATION_SETTINGS";
     field public static final java.lang.String ACTION_BLUETOOTH_SETTINGS = "android.settings.BLUETOOTH_SETTINGS";
     field public static final java.lang.String ACTION_CAPTIONING_SETTINGS = "android.settings.CAPTIONING_SETTINGS";
+    field public static final java.lang.String ACTION_CAST_SETTINGS = "android.settings.CAST_SETTINGS";
     field public static final java.lang.String ACTION_DATA_ROAMING_SETTINGS = "android.settings.DATA_ROAMING_SETTINGS";
     field public static final java.lang.String ACTION_DATE_SETTINGS = "android.settings.DATE_SETTINGS";
     field public static final java.lang.String ACTION_DEVICE_INFO_SETTINGS = "android.settings.DEVICE_INFO_SETTINGS";
     field public static final java.lang.String ACTION_DISPLAY_SETTINGS = "android.settings.DISPLAY_SETTINGS";
     field public static final java.lang.String ACTION_DREAM_SETTINGS = "android.settings.DREAM_SETTINGS";
+    field public static final java.lang.String ACTION_HOME_SETTINGS = "android.settings.HOME_SETTINGS";
     field public static final java.lang.String ACTION_INPUT_METHOD_SETTINGS = "android.settings.INPUT_METHOD_SETTINGS";
     field public static final java.lang.String ACTION_INPUT_METHOD_SUBTYPE_SETTINGS = "android.settings.INPUT_METHOD_SUBTYPE_SETTINGS";
     field public static final java.lang.String ACTION_INTERNAL_STORAGE_SETTINGS = "android.settings.INTERNAL_STORAGE_SETTINGS";
@@ -25340,6 +25685,7 @@
     field public static final java.lang.String NUMBER = "number";
     field public static final java.lang.String SOURCE_DATA = "source_data";
     field public static final java.lang.String SOURCE_PACKAGE = "source_package";
+    field public static final java.lang.String TRANSCRIPTION = "transcription";
   }
 
 }
@@ -27593,6 +27939,62 @@
 
 package android.telecomm {
 
+  public final class Call {
+    method public void addListener(android.telecomm.Call.Listener);
+    method public void answer();
+    method public void conference();
+    method public void disconnect();
+    method public android.telecomm.RemoteCallVideoProvider getCallVideoProvider();
+    method public java.util.List<java.lang.String> getCannedTextResponses();
+    method public java.util.List<android.telecomm.Call> getChildren();
+    method public android.telecomm.Call.Details getDetails();
+    method public android.telecomm.Call getParent();
+    method public java.lang.String getRemainingPostDialSequence();
+    method public int getState();
+    method public void hold();
+    method public void phoneAccountClicked();
+    method public void playDtmfTone(char);
+    method public void postDialContinue(boolean);
+    method public void reject(boolean, java.lang.String);
+    method public void removeListener(android.telecomm.Call.Listener);
+    method public void splitFromConference();
+    method public void stopDtmfTone();
+    method public void swapWithBackgroundCall();
+    method public void unhold();
+    field public static final int STATE_ACTIVE = 4; // 0x4
+    field public static final int STATE_DIALING = 1; // 0x1
+    field public static final int STATE_DISCONNECTED = 7; // 0x7
+    field public static final int STATE_HOLDING = 3; // 0x3
+    field public static final int STATE_NEW = 0; // 0x0
+    field public static final int STATE_RINGING = 2; // 0x2
+  }
+
+  public static class Call.Details {
+    method public android.telecomm.PhoneAccount getAccount();
+    method public java.lang.String getCallerDisplayName();
+    method public int getCallerDisplayNamePresentation();
+    method public int getCapabilities();
+    method public long getConnectTimeMillis();
+    method public int getDisconnectCauseCode();
+    method public java.lang.String getDisconnectCauseMsg();
+    method public android.telecomm.GatewayInfo getGatewayInfo();
+    method public android.net.Uri getHandle();
+    method public int getHandlePresentation();
+  }
+
+  public static abstract class Call.Listener {
+    ctor public Call.Listener();
+    method public void onCallDestroyed(android.telecomm.Call);
+    method public void onCallVideoProviderChanged(android.telecomm.Call, android.telecomm.RemoteCallVideoProvider);
+    method public void onCannedTextResponsesLoaded(android.telecomm.Call, java.util.List<java.lang.String>);
+    method public void onChildrenChanged(android.telecomm.Call, java.util.List<android.telecomm.Call>);
+    method public void onDetailsChanged(android.telecomm.Call, android.telecomm.Call.Details);
+    method public void onParentChanged(android.telecomm.Call, android.telecomm.Call);
+    method public void onPostDial(android.telecomm.Call, java.lang.String);
+    method public void onPostDialWait(android.telecomm.Call, java.lang.String);
+    method public void onStateChanged(android.telecomm.Call, int);
+  }
+
   public final class CallAudioState implements android.os.Parcelable {
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
@@ -27636,39 +28038,10 @@
 
   public class CallPropertyPresentation {
     ctor public CallPropertyPresentation();
-    field public static final int ALLOWED = 0; // 0x0
-    field public static final int PAYPHONE = 3; // 0x3
-    field public static final int RESTRICTED = 1; // 0x1
-    field public static final int UNKNOWN = 2; // 0x2
-  }
-
-  public final class CallServiceDescriptor implements android.os.Parcelable {
-    method public int describeContents();
-    method public java.lang.String getConnectionServiceId();
-    method public int getNetworkType();
-    method public android.content.ComponentName getServiceComponent();
-    method public static android.telecomm.CallServiceDescriptor.Builder newBuilder(android.content.Context);
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator CREATOR;
-    field public static final int FLAG_MOBILE = 4; // 0x4
-    field public static final int FLAG_PSTN = 2; // 0x2
-    field public static final int FLAG_WIFI = 1; // 0x1
-  }
-
-  public static class CallServiceDescriptor.Builder {
-    method public android.telecomm.CallServiceDescriptor build();
-    method public android.telecomm.CallServiceDescriptor.Builder setConnectionService(java.lang.Class<? extends android.telecomm.ConnectionService>);
-    method public android.telecomm.CallServiceDescriptor.Builder setNetworkType(int);
-  }
-
-  public final class CallServiceLookupResponse {
-    method public void setCallServiceDescriptors(java.util.List<android.telecomm.CallServiceDescriptor>);
-  }
-
-  public abstract class CallServiceProvider extends android.app.Service {
-    ctor protected CallServiceProvider();
-    method public abstract void lookupCallServices(android.telecomm.CallServiceLookupResponse);
-    method public android.os.IBinder onBind(android.content.Intent);
+    field public static final int ALLOWED = 1; // 0x1
+    field public static final int PAYPHONE = 4; // 0x4
+    field public static final int RESTRICTED = 2; // 0x2
+    field public static final int UNKNOWN = 3; // 0x3
   }
 
   public final class CallState extends java.lang.Enum {
@@ -27679,8 +28052,6 @@
     enum_constant public static final android.telecomm.CallState DISCONNECTED;
     enum_constant public static final android.telecomm.CallState NEW;
     enum_constant public static final android.telecomm.CallState ON_HOLD;
-    enum_constant public static final android.telecomm.CallState POST_DIAL;
-    enum_constant public static final android.telecomm.CallState POST_DIAL_WAIT;
     enum_constant public static final android.telecomm.CallState RINGING;
   }
 
@@ -27790,7 +28161,8 @@
 
   public abstract class ConnectionService extends android.app.Service {
     ctor public ConnectionService();
-    method public final void createRemoteOutgoingConnection(android.telecomm.ConnectionRequest, android.telecomm.ConnectionService.OutgoingCallResponse<android.telecomm.RemoteConnection>);
+    method public final void createRemoteIncomingConnection(android.telecomm.ConnectionRequest, android.telecomm.ConnectionService.CreateConnectionResponse<android.telecomm.RemoteConnection>);
+    method public final void createRemoteOutgoingConnection(android.telecomm.ConnectionRequest, android.telecomm.ConnectionService.CreateConnectionResponse<android.telecomm.RemoteConnection>);
     method public final java.util.Collection<android.telecomm.Connection> getAllConnections();
     method public final void lookupRemoteAccounts(android.net.Uri, android.telecomm.SimpleResponse<android.net.Uri, java.util.List<android.telecomm.PhoneAccount>>);
     method public final void maybeRespondToAccountLookup();
@@ -27798,11 +28170,11 @@
     method protected void onConnectionAdded(android.telecomm.Connection);
     method protected void onConnectionRemoved(android.telecomm.Connection);
     method protected void onCreateConferenceConnection(java.lang.String, android.telecomm.Connection, android.telecomm.Response<java.lang.String, android.telecomm.Connection>);
-    method protected void onCreateConnections(android.telecomm.ConnectionRequest, android.telecomm.ConnectionService.OutgoingCallResponse<android.telecomm.Connection>);
-    method protected void onCreateIncomingConnection(android.telecomm.ConnectionRequest, android.telecomm.Response<android.telecomm.ConnectionRequest, android.telecomm.Connection>);
+    method protected void onCreateIncomingConnection(android.telecomm.ConnectionRequest, android.telecomm.ConnectionService.CreateConnectionResponse<android.telecomm.Connection>);
+    method protected void onCreateOutgoingConnection(android.telecomm.ConnectionRequest, android.telecomm.ConnectionService.CreateConnectionResponse<android.telecomm.Connection>);
   }
 
-  public static abstract interface ConnectionService.OutgoingCallResponse {
+  public static abstract interface ConnectionService.CreateConnectionResponse {
     method public abstract void onCancel(android.telecomm.ConnectionRequest);
     method public abstract void onFailure(android.telecomm.ConnectionRequest, int, java.lang.String);
     method public abstract void onSuccess(android.telecomm.ConnectionRequest, CONNECTION);
@@ -27842,7 +28214,6 @@
     method public java.util.List<java.lang.String> getCannedSmsResponses();
     method public int getCapabilities();
     method public long getConnectTimeMillis();
-    method public android.telecomm.CallServiceDescriptor getCurrentCallServiceDescriptor();
     method public int getDisconnectCauseCode();
     method public java.lang.String getDisconnectCauseMsg();
     method public android.telecomm.GatewayInfo getGatewayInfo();
@@ -27855,31 +28226,52 @@
     field public static final android.os.Parcelable.Creator CREATOR;
   }
 
-  public abstract class InCallService extends android.app.Service {
+  public abstract class InCallService {
     ctor protected InCallService();
-    method protected abstract void addCall(android.telecomm.InCallCall);
-    method protected abstract void bringToForeground(boolean);
-    method protected final android.telecomm.InCallAdapter getAdapter();
-    method protected void onAdapterAttached(android.telecomm.InCallAdapter);
-    method protected abstract void onAudioStateChanged(android.telecomm.CallAudioState);
-    method public final android.os.IBinder onBind(android.content.Intent);
-    method protected abstract void setPostDial(java.lang.String, java.lang.String);
-    method protected abstract void setPostDialWait(java.lang.String, java.lang.String);
-    method protected abstract void updateCall(android.telecomm.InCallCall);
+    method public final android.os.IBinder getBinder();
+    method public android.telecomm.Phone getPhone();
+    method public void onPhoneCreated(android.telecomm.Phone);
+    method public void onPhoneDestroyed(android.telecomm.Phone);
   }
 
-  public final class PhoneAccount implements android.os.Parcelable {
-    ctor public PhoneAccount(android.content.ComponentName, java.lang.String, android.net.Uri, java.lang.String, java.lang.String, boolean, boolean);
+  public final class Phone {
+    method public final void addListener(android.telecomm.Phone.Listener);
+    method public final android.telecomm.CallAudioState getAudioState();
+    method public final java.util.List<android.telecomm.Call> getCalls();
+    method public final void removeListener(android.telecomm.Phone.Listener);
+    method public final void setAudioRoute(int);
+    method public final void setMuted(boolean);
+  }
+
+  public static abstract class Phone.Listener {
+    ctor public Phone.Listener();
+    method public void onAudioStateChanged(android.telecomm.Phone, android.telecomm.CallAudioState);
+    method public void onBringToForeground(android.telecomm.Phone, boolean);
+    method public void onCallAdded(android.telecomm.Phone, android.telecomm.Call);
+    method public void onCallRemoved(android.telecomm.Phone, android.telecomm.Call);
+  }
+
+  public class PhoneAccount implements android.os.Parcelable {
+    ctor public PhoneAccount(android.content.ComponentName, java.lang.String, android.net.Uri, int);
     method public int describeContents();
+    method public int getCapabilities();
     method public android.content.ComponentName getComponentName();
     method public android.net.Uri getHandle();
-    method public android.graphics.drawable.Drawable getIcon(android.content.Context);
-    method public android.graphics.drawable.Drawable getIcon(android.content.Context, int);
     method public java.lang.String getId();
-    method public java.lang.String getLabel(android.content.Context);
-    method public java.lang.String getShortDescription(android.content.Context);
-    method public boolean isEnabled();
-    method public boolean isSystemDefault();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CAPABILITY_CALL_PROVIDER = 2; // 0x2
+    field public static final int CAPABILITY_SIM_CALL_MANAGER = 1; // 0x1
+    field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
+    field public static final android.os.Parcelable.Creator CREATOR;
+  }
+
+  public class PhoneAccountMetadata implements android.os.Parcelable {
+    ctor public PhoneAccountMetadata(android.telecomm.PhoneAccount, int, java.lang.String, java.lang.String);
+    method public int describeContents();
+    method public android.telecomm.PhoneAccount getAccount();
+    method public android.graphics.drawable.Drawable getIcon(android.content.Context);
+    method public java.lang.String getLabel();
+    method public java.lang.String getShortDescription();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
   }
@@ -27894,18 +28286,17 @@
     method public void updatePeerDimensions(int, int) throws android.os.RemoteException;
   }
 
-  public class RemoteCallVideoProvider implements android.os.IBinder.DeathRecipient {
-    method public void binderDied();
-    method public void requestCallDataUsage() throws android.os.RemoteException;
-    method public void requestCameraCapabilities() throws android.os.RemoteException;
-    method public void sendSessionModifyRequest(android.telecomm.VideoCallProfile) throws android.os.RemoteException;
-    method public void sendSessionModifyResponse(android.telecomm.VideoCallProfile) throws android.os.RemoteException;
-    method public void setCallVideoClient(android.telecomm.CallVideoClient) throws android.os.RemoteException;
+  public class RemoteCallVideoProvider {
+    method public void requestCallDataUsage();
+    method public void requestCameraCapabilities();
+    method public void sendSessionModifyRequest(android.telecomm.VideoCallProfile);
+    method public void sendSessionModifyResponse(android.telecomm.VideoCallProfile);
+    method public void setCallVideoClient(android.telecomm.CallVideoClient);
     method public void setCamera(java.lang.String) throws android.os.RemoteException;
-    method public void setDeviceOrientation(int) throws android.os.RemoteException;
-    method public void setDisplaySurface(android.view.Surface) throws android.os.RemoteException;
-    method public void setPauseImage(java.lang.String) throws android.os.RemoteException;
-    method public void setPreviewSurface(android.view.Surface) throws android.os.RemoteException;
+    method public void setDeviceOrientation(int);
+    method public void setDisplaySurface(android.view.Surface);
+    method public void setPauseImage(java.lang.String);
+    method public void setPreviewSurface(android.view.Surface);
     method public void setZoom(float) throws android.os.RemoteException;
   }
 
@@ -27972,16 +28363,16 @@
 
   public final class TelecommConstants {
     ctor public TelecommConstants();
-    field public static final java.lang.String ACTION_CALL_SERVICE_PROVIDER;
     field public static final java.lang.String ACTION_CONNECTION_SERVICE;
+    field public static final java.lang.String ACTION_CONNECTION_SERVICE_CONFIGURE = "android.intent.action.CONNECTION_SERVICE_CONFIGURE";
     field public static final java.lang.String ACTION_INCOMING_CALL = "android.intent.action.INCOMING_CALL";
     field public static final char DTMF_CHARACTER_PAUSE = 44; // 0x002c ','
     field public static final char DTMF_CHARACTER_WAIT = 59; // 0x003b ';'
     field public static final java.lang.String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecomm.extra.CALL_DISCONNECT_CAUSE";
     field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecomm.extra.CALL_DISCONNECT_MESSAGE";
-    field public static final java.lang.String EXTRA_CALL_SERVICE_DESCRIPTOR = "android.intent.extra.CALL_SERVICE_DESCRIPTOR";
     field public static final java.lang.String EXTRA_CONNECTION_SERVICE = "android.telecomm.extra.CONNECTION_SERVICE";
     field public static final java.lang.String EXTRA_INCOMING_CALL_EXTRAS = "android.intent.extra.INCOMING_CALL_EXTRAS";
+    field public static final java.lang.String EXTRA_PHONE_ACCOUNT = "android.intent.extra.PHONE_ACCOUNT";
     field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.intent.extra.START_CALL_WITH_SPEAKERPHONE";
     field public static final java.lang.String EXTRA_START_CALL_WITH_VIDEO_STATE = "android.intent.extra.START_CALL_WITH_VIDEO_STATE";
   }
@@ -28420,7 +28811,6 @@
   }
 
   public class TelephonyManager {
-    method public java.util.List<android.telecomm.PhoneAccount> getAccounts();
     method public java.util.List<android.telephony.CellInfo> getAllCellInfo();
     method public int getCallState();
     method public android.telephony.CellLocation getCellLocation();
@@ -28469,7 +28859,6 @@
     field public static final int DATA_CONNECTING = 1; // 0x1
     field public static final int DATA_DISCONNECTED = 0; // 0x0
     field public static final int DATA_SUSPENDED = 3; // 0x3
-    field public static final java.lang.String EXTRA_ACCOUNT = "account";
     field public static final java.lang.String EXTRA_INCOMING_NUMBER = "incoming_number";
     field public static final java.lang.String EXTRA_STATE = "state";
     field public static final java.lang.String EXTRA_STATE_IDLE;
@@ -28985,6 +29374,7 @@
     method public java.io.File getFileStreamPath(java.lang.String);
     method public java.io.File getFilesDir();
     method public android.os.Looper getMainLooper();
+    method public java.io.File getNoBackupFilesDir();
     method public java.io.File getObbDir();
     method public java.io.File[] getObbDirs();
     method public java.lang.String getPackageCodePath();
@@ -29121,8 +29511,10 @@
     method public android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
     method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
+    method public android.content.pm.PackageInstaller getInstaller();
     method public java.lang.String getInstallerPackageName(java.lang.String);
     method public android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public android.content.pm.KeySet getKeySetByAlias(java.lang.String, java.lang.String);
     method public android.content.Intent getLaunchIntentForPackage(java.lang.String);
     method public android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
     method public java.lang.String getNameForUid(int);
@@ -29140,12 +29532,15 @@
     method public android.content.res.Resources getResourcesForApplication(android.content.pm.ApplicationInfo);
     method public android.content.res.Resources getResourcesForApplication(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.pm.ServiceInfo getServiceInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public android.content.pm.KeySet getSigningKeySet(java.lang.String);
     method public android.content.pm.FeatureInfo[] getSystemAvailableFeatures();
     method public java.lang.String[] getSystemSharedLibraryNames();
     method public java.lang.CharSequence getText(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public boolean hasSystemFeature(java.lang.String);
     method public boolean isSafeMode();
+    method public boolean isSignedBy(java.lang.String, android.content.pm.KeySet);
+    method public boolean isSignedByExactly(java.lang.String, android.content.pm.KeySet);
     method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
     method public java.util.List<android.content.pm.ProviderInfo> queryContentProviders(java.lang.String, int, int);
     method public java.util.List<android.content.pm.InstrumentationInfo> queryInstrumentation(java.lang.String, int);
@@ -34035,6 +34430,7 @@
     method protected final int getForcedWindowFlags();
     method public abstract android.view.LayoutInflater getLayoutInflater();
     method protected final int getLocalFeatures();
+    method public android.media.session.MediaController getMediaController();
     method public abstract int getNavigationBarColor();
     method public android.transition.Transition getSharedElementEnterTransition();
     method public android.transition.Transition getSharedElementExitTransition();
@@ -34091,6 +34487,7 @@
     method public void setLayout(int, int);
     method public void setLocalFocus(boolean, boolean);
     method public void setLogo(int);
+    method public void setMediaController(android.media.session.MediaController);
     method public abstract void setNavigationBarColor(int);
     method public void setSharedElementEnterTransition(android.transition.Transition);
     method public void setSharedElementExitTransition(android.transition.Transition);
diff --git a/cmds/media/src/com/android/commands/media/Media.java b/cmds/media/src/com/android/commands/media/Media.java
index 4e91361..800b925 100644
--- a/cmds/media/src/com/android/commands/media/Media.java
+++ b/cmds/media/src/com/android/commands/media/Media.java
@@ -25,7 +25,6 @@
 import android.media.session.MediaController;
 import android.media.session.MediaSessionInfo;
 import android.media.session.PlaybackState;
-import android.media.session.RouteInfo;
 import android.os.Bundle;
 import android.os.HandlerThread;
 import android.os.IBinder;
@@ -181,11 +180,6 @@
         }
 
         @Override
-        public void onRouteChanged(RouteInfo route) {
-            System.out.println("onRouteChanged " + route);
-        }
-
-        @Override
         public void onPlaybackStateChanged(PlaybackState state) {
             System.out.println("onPlaybackStateChanged " + state);
         }
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 3d0eec4..b7f1ff9 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -50,6 +50,7 @@
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.content.PackageHelper;
@@ -923,33 +924,13 @@
                     return;
                 }
             } else if (opt.equals("--abi")) {
-                abi = nextOptionData();
-                if (abi == null) {
-                    System.err.println("Error: must supply argument for --abi");
-                    return;
-                }
+                abi = checkAbiArgument(nextOptionData());
             } else {
                 System.err.println("Error: Unknown option: " + opt);
                 return;
             }
         }
 
-        if (abi != null) {
-            final String[] supportedAbis = Build.SUPPORTED_ABIS;
-            boolean matched = false;
-            for (String supportedAbi : supportedAbis) {
-                if (supportedAbi.equals(abi)) {
-                    matched = true;
-                    break;
-                }
-            }
-
-            if (!matched) {
-                System.err.println("Error: abi " + abi + " not supported on this device.");
-                return;
-            }
-        }
-
         final Uri verificationURI;
         final Uri originatingURI;
         final Uri referrerURI;
@@ -1017,7 +998,7 @@
 
         final InstallSessionParams params = new InstallSessionParams();
         params.installFlags = PackageManager.INSTALL_ALL_USERS;
-        params.fullInstall = true;
+        params.mode = InstallSessionParams.MODE_FULL_INSTALL;
         params.progressMax = -1;
 
         String opt;
@@ -1040,10 +1021,12 @@
             } else if (opt.equals("-d")) {
                 params.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
             } else if (opt.equals("-p")) {
-                params.fullInstall = false;
+                params.mode = InstallSessionParams.MODE_INHERIT_EXISTING;
             } else if (opt.equals("-S")) {
                 params.deltaSize = Long.parseLong(nextOptionData());
                 params.progressMax = (int) params.deltaSize;
+            } else if (opt.equals("--abi")) {
+                params.abiOverride = checkAbiArgument(nextOptionData());
             } else {
                 throw new IllegalArgumentException("Unknown option " + opt);
             }
@@ -1684,6 +1667,21 @@
         }
     }
 
+    private static String checkAbiArgument(String abi) {
+        if (TextUtils.isEmpty(abi)) {
+            throw new IllegalArgumentException("Missing ABI argument");
+        }
+
+        final String[] supportedAbis = Build.SUPPORTED_ABIS;
+        for (String supportedAbi : supportedAbis) {
+            if (supportedAbi.equals(abi)) {
+                return abi;
+            }
+        }
+
+        throw new IllegalArgumentException("ABI " + abi + " not supported on this device");
+    }
+
     private String nextOption() {
         if (mNextArg >= mArgs.length) {
             return null;
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index f1a2576..cac646d 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -23,6 +23,7 @@
 import android.util.ArrayMap;
 import android.util.SuperNotCalledException;
 import android.widget.Toolbar;
+
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.app.WindowDecorActionBar;
 import com.android.internal.app.ToolbarActionBar;
@@ -51,6 +52,8 @@
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
 import android.media.AudioManager;
+import android.media.session.MediaController;
+import android.media.session.MediaSession;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
@@ -109,14 +112,14 @@
  * or embedded inside of another activity (using {@link ActivityGroup}).
  *
  * There are two methods almost all subclasses of Activity will implement:
- * 
+ *
  * <ul>
  *     <li> {@link #onCreate} is where you initialize your activity.  Most
  *     importantly, here you will usually call {@link #setContentView(int)}
  *     with a layout resource defining your UI, and using {@link #findViewById}
  *     to retrieve the widgets in that UI that you need to interact with
  *     programmatically.
- * 
+ *
  *     <li> {@link #onPause} is where you deal with the user leaving your
  *     activity.  Most importantly, any changes made by the user should at this
  *     point be committed (usually to the
@@ -127,7 +130,7 @@
  * activity classes must have a corresponding
  * {@link android.R.styleable#AndroidManifestActivity &lt;activity&gt;}
  * declaration in their package's <code>AndroidManifest.xml</code>.</p>
- * 
+ *
  * <p>Topics covered here:
  * <ol>
  * <li><a href="#Fragments">Fragments</a>
@@ -170,14 +173,14 @@
  * and becomes the running activity -- the previous activity always remains
  * below it in the stack, and will not come to the foreground again until
  * the new activity exits.</p>
- * 
+ *
  * <p>An activity has essentially four states:</p>
  * <ul>
  *     <li> If an activity in the foreground of the screen (at the top of
  *         the stack),
  *         it is <em>active</em> or  <em>running</em>. </li>
  *     <li>If an activity has lost focus but is still visible (that is, a new non-full-sized
- *         or transparent activity has focus on top of your activity), it 
+ *         or transparent activity has focus on top of your activity), it
  *         is <em>paused</em>. A paused activity is completely alive (it
  *         maintains all state and member information and remains attached to
  *         the window manager), but can be killed by the system in extreme
@@ -197,13 +200,13 @@
  * The square rectangles represent callback methods you can implement to
  * perform operations when the Activity moves between states.  The colored
  * ovals are major states the Activity can be in.</p>
- * 
+ *
  * <p><img src="../../../images/activity_lifecycle.png"
  *      alt="State diagram for an Android Activity Lifecycle." border="0" /></p>
- * 
+ *
  * <p>There are three key loops you may be interested in monitoring within your
  * activity:
- * 
+ *
  * <ul>
  * <li>The <b>entire lifetime</b> of an activity happens between the first call
  * to {@link android.app.Activity#onCreate} through to a single final call
@@ -212,7 +215,7 @@
  * onDestroy().  For example, if it has a thread running in the background
  * to download data from the network, it may create that thread in onCreate()
  * and then stop the thread in onDestroy().
- * 
+ *
  * <li>The <b>visible lifetime</b> of an activity happens between a call to
  * {@link android.app.Activity#onStart} until a corresponding call to
  * {@link android.app.Activity#onStop}.  During this time the user can see the
@@ -224,7 +227,7 @@
  * longer sees what you are displaying.  The onStart() and onStop() methods
  * can be called multiple times, as the activity becomes visible and hidden
  * to the user.
- * 
+ *
  * <li>The <b>foreground lifetime</b> of an activity happens between a call to
  * {@link android.app.Activity#onResume} until a corresponding call to
  * {@link android.app.Activity#onPause}.  During this time the activity is
@@ -234,7 +237,7 @@
  * intent is delivered -- so the code in these methods should be fairly
  * lightweight.
  * </ul>
- * 
+ *
  * <p>The entire lifecycle of an activity is defined by the following
  * Activity methods.  All of these are hooks that you can override
  * to do appropriate work when the activity changes state.  All
@@ -250,7 +253,7 @@
  *     protected void onCreate(Bundle savedInstanceState);
  *
  *     protected void onStart();
- *     
+ *
  *     protected void onRestart();
  *
  *     protected void onResume();
@@ -366,7 +369,7 @@
  * {@link #onSaveInstanceState(Bundle)} is called before placing the activity
  * in such a background state, allowing you to save away any dynamic instance
  * state in your activity into the given Bundle, to be later received in
- * {@link #onCreate} if the activity needs to be re-created.  
+ * {@link #onCreate} if the activity needs to be re-created.
  * See the <a href="#ProcessLifecycle">Process Lifecycle</a>
  * section for more information on how the lifecycle of a process is tied
  * to the activities it is hosting.  Note that it is important to save
@@ -390,14 +393,14 @@
  *
  * <a name="ConfigurationChanges"></a>
  * <h3>Configuration Changes</h3>
- * 
+ *
  * <p>If the configuration of the device (as defined by the
  * {@link Configuration Resources.Configuration} class) changes,
  * then anything displaying a user interface will need to update to match that
  * configuration.  Because Activity is the primary mechanism for interacting
  * with the user, it includes special support for handling configuration
  * changes.</p>
- * 
+ *
  * <p>Unless you specify otherwise, a configuration change (such as a change
  * in screen orientation, language, input devices, etc) will cause your
  * current activity to be <em>destroyed</em>, going through the normal activity
@@ -407,7 +410,7 @@
  * called in that instance then a new instance of the activity will be
  * created, with whatever savedInstanceState the previous instance had generated
  * from {@link #onSaveInstanceState}.</p>
- * 
+ *
  * <p>This is done because any application resource,
  * including layout files, can change based on any configuration value.  Thus
  * the only safe way to handle a configuration change is to re-retrieve all
@@ -415,7 +418,7 @@
  * must already know how to save their state and re-create themselves from
  * that state, this is a convenient way to have an activity restart itself
  * with a new configuration.</p>
- * 
+ *
  * <p>In some special cases, you may want to bypass restarting of your
  * activity based on one or more types of configuration changes.  This is
  * done with the {@link android.R.attr#configChanges android:configChanges}
@@ -425,7 +428,7 @@
  * a configuration change involves any that you do not handle, however, the
  * activity will still be restarted and {@link #onConfigurationChanged}
  * will not be called.</p>
- * 
+ *
  * <a name="StartingActivities"></a>
  * <h3>Starting Activities and Getting Results</h3>
  *
@@ -440,10 +443,10 @@
  * ends.  For example, you may start an activity that lets the user pick
  * a person in a list of contacts; when it ends, it returns the person
  * that was selected.  To do this, you call the
- * {@link android.app.Activity#startActivityForResult(Intent, int)} 
- * version with a second integer parameter identifying the call.  The result 
+ * {@link android.app.Activity#startActivityForResult(Intent, int)}
+ * version with a second integer parameter identifying the call.  The result
  * will come back through your {@link android.app.Activity#onActivityResult}
- * method.</p> 
+ * method.</p>
  *
  * <p>When an activity exits, it can call
  * {@link android.app.Activity#setResult(int)}
@@ -570,17 +573,17 @@
  *
  *     protected void onPause() {
  *         super.onPause();
- * 
+ *
  *         SharedPreferences.Editor ed = mPrefs.edit();
  *         ed.putInt("view_mode", mCurViewMode);
  *         ed.commit();
  *     }
  * }
  * </pre>
- * 
+ *
  * <a name="Permissions"></a>
  * <h3>Permissions</h3>
- * 
+ *
  * <p>The ability to start a particular Activity can be enforced when it is
  * declared in its
  * manifest's {@link android.R.styleable#AndroidManifestActivity &lt;activity&gt;}
@@ -601,10 +604,10 @@
  *
  * <p>See the <a href="{@docRoot}guide/topics/security/security.html">Security and Permissions</a>
  * document for more information on permissions and security in general.
- * 
+ *
  * <a name="ProcessLifecycle"></a>
  * <h3>Process Lifecycle</h3>
- * 
+ *
  * <p>The Android system attempts to keep application process around for as
  * long as possible, but eventually will need to remove old processes when
  * memory runs low.  As described in <a href="#ActivityLifecycle">Activity
@@ -614,7 +617,7 @@
  * listed here in order of importance.  The system will kill less important
  * processes (the last ones) before it resorts to killing more important
  * processes (the first ones).
- * 
+ *
  * <ol>
  * <li> <p>The <b>foreground activity</b> (the activity at the top of the screen
  * that the user is currently interacting with) is considered the most important.
@@ -642,7 +645,7 @@
  * context of an activity BroadcastReceiver or Service to ensure that the system
  * knows it needs to keep your process around.
  * </ol>
- * 
+ *
  * <p>Sometimes an Activity may need to do a long-running operation that exists
  * independently of the activity lifecycle itself.  An example may be a camera
  * application that allows you to upload a picture to a web site.  The upload
@@ -720,7 +723,7 @@
         VoiceInteractor voiceInteractor;
     }
     /* package */ NonConfigurationInstances mLastNonConfigurationInstances;
-    
+
     private Window mWindow;
 
     private WindowManager mWindowManager;
@@ -764,7 +767,7 @@
     private final ArrayList<ManagedCursor> mManagedCursors =
         new ArrayList<ManagedCursor>();
 
-    // protected by synchronized (this) 
+    // protected by synchronized (this)
     int mResultCode = RESULT_CANCELED;
     Intent mResultData = null;
 
@@ -775,7 +778,7 @@
 
     private int mDefaultKeyMode = DEFAULT_KEYS_DISABLE;
     private SpannableStringBuilder mDefaultKeySsb = null;
-    
+
     protected static final int[] FOCUSED_STATE_SET = {com.android.internal.R.attr.state_focused};
 
     @SuppressWarnings("unused")
@@ -793,16 +796,16 @@
         return mIntent;
     }
 
-    /** 
-     * Change the intent returned by {@link #getIntent}.  This holds a 
-     * reference to the given intent; it does not copy it.  Often used in 
-     * conjunction with {@link #onNewIntent}. 
-     *  
-     * @param newIntent The new Intent object to return from getIntent 
-     * 
+    /**
+     * Change the intent returned by {@link #getIntent}.  This holds a
+     * reference to the given intent; it does not copy it.  Often used in
+     * conjunction with {@link #onNewIntent}.
+     *
+     * @param newIntent The new Intent object to return from getIntent
+     *
      * @see #getIntent
      * @see #onNewIntent
-     */ 
+     */
     public void setIntent(Intent newIntent) {
         mIntent = newIntent;
     }
@@ -816,7 +819,7 @@
     public final boolean isChild() {
         return mParent != null;
     }
-    
+
     /** Return the parent activity if this view is an embedded child. */
     public final Activity getParent() {
         return mParent;
@@ -831,7 +834,7 @@
      * Retrieve the current {@link android.view.Window} for the activity.
      * This can be used to directly access parts of the Window API that
      * are not available through Activity/Screen.
-     * 
+     *
      * @return Window The current window, or null if the activity is not
      *         visual.
      */
@@ -850,7 +853,7 @@
         mLoaderManager = getLoaderManager("(root)", mLoadersStarted, true);
         return mLoaderManager;
     }
-    
+
     LoaderManagerImpl getLoaderManager(String who, boolean started, boolean create) {
         if (mAllLoaderManagers == null) {
             mAllLoaderManagers = new ArrayMap<String, LoaderManagerImpl>();
@@ -866,13 +869,13 @@
         }
         return lm;
     }
-    
+
     /**
      * Calls {@link android.view.Window#getCurrentFocus} on the
      * Window of this Activity to return the currently focused view.
-     * 
+     *
      * @return View The current View with focus or null.
-     * 
+     *
      * @see #getWindow
      * @see android.view.Window#getCurrentFocus
      */
@@ -888,20 +891,20 @@
      * with widgets in the UI, calling
      * {@link #managedQuery(android.net.Uri , String[], String, String[], String)} to retrieve
      * cursors for data being displayed, etc.
-     * 
+     *
      * <p>You can call {@link #finish} from within this function, in
      * which case onDestroy() will be immediately called without any of the rest
      * of the activity lifecycle ({@link #onStart}, {@link #onResume},
      * {@link #onPause}, etc) executing.
-     * 
+     *
      * <p><em>Derived classes must call through to the super class's
      * implementation of this method.  If they do not, an exception will be
      * thrown.</em></p>
-     * 
+     *
      * @param savedInstanceState If the activity is being re-initialized after
      *     previously being shut down then this Bundle contains the data it most
      *     recently supplied in {@link #onSaveInstanceState}.  <b><i>Note: Otherwise it is null.</i></b>
-     * 
+     *
      * @see #onStart
      * @see #onSaveInstanceState
      * @see #onRestoreInstanceState
@@ -996,12 +999,12 @@
      * decide whether to use your default implementation.  The default
      * implementation of this method performs a restore of any view state that
      * had previously been frozen by {@link #onSaveInstanceState}.
-     * 
+     *
      * <p>This method is called between {@link #onStart} and
      * {@link #onPostCreate}.
-     * 
+     *
      * @param savedInstanceState the data most recently supplied in {@link #onSaveInstanceState}.
-     * 
+     *
      * @see #onCreate
      * @see #onPostCreate
      * @see #onResume
@@ -1098,11 +1101,11 @@
      * and {@link #onRestoreInstanceState} have been called).  Applications will
      * generally not implement this method; it is intended for system
      * classes to do final initialization after application code has run.
-     * 
+     *
      * <p><em>Derived classes must call through to the super class's
      * implementation of this method.  If they do not, an exception will be
      * thrown.</em></p>
-     * 
+     *
      * @param savedInstanceState If the activity is being re-initialized after
      *     previously being shut down then this Bundle contains the data it most
      *     recently supplied in {@link #onSaveInstanceState}.  <b><i>Note: Otherwise it is null.</i></b>
@@ -1133,14 +1136,14 @@
     }
 
     /**
-     * Called after {@link #onCreate} &mdash; or after {@link #onRestart} when  
-     * the activity had been stopped, but is now again being displayed to the 
+     * Called after {@link #onCreate} &mdash; or after {@link #onRestart} when
+     * the activity had been stopped, but is now again being displayed to the
      * user.  It will be followed by {@link #onResume}.
      *
      * <p><em>Derived classes must call through to the super class's
      * implementation of this method.  If they do not, an exception will be
      * thrown.</em></p>
-     * 
+     *
      * @see #onCreate
      * @see #onStop
      * @see #onResume
@@ -1148,7 +1151,7 @@
     protected void onStart() {
         if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStart " + this);
         mCalled = true;
-        
+
         if (!mLoadersStarted) {
             mLoadersStarted = true;
             if (mLoaderManager != null) {
@@ -1173,11 +1176,11 @@
      * this is usually the place
      * where the cursor should be requeried (because you had deactivated it in
      * {@link #onStop}.
-     * 
+     *
      * <p><em>Derived classes must call through to the super class's
      * implementation of this method.  If they do not, an exception will be
      * thrown.</em></p>
-     * 
+     *
      * @see #onStop
      * @see #onStart
      * @see #onResume
@@ -1200,7 +1203,7 @@
      * <p><em>Derived classes must call through to the super class's
      * implementation of this method.  If they do not, an exception will be
      * thrown.</em></p>
-     * 
+     *
      * @see #onRestoreInstanceState
      * @see #onRestart
      * @see #onPostResume
@@ -1218,11 +1221,11 @@
      * been called). Applications will generally not implement this method;
      * it is intended for system classes to do final setup after application
      * resume code has run.
-     * 
+     *
      * <p><em>Derived classes must call through to the super class's
      * implementation of this method.  If they do not, an exception will be
      * thrown.</em></p>
-     * 
+     *
      * @see #onResume
      */
     protected void onPostResume() {
@@ -1256,19 +1259,19 @@
      * activity is re-launched while at the top of the activity stack instead
      * of a new instance of the activity being started, onNewIntent() will be
      * called on the existing instance with the Intent that was used to
-     * re-launch it. 
-     *  
-     * <p>An activity will always be paused before receiving a new intent, so 
-     * you can count on {@link #onResume} being called after this method. 
-     * 
-     * <p>Note that {@link #getIntent} still returns the original Intent.  You 
-     * can use {@link #setIntent} to update it to this new Intent. 
-     * 
-     * @param intent The new intent that was started for the activity. 
-     *  
+     * re-launch it.
+     *
+     * <p>An activity will always be paused before receiving a new intent, so
+     * you can count on {@link #onResume} being called after this method.
+     *
+     * <p>Note that {@link #getIntent} still returns the original Intent.  You
+     * can use {@link #setIntent} to update it to this new Intent.
+     *
+     * @param intent The new intent that was started for the activity.
+     *
      * @see #getIntent
-     * @see #setIntent 
-     * @see #onResume 
+     * @see #setIntent
+     * @see #onResume
      */
     protected void onNewIntent(Intent intent) {
     }
@@ -1342,9 +1345,9 @@
      *
      * <p>If called, this method will occur before {@link #onStop}.  There are
      * no guarantees about whether it will occur before or after {@link #onPause}.
-     * 
+     *
      * @param outState Bundle in which to place your saved state.
-     * 
+     *
      * @see #onCreate
      * @see #onRestoreInstanceState
      * @see #onPause
@@ -1429,23 +1432,23 @@
      * noticeable amount of CPU in order to make the switch to the next activity
      * as fast as possible, or to close resources that are exclusive access
      * such as the camera.
-     * 
+     *
      * <p>In situations where the system needs more memory it may kill paused
      * processes to reclaim resources.  Because of this, you should be sure
      * that all of your state is saved by the time you return from
      * this function.  In general {@link #onSaveInstanceState} is used to save
      * per-instance state in the activity and this method is used to store
      * global persistent data (in content providers, files, etc.)
-     * 
+     *
      * <p>After receiving this call you will usually receive a following call
      * to {@link #onStop} (after the next activity has been resumed and
      * displayed), however in some cases there will be a direct call back to
      * {@link #onResume} without going through the stopped state.
-     * 
+     *
      * <p><em>Derived classes must call through to the super class's
      * implementation of this method.  If they do not, an exception will be
      * thrown.</em></p>
-     * 
+     *
      * @see #onResume
      * @see #onSaveInstanceState
      * @see #onStop
@@ -1464,32 +1467,32 @@
      * brought to the foreground, {@link #onUserLeaveHint} will not be called on
      * the activity being interrupted.  In cases when it is invoked, this method
      * is called right before the activity's {@link #onPause} callback.
-     * 
+     *
      * <p>This callback and {@link #onUserInteraction} are intended to help
      * activities manage status bar notifications intelligently; specifically,
      * for helping activities determine the proper time to cancel a notfication.
-     * 
+     *
      * @see #onUserInteraction()
      */
     protected void onUserLeaveHint() {
     }
-    
+
     /**
      * Generate a new thumbnail for this activity.  This method is called before
      * pausing the activity, and should draw into <var>outBitmap</var> the
      * imagery for the desired thumbnail in the dimensions of that bitmap.  It
      * can use the given <var>canvas</var>, which is configured to draw into the
      * bitmap, for rendering if desired.
-     * 
+     *
      * <p>The default implementation returns fails and does not draw a thumbnail;
      * this will result in the platform creating its own thumbnail if needed.
-     * 
+     *
      * @param outBitmap The bitmap to contain the thumbnail.
      * @param canvas Can be used to render into the bitmap.
-     * 
+     *
      * @return Return true if you have drawn into the bitmap; otherwise after
      *         you return it will be filled with a default thumbnail.
-     * 
+     *
      * @see #onCreateDescription
      * @see #onSaveInstanceState
      * @see #onPause
@@ -1502,15 +1505,15 @@
      * Generate a new description for this activity.  This method is called
      * before pausing the activity and can, if desired, return some textual
      * description of its current state to be displayed to the user.
-     * 
+     *
      * <p>The default implementation returns null, which will cause you to
      * inherit the description from the previous activity.  If all activities
      * return null, generally the label of the top activity will be used as the
      * description.
-     * 
+     *
      * @return A description of what the user is doing.  It should be short and
      *         sweet (only a few words).
-     * 
+     *
      * @see #onCreateThumbnail
      * @see #onSaveInstanceState
      * @see #onPause
@@ -1538,15 +1541,15 @@
      * Called when you are no longer visible to the user.  You will next
      * receive either {@link #onRestart}, {@link #onDestroy}, or nothing,
      * depending on later user activity.
-     * 
+     *
      * <p>Note that this method may never be called, in low memory situations
      * where the system does not have enough memory to keep your activity's
      * process running after its {@link #onPause} method is called.
-     * 
+     *
      * <p><em>Derived classes must call through to the super class's
      * implementation of this method.  If they do not, an exception will be
      * thrown.</em></p>
-     * 
+     *
      * @see #onRestart
      * @see #onResume
      * @see #onSaveInstanceState
@@ -1567,7 +1570,7 @@
      * {@link #finish} on it, or because the system is temporarily destroying
      * this instance of the activity to save space.  You can distinguish
      * between these two scenarios with the {@link #isFinishing} method.
-     * 
+     *
      * <p><em>Note: do not count on this method being called as a place for
      * saving data! For example, if an activity is editing data in a content
      * provider, those edits should be committed in either {@link #onPause} or
@@ -1579,11 +1582,11 @@
      * calling this method (or any others) in it, so it should not be used to
      * do things that are intended to remain around after the process goes
      * away.
-     * 
+     *
      * <p><em>Derived classes must call through to the super class's
      * implementation of this method.  If they do not, an exception will be
      * thrown.</em></p>
-     * 
+     *
      * @see #onPause
      * @see #onStop
      * @see #finish
@@ -1657,11 +1660,11 @@
      * by that attribute, then instead of reporting it the system will stop
      * and restart the activity (to have it launched with the new
      * configuration).
-     * 
+     *
      * <p>At the time that this function has been called, your Resources
      * object will have been updated to return resource values matching the
      * new configuration.
-     * 
+     *
      * @param newConfig The new device configuration.
      */
     public void onConfigurationChanged(Configuration newConfig) {
@@ -1681,7 +1684,7 @@
             mActionBar.onConfigurationChanged(newConfig);
         }
     }
-    
+
     /**
      * If this activity is being destroyed because it can not handle a
      * configuration parameter being changed (and thus its
@@ -1691,7 +1694,7 @@
      * destroyed.  Note that there is no guarantee that these will be
      * accurate (other changes could have happened at any time), so you should
      * only use this as an optimization hint.
-     * 
+     *
      * @return Returns a bit field of the configuration parameters that are
      * changing, as defined by the {@link android.content.res.Configuration}
      * class.
@@ -1699,21 +1702,21 @@
     public int getChangingConfigurations() {
         return mConfigChangeFlags;
     }
-    
+
     /**
      * Retrieve the non-configuration instance data that was previously
      * returned by {@link #onRetainNonConfigurationInstance()}.  This will
      * be available from the initial {@link #onCreate} and
      * {@link #onStart} calls to the new instance, allowing you to extract
      * any useful dynamic state from the previous instance.
-     * 
+     *
      * <p>Note that the data you retrieve here should <em>only</em> be used
      * as an optimization for handling configuration changes.  You should always
      * be able to handle getting a null pointer back, and an activity must
      * still be able to restore itself to its previous state (through the
      * normal {@link #onSaveInstanceState(Bundle)} mechanism) even if this
      * function returns null.
-     * 
+     *
      * @return Returns the object previously returned by
      * {@link #onRetainNonConfigurationInstance()}.
      *
@@ -1727,7 +1730,7 @@
         return mLastNonConfigurationInstances != null
                 ? mLastNonConfigurationInstances.activity : null;
     }
-    
+
     /**
      * Called by the system, as part of destroying an
      * activity due to a configuration change, when it is known that a new
@@ -1736,7 +1739,7 @@
      * itself, which can later be retrieved by calling
      * {@link #getLastNonConfigurationInstance()} in the new activity
      * instance.
-     * 
+     *
      * <em>If you are targeting {@link android.os.Build.VERSION_CODES#HONEYCOMB}
      * or later, consider instead using a {@link Fragment} with
      * {@link Fragment#setRetainInstance(boolean)
@@ -1756,14 +1759,14 @@
      * the {@link #getLastNonConfigurationInstance()} method of the following
      * activity instance as described there.
      * </ul>
-     * 
+     *
      * <p>These guarantees are designed so that an activity can use this API
      * to propagate extensive state from the old to new activity instance, from
      * loaded bitmaps, to network connections, to evenly actively running
      * threads.  Note that you should <em>not</em> propagate any data that
      * may change based on the configuration, including any data loaded from
      * resources such as strings, layouts, or drawables.
-     * 
+     *
      * <p>The guarantee of no message handling during the switch to the next
      * activity simplifies use with active objects.  For example if your retained
      * state is an {@link android.os.AsyncTask} you are guaranteed that its
@@ -1783,21 +1786,21 @@
     public Object onRetainNonConfigurationInstance() {
         return null;
     }
-    
+
     /**
      * Retrieve the non-configuration instance data that was previously
      * returned by {@link #onRetainNonConfigurationChildInstances()}.  This will
      * be available from the initial {@link #onCreate} and
      * {@link #onStart} calls to the new instance, allowing you to extract
      * any useful dynamic state from the previous instance.
-     * 
+     *
      * <p>Note that the data you retrieve here should <em>only</em> be used
      * as an optimization for handling configuration changes.  You should always
      * be able to handle getting a null pointer back, and an activity must
      * still be able to restore itself to its previous state (through the
      * normal {@link #onSaveInstanceState(Bundle)} mechanism) even if this
      * function returns null.
-     * 
+     *
      * @return Returns the object previously returned by
      * {@link #onRetainNonConfigurationChildInstances()}
      */
@@ -1806,7 +1809,7 @@
         return mLastNonConfigurationInstances != null
                 ? mLastNonConfigurationInstances.children : null;
     }
-    
+
     /**
      * This method is similar to {@link #onRetainNonConfigurationInstance()} except that
      * it should return either a mapping from  child activity id strings to arbitrary objects,
@@ -1818,7 +1821,7 @@
     HashMap<String,Object> onRetainNonConfigurationChildInstances() {
         return null;
     }
-    
+
     NonConfigurationInstances retainNonConfigurationInstances() {
         Object activity = onRetainNonConfigurationInstance();
         HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
@@ -1846,7 +1849,7 @@
                 && mVoiceInteractor == null) {
             return null;
         }
-        
+
         NonConfigurationInstances nci = new NonConfigurationInstances();
         nci.activity = activity;
         nci.children = children;
@@ -1886,7 +1889,7 @@
             }
         }
     }
-    
+
     /**
      * Called when a Fragment is being attached to this activity, immediately
      * after the call to its {@link Fragment#onAttach Fragment.onAttach()}
@@ -1894,14 +1897,14 @@
      */
     public void onAttachFragment(Fragment fragment) {
     }
-    
+
     /**
      * Wrapper around
      * {@link ContentResolver#query(android.net.Uri , String[], String, String[], String)}
      * that gives the resulting {@link Cursor} to call
      * {@link #startManagingCursor} so that the activity will manage its
      * lifecycle for you.
-     * 
+     *
      * <em>If you are targeting {@link android.os.Build.VERSION_CODES#HONEYCOMB}
      * or later, consider instead using {@link LoaderManager} instead, available
      * via {@link #getLoaderManager()}.</em>
@@ -1911,14 +1914,14 @@
      * you call {@link #stopManagingCursor} on a cursor from a managed query, the system <em>will
      * not</em> automatically close the cursor and, in that case, you must call
      * {@link Cursor#close()}.</p>
-     * 
+     *
      * @param uri The URI of the content provider to query.
      * @param projection List of columns to return.
      * @param selection SQL WHERE clause.
      * @param sortOrder SQL ORDER BY clause.
-     * 
+     *
      * @return The Cursor that was returned by query().
-     * 
+     *
      * @see ContentResolver#query(android.net.Uri , String[], String, String[], String)
      * @see #startManagingCursor
      * @hide
@@ -1941,7 +1944,7 @@
      * that gives the resulting {@link Cursor} to call
      * {@link #startManagingCursor} so that the activity will manage its
      * lifecycle for you.
-     * 
+     *
      * <em>If you are targeting {@link android.os.Build.VERSION_CODES#HONEYCOMB}
      * or later, consider instead using {@link LoaderManager} instead, available
      * via {@link #getLoaderManager()}.</em>
@@ -1951,15 +1954,15 @@
      * you call {@link #stopManagingCursor} on a cursor from a managed query, the system <em>will
      * not</em> automatically close the cursor and, in that case, you must call
      * {@link Cursor#close()}.</p>
-     * 
+     *
      * @param uri The URI of the content provider to query.
      * @param projection List of columns to return.
      * @param selection SQL WHERE clause.
      * @param selectionArgs The arguments to selection, if any ?s are pesent
      * @param sortOrder SQL ORDER BY clause.
-     * 
+     *
      * @return The Cursor that was returned by query().
-     * 
+     *
      * @see ContentResolver#query(android.net.Uri , String[], String, String[], String)
      * @see #startManagingCursor
      *
@@ -1982,7 +1985,7 @@
      * {@link Cursor#deactivate} on the given Cursor, and when it is later restarted
      * it will call {@link Cursor#requery} for you.  When the activity is
      * destroyed, all managed Cursors will be closed automatically.
-     * 
+     *
      * <em>If you are targeting {@link android.os.Build.VERSION_CODES#HONEYCOMB}
      * or later, consider instead using {@link LoaderManager} instead, available
      * via {@link #getLoaderManager()}.</em>
@@ -1992,9 +1995,9 @@
      * However, if you call {@link #stopManagingCursor} on a cursor from a managed query, the system
      * <em>will not</em> automatically close the cursor and, in that case, you must call
      * {@link Cursor#close()}.</p>
-     * 
+     *
      * @param c The Cursor to be managed.
-     * 
+     *
      * @see #managedQuery(android.net.Uri , String[], String, String[], String)
      * @see #stopManagingCursor
      *
@@ -2013,13 +2016,13 @@
      * Given a Cursor that was previously given to
      * {@link #startManagingCursor}, stop the activity's management of that
      * cursor.
-     * 
+     *
      * <p><strong>Warning:</strong> After calling this method on a cursor from a managed query,
-     * the system <em>will not</em> automatically close the cursor and you must call 
+     * the system <em>will not</em> automatically close the cursor and you must call
      * {@link Cursor#close()}.</p>
-     * 
+     *
      * @param c The Cursor that was being managed.
-     * 
+     *
      * @see #startManagingCursor
      *
      * @deprecated Use the new {@link android.content.CursorLoader} class with
@@ -2058,7 +2061,7 @@
     public View findViewById(int id) {
         return getWindow().findViewById(id);
     }
-    
+
     /**
      * Retrieve a reference to this activity's ActionBar.
      *
@@ -2094,7 +2097,7 @@
         mActionBar = new ToolbarActionBar(toolbar, getTitle(), this);
         mActionBar.invalidateOptionsMenu();
     }
-    
+
     /**
      * Creates a new ActionBar, locates the inflated ActionBarView,
      * initializes the ActionBar with the view, and sets mActionBar.
@@ -2116,13 +2119,13 @@
         mWindow.setDefaultIcon(mActivityInfo.getIconResource());
         mWindow.setDefaultLogo(mActivityInfo.getLogoResource());
     }
-    
+
     /**
      * Set the activity content from a layout resource.  The resource will be
      * inflated, adding all top-level views to the activity.
      *
      * @param layoutResID Resource ID to be inflated.
-     * 
+     *
      * @see #setContentView(android.view.View)
      * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
      */
@@ -2140,7 +2143,7 @@
      * your own layout parameters, invoke
      * {@link #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)}
      * instead.
-     * 
+     *
      * @param view The desired content to display.
      *
      * @see #setContentView(int)
@@ -2155,7 +2158,7 @@
      * Set the activity content to an explicit view.  This view is placed
      * directly into the activity's view hierarchy.  It can itself be a complex
      * view hierarchy.
-     * 
+     *
      * @param view The desired content to display.
      * @param params Layout parameters for the view.
      *
@@ -2170,7 +2173,7 @@
     /**
      * Add an additional content view to the activity.  Added after any existing
      * ones in the activity -- existing views are NOT removed.
-     * 
+     *
      * @param view The desired content to display.
      * @param params Layout parameters for the view.
      */
@@ -2235,23 +2238,23 @@
     /**
      * Use with {@link #setDefaultKeyMode} to turn off default handling of
      * keys.
-     * 
+     *
      * @see #setDefaultKeyMode
      */
     static public final int DEFAULT_KEYS_DISABLE = 0;
     /**
      * Use with {@link #setDefaultKeyMode} to launch the dialer during default
      * key handling.
-     * 
+     *
      * @see #setDefaultKeyMode
      */
     static public final int DEFAULT_KEYS_DIALER = 1;
     /**
      * Use with {@link #setDefaultKeyMode} to execute a menu shortcut in
      * default key handling.
-     * 
+     *
      * <p>That is, the user does not need to hold down the menu key to execute menu shortcuts.
-     * 
+     *
      * @see #setDefaultKeyMode
      */
     static public final int DEFAULT_KEYS_SHORTCUT = 2;
@@ -2259,9 +2262,9 @@
      * Use with {@link #setDefaultKeyMode} to specify that unhandled keystrokes
      * will start an application-defined search.  (If the application or activity does not
      * actually define a search, the the keys will be ignored.)
-     * 
+     *
      * <p>See {@link android.app.SearchManager android.app.SearchManager} for more details.
-     * 
+     *
      * @see #setDefaultKeyMode
      */
     static public final int DEFAULT_KEYS_SEARCH_LOCAL = 3;
@@ -2270,9 +2273,9 @@
      * Use with {@link #setDefaultKeyMode} to specify that unhandled keystrokes
      * will start a global search (typically web search, but some platforms may define alternate
      * methods for global search)
-     * 
+     *
      * <p>See {@link android.app.SearchManager android.app.SearchManager} for more details.
-     * 
+     *
      * @see #setDefaultKeyMode
      */
     static public final int DEFAULT_KEYS_SEARCH_GLOBAL = 4;
@@ -2284,16 +2287,16 @@
      * floor. Other modes allow you to launch the dialer
      * ({@link #DEFAULT_KEYS_DIALER}), execute a shortcut in your options
      * menu without requiring the menu key be held down
-     * ({@link #DEFAULT_KEYS_SHORTCUT}), or launch a search ({@link #DEFAULT_KEYS_SEARCH_LOCAL} 
+     * ({@link #DEFAULT_KEYS_SHORTCUT}), or launch a search ({@link #DEFAULT_KEYS_SEARCH_LOCAL}
      * and {@link #DEFAULT_KEYS_SEARCH_GLOBAL}).
-     * 
+     *
      * <p>Note that the mode selected here does not impact the default
      * handling of system keys, such as the "back" and "menu" keys, and your
      * activity and its views always get a first chance to receive and handle
      * all application keys.
-     * 
+     *
      * @param mode The desired default key mode constant.
-     * 
+     *
      * @see #DEFAULT_KEYS_DISABLE
      * @see #DEFAULT_KEYS_DIALER
      * @see #DEFAULT_KEYS_SHORTCUT
@@ -2303,7 +2306,7 @@
      */
     public final void setDefaultKeyMode(@DefaultKeyMode int mode) {
         mDefaultKeyMode = mode;
-        
+
         // Some modes use a SpannableStringBuilder to track & dispatch input events
         // This list must remain in sync with the switch in onKeyDown()
         switch (mode) {
@@ -2324,10 +2327,10 @@
 
     /**
      * Called when a key was pressed down and not handled by any of the views
-     * inside of the activity. So, for example, key presses while the cursor 
+     * inside of the activity. So, for example, key presses while the cursor
      * is inside a TextView will not trigger the event (unless it is a navigation
      * to another object) because TextView handles its own key presses.
-     * 
+     *
      * <p>If the focused view didn't want this event, this method is called.
      *
      * <p>The default implementation takes care of {@link KeyEvent#KEYCODE_BACK}
@@ -2338,12 +2341,12 @@
      * will be performed; for earlier applications, it will perform the
      * action immediately in on-down, as those versions of the platform
      * behaved.
-     * 
+     *
      * <p>Other additional default key handling may be performed
      * if configured with {@link #setDefaultKeyMode}.
-     * 
+     *
      * @return Return <code>true</code> to prevent this event from being propagated
-     * further, or <code>false</code> to indicate that you have not handled 
+     * further, or <code>false</code> to indicate that you have not handled
      * this event and it should continue to be propagated.
      * @see #onKeyUp
      * @see android.view.KeyEvent
@@ -2358,11 +2361,11 @@
             }
             return true;
         }
-        
+
         if (mDefaultKeyMode == DEFAULT_KEYS_DISABLE) {
             return false;
         } else if (mDefaultKeyMode == DEFAULT_KEYS_SHORTCUT) {
-            if (getWindow().performPanelShortcut(Window.FEATURE_OPTIONS_PANEL, 
+            if (getWindow().performPanelShortcut(Window.FEATURE_OPTIONS_PANEL,
                     keyCode, event, Menu.FLAG_ALWAYS_PERFORM_CLOSE)) {
                 return true;
             }
@@ -2382,12 +2385,12 @@
 
                     final String str = mDefaultKeySsb.toString();
                     clearSpannable = true;
-                    
+
                     switch (mDefaultKeyMode) {
                     case DEFAULT_KEYS_DIALER:
                         Intent intent = new Intent(Intent.ACTION_DIAL,  Uri.parse("tel:" + str));
                         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                        startActivity(intent);    
+                        startActivity(intent);
                         break;
                     case DEFAULT_KEYS_SEARCH_LOCAL:
                         startSearch(str, false, null, false);
@@ -2418,16 +2421,16 @@
 
     /**
      * Called when a key was released and not handled by any of the views
-     * inside of the activity. So, for example, key presses while the cursor 
+     * inside of the activity. So, for example, key presses while the cursor
      * is inside a TextView will not trigger the event (unless it is a navigation
      * to another object) because TextView handles its own key presses.
-     * 
+     *
      * <p>The default implementation handles KEYCODE_BACK to stop the activity
      * and go back.
-     * 
+     *
      * @return Return <code>true</code> to prevent this event from being propagated
-     * further, or <code>false</code> to indicate that you have not handled 
-     * this event and it should continue to be propagated. 
+     * further, or <code>false</code> to indicate that you have not handled
+     * this event and it should continue to be propagated.
      * @see #onKeyDown
      * @see KeyEvent
      */
@@ -2451,7 +2454,7 @@
     public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
         return false;
     }
-    
+
     /**
      * Called when the activity has detected the user's press of the back
      * key.  The default implementation simply finishes the current activity,
@@ -2485,9 +2488,9 @@
      * Called when a touch screen event was not handled by any of the views
      * under it.  This is most useful to process touch events that happen
      * outside of your window bounds, where there is no view to receive it.
-     * 
+     *
      * @param event The touch screen event being processed.
-     * 
+     *
      * @return Return true if you have consumed the event, false if you haven't.
      * The default implementation always returns false.
      */
@@ -2496,10 +2499,10 @@
             finish();
             return true;
         }
-        
+
         return false;
     }
-    
+
     /**
      * Called when the trackball was moved and not handled by any of the
      * views inside of the activity.  So, for example, if the trackball moves
@@ -2508,9 +2511,9 @@
      * here happens <em>before</em> trackball movements are converted to
      * DPAD key events, which then get sent back to the view hierarchy, and
      * will be processed at the point for things like focus navigation.
-     * 
+     *
      * @param event The trackball event being processed.
-     * 
+     *
      * @return Return true if you have consumed the event, false if you haven't.
      * The default implementation always returns false.
      */
@@ -2554,21 +2557,21 @@
      * This callback and {@link #onUserLeaveHint} are intended to help
      * activities manage status bar notifications intelligently; specifically,
      * for helping activities determine the proper time to cancel a notfication.
-     * 
+     *
      * <p>All calls to your activity's {@link #onUserLeaveHint} callback will
      * be accompanied by calls to {@link #onUserInteraction}.  This
      * ensures that your activity will be told of relevant user activity such
      * as pulling down the notification pane and touching an item there.
-     * 
+     *
      * <p>Note that this callback will be invoked for the touch down action
      * that begins a touch gesture, but may not be invoked for the touch-moved
      * and touch-up actions that follow.
-     * 
+     *
      * @see #onUserLeaveHint()
      */
     public void onUserInteraction() {
     }
-    
+
     public void onWindowAttributesChanged(WindowManager.LayoutParams params) {
         // Update window manager if: we have a view, that view is
         // attached to its parent (which will be a RootView), and
@@ -2589,14 +2592,14 @@
      * focus.  This is the best indicator of whether this activity is visible
      * to the user.  The default implementation clears the key tracking
      * state, so should always be called.
-     * 
+     *
      * <p>Note that this provides information about global focus state, which
      * is managed independently of activity lifecycles.  As such, while focus
      * changes will generally have some relation to lifecycle changes (an
      * activity that is stopped will not generally get window focus), you
      * should not rely on any particular order between the callbacks here and
      * those in the other lifecycle methods such as {@link #onResume}.
-     * 
+     *
      * <p>As a general rule, however, a resumed activity will have window
      * focus...  unless it has displayed other dialogs or popups that take
      * input focus, in which case the activity itself will not have focus
@@ -2606,14 +2609,14 @@
      * pausing the foreground activity.
      *
      * @param hasFocus Whether the window of this activity has focus.
-     * 
+     *
      * @see #hasWindowFocus()
      * @see #onResume
      * @see View#onWindowFocusChanged(boolean)
      */
     public void onWindowFocusChanged(boolean hasFocus) {
     }
-    
+
     /**
      * Called when the main window associated with the activity has been
      * attached to the window manager.
@@ -2623,7 +2626,7 @@
      */
     public void onAttachedToWindow() {
     }
-    
+
     /**
      * Called when the main window associated with the activity has been
      * detached from the window manager.
@@ -2633,13 +2636,13 @@
      */
     public void onDetachedFromWindow() {
     }
-    
+
     /**
      * Returns true if this activity's <em>main</em> window currently has window focus.
      * Note that this is not the same as the view itself having focus.
-     * 
+     *
      * @return True if this activity's main window currently has window focus.
-     * 
+     *
      * @see #onWindowAttributesChanged(android.view.WindowManager.LayoutParams)
      */
     public boolean hasWindowFocus() {
@@ -2661,14 +2664,14 @@
     public void onWindowDismissed() {
         finish();
     }
-    
+
     /**
-     * Called to process key events.  You can override this to intercept all 
-     * key events before they are dispatched to the window.  Be sure to call 
+     * Called to process key events.  You can override this to intercept all
+     * key events before they are dispatched to the window.  Be sure to call
      * this implementation for key events that should be handled normally.
-     * 
+     *
      * @param event The key event.
-     * 
+     *
      * @return boolean Return true if this event was consumed.
      */
     public boolean dispatchKeyEvent(KeyEvent event) {
@@ -2713,9 +2716,9 @@
      * intercept all touch screen events before they are dispatched to the
      * window.  Be sure to call this implementation for touch screen events
      * that should be handled normally.
-     * 
+     *
      * @param ev The touch screen event.
-     * 
+     *
      * @return boolean Return true if this event was consumed.
      */
     public boolean dispatchTouchEvent(MotionEvent ev) {
@@ -2727,15 +2730,15 @@
         }
         return onTouchEvent(ev);
     }
-    
+
     /**
      * Called to process trackball events.  You can override this to
      * intercept all trackball events before they are dispatched to the
      * window.  Be sure to call this implementation for trackball events
      * that should be handled normally.
-     * 
+     *
      * @param ev The trackball event.
-     * 
+     *
      * @return boolean Return true if this event was consumed.
      */
     public boolean dispatchTrackballEvent(MotionEvent ev) {
@@ -2830,7 +2833,7 @@
 
     /**
      * {@inheritDoc}
-     * 
+     *
      * @return The default implementation returns true.
      */
     public boolean onMenuOpened(int featureId, Menu menu) {
@@ -2880,7 +2883,7 @@
                     }
                 }
                 return false;
-                
+
             case Window.FEATURE_CONTEXT_MENU:
                 if(titleCondensed != null) {
                     EventLog.writeEvent(50000, 1, titleCondensed.toString());
@@ -2894,7 +2897,7 @@
                 return false;
         }
     }
-    
+
     /**
      * Default implementation of
      * {@link android.view.Window.Callback#onPanelClosed(int, Menu)} for
@@ -2910,7 +2913,7 @@
                 mFragments.dispatchOptionsMenuClosed(menu);
                 onOptionsMenuClosed(menu);
                 break;
-                
+
             case Window.FEATURE_CONTEXT_MENU:
                 onContextMenuClosed(menu);
                 break;
@@ -2932,32 +2935,32 @@
             mWindow.invalidatePanelMenu(Window.FEATURE_OPTIONS_PANEL);
         }
     }
-    
+
     /**
      * Initialize the contents of the Activity's standard options menu.  You
      * should place your menu items in to <var>menu</var>.
-     * 
+     *
      * <p>This is only called once, the first time the options menu is
      * displayed.  To update the menu every time it is displayed, see
      * {@link #onPrepareOptionsMenu}.
-     * 
+     *
      * <p>The default implementation populates the menu with standard system
-     * menu items.  These are placed in the {@link Menu#CATEGORY_SYSTEM} group so that 
-     * they will be correctly ordered with application-defined menu items. 
-     * Deriving classes should always call through to the base implementation. 
-     * 
+     * menu items.  These are placed in the {@link Menu#CATEGORY_SYSTEM} group so that
+     * they will be correctly ordered with application-defined menu items.
+     * Deriving classes should always call through to the base implementation.
+     *
      * <p>You can safely hold on to <var>menu</var> (and any items created
      * from it), making modifications to it as desired, until the next
      * time onCreateOptionsMenu() is called.
-     * 
+     *
      * <p>When you add items to the menu, you can implement the Activity's
      * {@link #onOptionsItemSelected} method to handle them there.
-     * 
+     *
      * @param menu The options menu in which you place your items.
-     * 
+     *
      * @return You must return true for the menu to be displayed;
      *         if you return false it will not be shown.
-     * 
+     *
      * @see #onPrepareOptionsMenu
      * @see #onOptionsItemSelected
      */
@@ -2973,17 +2976,17 @@
      * called right before the menu is shown, every time it is shown.  You can
      * use this method to efficiently enable/disable items or otherwise
      * dynamically modify the contents.
-     * 
+     *
      * <p>The default implementation updates the system menu items based on the
      * activity's state.  Deriving classes should always call through to the
      * base class implementation.
-     * 
+     *
      * @param menu The options menu as last shown or first initialized by
      *             onCreateOptionsMenu().
-     * 
+     *
      * @return You must return true for the menu to be displayed;
      *         if you return false it will not be shown.
-     * 
+     *
      * @see #onCreateOptionsMenu
      */
     public boolean onPrepareOptionsMenu(Menu menu) {
@@ -3000,15 +3003,15 @@
      * its Handler as appropriate).  You can use this method for any items
      * for which you would like to do processing without those other
      * facilities.
-     * 
+     *
      * <p>Derived classes should call through to the base class for it to
      * perform the default menu handling.</p>
-     * 
+     *
      * @param item The menu item that was selected.
-     * 
+     *
      * @return boolean Return false to allow normal menu processing to
      *         proceed, true to consume it here.
-     * 
+     *
      * @see #onCreateOptionsMenu
      */
     public boolean onOptionsItemSelected(MenuItem item) {
@@ -3125,7 +3128,7 @@
     /**
      * This hook is called whenever the options menu is being closed (either by the user canceling
      * the menu with the back/menu button, or when an item is selected).
-     *  
+     *
      * @param menu The options menu as last shown or first initialized by
      *             onCreateOptionsMenu().
      */
@@ -3134,7 +3137,7 @@
             mParent.onOptionsMenuClosed(menu);
         }
     }
-    
+
     /**
      * Programmatically opens the options menu. If the options menu is already
      * open, this method does nothing.
@@ -3144,7 +3147,7 @@
             mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, null);
         }
     }
-    
+
     /**
      * Progammatically closes the options menu. If the options menu is already
      * closed, this method does nothing.
@@ -3175,43 +3178,43 @@
      * {@link OnCreateContextMenuListener} on the view to this activity, so
      * {@link #onCreateContextMenu(ContextMenu, View, ContextMenuInfo)} will be
      * called when it is time to show the context menu.
-     * 
+     *
      * @see #unregisterForContextMenu(View)
      * @param view The view that should show a context menu.
      */
     public void registerForContextMenu(View view) {
         view.setOnCreateContextMenuListener(this);
     }
-    
+
     /**
      * Prevents a context menu to be shown for the given view. This method will remove the
      * {@link OnCreateContextMenuListener} on the view.
-     * 
+     *
      * @see #registerForContextMenu(View)
      * @param view The view that should stop showing a context menu.
      */
     public void unregisterForContextMenu(View view) {
         view.setOnCreateContextMenuListener(null);
     }
-    
+
     /**
      * Programmatically opens the context menu for a particular {@code view}.
      * The {@code view} should have been added via
      * {@link #registerForContextMenu(View)}.
-     * 
+     *
      * @param view The view to show the context menu for.
      */
     public void openContextMenu(View view) {
         view.showContextMenu();
     }
-    
+
     /**
      * Programmatically closes the most recently opened context menu, if showing.
      */
     public void closeContextMenu() {
         mWindow.closePanel(Window.FEATURE_CONTEXT_MENU);
     }
-    
+
     /**
      * This hook is called whenever an item in a context menu is selected. The
      * default implementation simply returns false to have the normal processing
@@ -3224,7 +3227,7 @@
      * <p>
      * Derived classes should call through to the base class for it to perform
      * the default menu handling.
-     * 
+     *
      * @param item The context menu item that was selected.
      * @return boolean Return false to allow normal context menu processing to
      *         proceed, true to consume it here.
@@ -3240,7 +3243,7 @@
      * This hook is called whenever the context menu is being closed (either by
      * the user canceling the menu with the back/menu button, or when an item is
      * selected).
-     * 
+     *
      * @param menu The context menu that is being closed.
      */
     public void onContextMenuClosed(Menu menu) {
@@ -3309,14 +3312,14 @@
      * Provides an opportunity to prepare a managed dialog before it is being
      * shown.  The default implementation calls through to
      * {@link #onPrepareDialog(int, Dialog)} for compatibility.
-     * 
+     *
      * <p>
      * Override this if you need to update a managed dialog based on the state
      * of the application each time it is shown. For example, a time picker
      * dialog might want to be updated with the current time. You should call
      * through to the superclass's implementation. The default implementation
      * will set this Activity as the owner activity on the Dialog.
-     * 
+     *
      * @param id The id of the managed dialog.
      * @param dialog The dialog.
      * @param args The dialog arguments provided to {@link #showDialog(int, Bundle)}.
@@ -3367,7 +3370,7 @@
      * If you need to rebuild the dialog, call {@link #removeDialog(int)} first.
      * @return Returns true if the Dialog was created; false is returned if
      * it is not created because {@link #onCreateDialog(int, Bundle)} returns false.
-     * 
+     *
      * @see Dialog
      * @see #onCreateDialog(int, Bundle)
      * @see #onPrepareDialog(int, Dialog, Bundle)
@@ -3393,7 +3396,7 @@
             }
             mManagedDialogs.put(id, md);
         }
-        
+
         md.mArgs = args;
         onPrepareDialog(id, md.mDialog, args);
         md.mDialog.show();
@@ -3422,7 +3425,7 @@
         if (mManagedDialogs == null) {
             throw missingDialog(id);
         }
-        
+
         final ManagedDialog md = mManagedDialogs.get(id);
         if (md == null) {
             throw missingDialog(id);
@@ -3449,7 +3452,7 @@
      * <p>As of {@link android.os.Build.VERSION_CODES#GINGERBREAD}, this function
      * will not throw an exception if you try to remove an ID that does not
      * currently have an associated dialog.</p>
-     * 
+     *
      * @param id The id of the managed dialog.
      *
      * @see #onCreateDialog(int, Bundle)
@@ -3474,37 +3477,37 @@
 
     /**
      * This hook is called when the user signals the desire to start a search.
-     * 
+     *
      * <p>You can use this function as a simple way to launch the search UI, in response to a
-     * menu item, search button, or other widgets within your activity. Unless overidden, 
+     * menu item, search button, or other widgets within your activity. Unless overidden,
      * calling this function is the same as calling
      * {@link #startSearch startSearch(null, false, null, false)}, which launches
      * search for the current activity as specified in its manifest, see {@link SearchManager}.
-     * 
+     *
      * <p>You can override this function to force global search, e.g. in response to a dedicated
      * search key, or to block search entirely (by simply returning false).
-     * 
+     *
      * @return Returns {@code true} if search launched, and {@code false} if activity blocks it.
      *         The default implementation always returns {@code true}.
-     * 
+     *
      * @see android.app.SearchManager
      */
     public boolean onSearchRequested() {
-        startSearch(null, false, null, false); 
+        startSearch(null, false, null, false);
         return true;
     }
-    
+
     /**
      * This hook is called to launch the search UI.
-     * 
-     * <p>It is typically called from onSearchRequested(), either directly from 
-     * Activity.onSearchRequested() or from an overridden version in any given 
+     *
+     * <p>It is typically called from onSearchRequested(), either directly from
+     * Activity.onSearchRequested() or from an overridden version in any given
      * Activity.  If your goal is simply to activate search, it is preferred to call
      * onSearchRequested(), which may have been overridden elsewhere in your Activity.  If your goal
      * is to inject specific data such as context data, it is preferred to <i>override</i>
      * onSearchRequested(), so that any callers to it will benefit from the override.
-     * 
-     * @param initialQuery Any non-null non-empty string will be inserted as 
+     *
+     * @param initialQuery Any non-null non-empty string will be inserted as
      * pre-entered text in the search query box.
      * @param selectInitialQuery If true, the initial query will be preselected, which means that
      * any further typing will replace it.  This is useful for cases where an entire pre-formed
@@ -3512,15 +3515,15 @@
      * inserted query.  This is useful when the inserted query is text that the user entered,
      * and the user would expect to be able to keep typing.  <i>This parameter is only meaningful
      * if initialQuery is a non-empty string.</i>
-     * @param appSearchData An application can insert application-specific 
-     * context here, in order to improve quality or specificity of its own 
+     * @param appSearchData An application can insert application-specific
+     * context here, in order to improve quality or specificity of its own
      * searches.  This data will be returned with SEARCH intent(s).  Null if
      * no extra data is required.
      * @param globalSearch If false, this will only launch the search that has been specifically
-     * defined by the application (which is usually defined as a local search).  If no default 
+     * defined by the application (which is usually defined as a local search).  If no default
      * search is defined in the current application or activity, global search will be launched.
      * If true, this will always launch a platform-global (e.g. web-based) search instead.
-     * 
+     *
      * @see android.app.SearchManager
      * @see #onSearchRequested
      */
@@ -3528,7 +3531,7 @@
             @Nullable Bundle appSearchData, boolean globalSearch) {
         ensureSearchManager();
         mSearchManager.startSearch(initialQuery, selectInitialQuery, getComponentName(),
-                appSearchData, globalSearch); 
+                appSearchData, globalSearch);
     }
 
     /**
@@ -3550,7 +3553,7 @@
      * Request that key events come to this activity. Use this if your
      * activity has no views with focus, but the activity still wants
      * a chance to process key events.
-     * 
+     *
      * @see android.view.Window#takeKeyEvents
      */
     public void takeKeyEvents(boolean get) {
@@ -3560,12 +3563,12 @@
     /**
      * Enable extended window features.  This is a convenience for calling
      * {@link android.view.Window#requestFeature getWindow().requestFeature()}.
-     * 
+     *
      * @param featureId The desired feature as defined in
      *                  {@link android.view.Window}.
      * @return Returns true if the requested feature is supported and now
      *         enabled.
-     * 
+     *
      * @see android.view.Window#requestFeature
      */
     public final boolean requestWindowFeature(int featureId) {
@@ -3677,7 +3680,7 @@
      * Launch an activity for which you would like a result when it finished.
      * When this activity exits, your
      * onActivityResult() method will be called with the given requestCode.
-     * Using a negative requestCode is the same as calling 
+     * Using a negative requestCode is the same as calling
      * {@link #startActivity} (the activity is not launched as a sub-activity).
      *
      * <p>Note that this method should only be used with Intent protocols
@@ -3687,7 +3690,7 @@
      * are launching uses the singleTask launch mode, it will not run in your
      * task and thus you will immediately receive a cancel result.
      *
-     * <p>As a special case, if you call startActivityForResult() with a requestCode 
+     * <p>As a special case, if you call startActivityForResult() with a requestCode
      * >= 0 during the initial onCreate(Bundle savedInstanceState)/onResume() of your
      * activity, then your window will not be displayed until a result is
      * returned back from the started activity.  This is to avoid visible
@@ -3751,6 +3754,48 @@
     /**
      * @hide Implement to provide correct calling token.
      */
+    public void startActivityForResultAsUser(Intent intent, int requestCode, UserHandle user) {
+        startActivityForResultAsUser(intent, requestCode, null, user);
+    }
+
+    /**
+     * @hide Implement to provide correct calling token.
+     */
+    public void startActivityForResultAsUser(Intent intent, int requestCode,
+            @Nullable Bundle options, UserHandle user) {
+        if (options != null) {
+            mActivityTransitionState.startExitOutTransition(this, options);
+        }
+        if (mParent != null) {
+            throw new RuntimeException("Can't be called from a child");
+        }
+        Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity(
+                this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode,
+                options, user);
+        if (ar != null) {
+            mMainThread.sendActivityResult(
+                mToken, mEmbeddedID, requestCode, ar.getResultCode(), ar.getResultData());
+        }
+        if (requestCode >= 0) {
+            // If this start is requesting a result, we can avoid making
+            // the activity visible until the result is received.  Setting
+            // this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
+            // activity hidden during this time, to avoid flickering.
+            // This can only be done when a result is requested because
+            // that guarantees we will get information back when the
+            // activity is finished, no matter what happens to it.
+            mStartedActivity = true;
+        }
+
+        final View decor = mWindow != null ? mWindow.peekDecorView() : null;
+        if (decor != null) {
+            decor.cancelPendingInputEvents();
+        }
+    }
+
+    /**
+     * @hide Implement to provide correct calling token.
+     */
     public void startActivityAsUser(Intent intent, UserHandle user) {
         startActivityAsUser(intent, null, user);
     }
@@ -3760,7 +3805,7 @@
      */
     public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) {
         if (mParent != null) {
-            throw new RuntimeException("Called be called from a child");
+            throw new RuntimeException("Can't be called from a child");
         }
         Instrumentation.ActivityResult ar =
                 mInstrumentation.execStartActivity(
@@ -3803,7 +3848,7 @@
      * here; otherwise, its associated action will be executed (such as
      * sending a broadcast) as if you had called
      * {@link IntentSender#sendIntent IntentSender.sendIntent} on it.
-     * 
+     *
      * @param intent The IntentSender to launch.
      * @param requestCode If >= 0, this code will be returned in
      *                    onActivityResult() when the activity exits.
@@ -3894,19 +3939,19 @@
      * information, the {@link Intent#FLAG_ACTIVITY_NEW_TASK} launch flag is not
      * required; if not specified, the new activity will be added to the
      * task of the caller.
-     * 
+     *
      * <p>This method throws {@link android.content.ActivityNotFoundException}
      * if there was no Activity found to run the given Intent.
-     * 
-     * @param intent The intent to start. 
+     *
+     * @param intent The intent to start.
      * @param options Additional options for how the Activity should be started.
      * See {@link android.content.Context#startActivity(Intent, Bundle)
      * Context.startActivity(Intent, Bundle)} for more details.
-     * 
+     *
      * @throws android.content.ActivityNotFoundException
      *
      * @see {@link #startActivity(Intent)}
-     * @see #startActivityForResult 
+     * @see #startActivityForResult
      */
     @Override
     public void startActivity(Intent intent, @Nullable Bundle options) {
@@ -3966,7 +4011,7 @@
     /**
      * Same as calling {@link #startIntentSender(IntentSender, Intent, int, int, int, Bundle)}
      * with no options.
-     * 
+     *
      * @param intent The IntentSender to launch.
      * @param fillInIntent If non-null, this will be provided as the
      * intent parameter to {@link IntentSender#sendIntent}.
@@ -4039,19 +4084,19 @@
     /**
      * A special variation to launch an activity only if a new activity
      * instance is needed to handle the given Intent.  In other words, this is
-     * just like {@link #startActivityForResult(Intent, int)} except: if you are 
+     * just like {@link #startActivityForResult(Intent, int)} except: if you are
      * using the {@link Intent#FLAG_ACTIVITY_SINGLE_TOP} flag, or
-     * singleTask or singleTop 
+     * singleTask or singleTop
      * {@link android.R.styleable#AndroidManifestActivity_launchMode launchMode},
-     * and the activity 
-     * that handles <var>intent</var> is the same as your currently running 
-     * activity, then a new instance is not needed.  In this case, instead of 
-     * the normal behavior of calling {@link #onNewIntent} this function will 
-     * return and you can handle the Intent yourself. 
-     * 
+     * and the activity
+     * that handles <var>intent</var> is the same as your currently running
+     * activity, then a new instance is not needed.  In this case, instead of
+     * the normal behavior of calling {@link #onNewIntent} this function will
+     * return and you can handle the Intent yourself.
+     *
      * <p>This function can only be called from a top-level activity; if it is
      * called from a child activity, a runtime exception will be thrown.
-     * 
+     *
      * @param intent The intent to start.
      * @param requestCode If >= 0, this code will be returned in
      *         onActivityResult() when the activity exits, as described in
@@ -4059,10 +4104,10 @@
      * @param options Additional options for how the Activity should be started.
      * See {@link android.content.Context#startActivity(Intent, Bundle)
      * Context.startActivity(Intent, Bundle)} for more details.
-     * 
+     *
      * @return If a new activity was launched then true is returned; otherwise
      *         false is returned and you must handle the Intent yourself.
-     *  
+     *
      * @see #startActivity
      * @see #startActivityForResult
      */
@@ -4125,7 +4170,7 @@
      * other activity components.  You can use this to hand the Intent off
      * to the next Activity that can handle it.  You typically call this in
      * {@link #onCreate} with the Intent returned by {@link #getIntent}.
-     * 
+     *
      * @param intent The intent to dispatch to the next activity.  For
      * correct behavior, this must be the same as the Intent that started
      * your own activity; the only changes you can make are to the extras
@@ -4133,7 +4178,7 @@
      * @param options Additional options for how the Activity should be started.
      * See {@link android.content.Context#startActivity(Intent, Bundle)
      * Context.startActivity(Intent, Bundle)} for more details.
-     * 
+     *
      * @return Returns a boolean indicating whether there was another Activity
      * to start: true if there was a next activity to start, false if there
      * wasn't.  In general, if true is returned you will then want to call
@@ -4175,23 +4220,23 @@
     }
 
     /**
-     * This is called when a child activity of this one calls its 
+     * This is called when a child activity of this one calls its
      * {@link #startActivity} or {@link #startActivityForResult} method.
-     * 
+     *
      * <p>This method throws {@link android.content.ActivityNotFoundException}
      * if there was no Activity found to run the given Intent.
-     * 
+     *
      * @param child The activity making the call.
      * @param intent The intent to start.
      * @param requestCode Reply request code.  < 0 if reply is not requested.
      * @param options Additional options for how the Activity should be started.
      * See {@link android.content.Context#startActivity(Intent, Bundle)
      * Context.startActivity(Intent, Bundle)} for more details.
-     * 
+     *
      * @throws android.content.ActivityNotFoundException
-     * 
-     * @see #startActivity 
-     * @see #startActivityForResult 
+     *
+     * @see #startActivity
+     * @see #startActivityForResult
      */
     public void startActivityFromChild(@NonNull Activity child, Intent intent,
             int requestCode, @Nullable Bundle options) {
@@ -4225,24 +4270,24 @@
     }
 
     /**
-     * This is called when a Fragment in this activity calls its 
+     * This is called when a Fragment in this activity calls its
      * {@link Fragment#startActivity} or {@link Fragment#startActivityForResult}
      * method.
-     * 
+     *
      * <p>This method throws {@link android.content.ActivityNotFoundException}
      * if there was no Activity found to run the given Intent.
-     * 
+     *
      * @param fragment The fragment making the call.
      * @param intent The intent to start.
-     * @param requestCode Reply request code.  < 0 if reply is not requested. 
+     * @param requestCode Reply request code.  < 0 if reply is not requested.
      * @param options Additional options for how the Activity should be started.
      * See {@link android.content.Context#startActivity(Intent, Bundle)
      * Context.startActivity(Intent, Bundle)} for more details.
-     * 
+     *
      * @throws android.content.ActivityNotFoundException
-     * 
-     * @see Fragment#startActivity 
-     * @see Fragment#startActivityForResult 
+     *
+     * @see Fragment#startActivity
+     * @see Fragment#startActivityForResult
      */
     public void startActivityFromFragment(@NonNull Fragment fragment, Intent intent,
             int requestCode, @Nullable Bundle options) {
@@ -4310,14 +4355,14 @@
         } catch (RemoteException e) {
         }
     }
-    
+
     /**
      * Call this to set the result that your activity will return to its
      * caller.
-     * 
+     *
      * @param resultCode The result code to propagate back to the originating
      *                   activity, often RESULT_CANCELED or RESULT_OK
-     * 
+     *
      * @see #RESULT_CANCELED
      * @see #RESULT_OK
      * @see #RESULT_FIRST_USER
@@ -4346,7 +4391,7 @@
      * @param resultCode The result code to propagate back to the originating
      *                   activity, often RESULT_CANCELED or RESULT_OK
      * @param data The data to propagate back to the originating activity.
-     * 
+     *
      * @see #RESULT_CANCELED
      * @see #RESULT_OK
      * @see #RESULT_FIRST_USER
@@ -4364,10 +4409,10 @@
      * the data in {@link #setResult setResult()} will be sent to.  You can
      * use this information to validate that the recipient is allowed to
      * receive the data.
-     * 
+     *
      * <p class="note">Note: if the calling activity is not expecting a result (that is it
-     * did not use the {@link #startActivityForResult} 
-     * form that includes a request code), then the calling package will be 
+     * did not use the {@link #startActivityForResult}
+     * form that includes a request code), then the calling package will be
      * null.</p>
      *
      * <p class="note">Note: prior to {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2},
@@ -4375,7 +4420,7 @@
      * package was no longer running, it would return null instead of the proper package
      * name.  You can use {@link #getCallingActivity()} and retrieve the package name
      * from that instead.</p>
-     * 
+     *
      * @return The package of the activity that will receive your
      *         reply, or null if none.
      */
@@ -4393,12 +4438,12 @@
      * who the data in {@link #setResult setResult()} will be sent to.  You
      * can use this information to validate that the recipient is allowed to
      * receive the data.
-     * 
+     *
      * <p class="note">Note: if the calling activity is not expecting a result (that is it
-     * did not use the {@link #startActivityForResult} 
-     * form that includes a request code), then the calling package will be 
-     * null. 
-     * 
+     * did not use the {@link #startActivityForResult}
+     * form that includes a request code), then the calling package will be
+     * null.
+     *
      * @return The ComponentName of the activity that will receive your
      *         reply, or null if none.
      */
@@ -4417,7 +4462,7 @@
      * UI itself, but can't just finish prior to onResume() because it needs
      * to wait for a service binding or such.  Setting this to false allows
      * you to prevent your UI from being shown during that time.
-     * 
+     *
      * <p>The default value for this is taken from the
      * {@link android.R.attr#windowNoDisplay} attribute of the activity's theme.
      */
@@ -4430,7 +4475,7 @@
             }
         }
     }
-    
+
     void makeVisible() {
         if (!mWindowAdded) {
             ViewManager wm = getWindowManager();
@@ -4439,16 +4484,16 @@
         }
         mDecor.setVisibility(View.VISIBLE);
     }
-    
+
     /**
      * Check to see whether this activity is in the process of finishing,
      * either because you called {@link #finish} on it or someone else
      * has requested that it finished.  This is often used in
      * {@link #onPause} to determine whether the activity is simply pausing or
      * completely finishing.
-     * 
+     *
      * @return If the activity is finishing, returns true; else returns false.
-     * 
+     *
      * @see #finish
      */
     public boolean isFinishing() {
@@ -4468,7 +4513,7 @@
      * recreated with a new configuration. This is often used in
      * {@link #onStop} to determine whether the state needs to be cleaned up or will be passed
      * on to the next instance of the activity via {@link #onRetainNonConfigurationInstance()}.
-     * 
+     *
      * @return If the activity is being torn down in order to be recreated with a new configuration,
      * returns true; else returns false.
      */
@@ -4561,12 +4606,12 @@
     }
 
     /**
-     * This is called when a child activity of this one calls its 
+     * This is called when a child activity of this one calls its
      * {@link #finish} method.  The default implementation simply calls
      * finish() on this activity (the parent), finishing the entire group.
-     * 
+     *
      * @param child The activity making the call.
-     * 
+     *
      * @see #finish
      */
     public void finishFromChild(Activity child) {
@@ -4589,7 +4634,7 @@
     /**
      * Force finish another activity that you had previously started with
      * {@link #startActivityForResult}.
-     * 
+     *
      * @param requestCode The request code of the activity that you had
      *                    given to startActivityForResult().  If there are multiple
      *                    activities started with this request code, they
@@ -4611,7 +4656,7 @@
     /**
      * This is called when a child activity of this one calls its
      * finishActivity().
-     * 
+     *
      * @param child The activity making the call.
      * @param requestCode Request code that had been used to start the
      *                    activity.
@@ -4639,10 +4684,10 @@
      * data from it.  The <var>resultCode</var> will be
      * {@link #RESULT_CANCELED} if the activity explicitly returned that,
      * didn't return any result, or crashed during its operation.
-     * 
+     *
      * <p>You will receive this call immediately before onResume() when your
      * activity is re-starting.
-     * 
+     *
      * @param requestCode The integer request code originally supplied to
      *                    startActivityForResult(), allowing you to identify who this
      *                    result came from.
@@ -4650,7 +4695,7 @@
      *                   through its setResult().
      * @param data An Intent, which can return result data to the caller
      *               (various data can be attached to Intent "extras").
-     * 
+     *
      * @see #startActivityForResult
      * @see #createPendingResult
      * @see #setResult(int)
@@ -4680,12 +4725,12 @@
     }
 
     /**
-     * Create a new PendingIntent object which you can hand to others 
-     * for them to use to send result data back to your 
-     * {@link #onActivityResult} callback.  The created object will be either 
-     * one-shot (becoming invalid after a result is sent back) or multiple 
-     * (allowing any number of results to be sent through it). 
-     *  
+     * Create a new PendingIntent object which you can hand to others
+     * for them to use to send result data back to your
+     * {@link #onActivityResult} callback.  The created object will be either
+     * one-shot (becoming invalid after a result is sent back) or multiple
+     * (allowing any number of results to be sent through it).
+     *
      * @param requestCode Private request code for the sender that will be
      * associated with the result data when it is returned.  The sender can not
      * modify this value, allowing you to identify incoming results.
@@ -4698,12 +4743,12 @@
      * or any of the flags as supported by
      * {@link Intent#fillIn Intent.fillIn()} to control which unspecified parts
      * of the intent that can be supplied when the actual send happens.
-     * 
+     *
      * @return Returns an existing or new PendingIntent matching the given
      * parameters.  May return null only if
      * {@link PendingIntent#FLAG_NO_CREATE PendingIntent.FLAG_NO_CREATE} has been
      * supplied.
-     * 
+     *
      * @see PendingIntent
      */
     public PendingIntent createPendingResult(int requestCode, @NonNull Intent data,
@@ -4730,7 +4775,7 @@
      * orientation, the screen will immediately be changed (possibly causing
      * the activity to be restarted). Otherwise, this will be used the next
      * time the activity is visible.
-     * 
+     *
      * @param requestedOrientation An orientation constant as used in
      * {@link ActivityInfo#screenOrientation ActivityInfo.screenOrientation}.
      */
@@ -4746,13 +4791,13 @@
             mParent.setRequestedOrientation(requestedOrientation);
         }
     }
-    
+
     /**
      * Return the current requested orientation of the activity.  This will
      * either be the orientation requested in its component's manifest, or
      * the last requested orientation given to
      * {@link #setRequestedOrientation(int)}.
-     * 
+     *
      * @return Returns an orientation constant as used in
      * {@link ActivityInfo#screenOrientation ActivityInfo.screenOrientation}.
      */
@@ -4770,11 +4815,11 @@
         }
         return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
     }
-    
+
     /**
      * Return the identifier of the task this activity is in.  This identifier
      * will remain the same for the lifetime of the activity.
-     * 
+     *
      * @return Task identifier, an opaque integer.
      */
     public int getTaskId() {
@@ -4789,7 +4834,7 @@
     /**
      * Return whether this activity is the root of a task.  The root is the
      * first activity in a task.
-     * 
+     *
      * @return True if this is the root activity, else false.
      */
     public boolean isTaskRoot() {
@@ -4804,11 +4849,11 @@
     /**
      * Move the task containing this activity to the back of the activity
      * stack.  The activity's order within the task is unchanged.
-     * 
+     *
      * @param nonRoot If false then this only works if the activity is the root
      *                of a task; if true it will work for any activity in
      *                a task.
-     * 
+     *
      * @return If the task was moved (or it was already at the
      *         back) true is returned, else false.
      */
@@ -4825,7 +4870,7 @@
     /**
      * Returns class name for this activity with the package prefix removed.
      * This is the default name used to read and write settings.
-     * 
+     *
      * @return The local class name.
      */
     @NonNull
@@ -4839,10 +4884,10 @@
         }
         return cls.substring(packageLen+1);
     }
-    
+
     /**
      * Returns complete component name of this activity.
-     * 
+     *
      * @return Returns the complete component name for this activity
      */
     public ComponentName getComponentName()
@@ -4855,9 +4900,9 @@
      * that are private to this activity.  This simply calls the underlying
      * {@link #getSharedPreferences(String, int)} method by passing in this activity's
      * class name as the preferences name.
-     * 
-     * @param mode Operating mode.  Use {@link #MODE_PRIVATE} for the default 
-     *             operation, {@link #MODE_WORLD_READABLE} and 
+     *
+     * @param mode Operating mode.  Use {@link #MODE_PRIVATE} for the default
+     *             operation, {@link #MODE_WORLD_READABLE} and
      *             {@link #MODE_WORLD_WRITEABLE} to control permissions.
      *
      * @return Returns the single SharedPreferences instance that can be used
@@ -4866,12 +4911,12 @@
     public SharedPreferences getPreferences(int mode) {
         return getSharedPreferences(getLocalClassName(), mode);
     }
-    
+
     private void ensureSearchManager() {
         if (mSearchManager != null) {
             return;
         }
-        
+
         mSearchManager = new SearchManager(this, null);
     }
 
@@ -4989,7 +5034,7 @@
      * <p>
      * In order for the progress bar to be shown, the feature must be requested
      * via {@link #requestWindowFeature(int)}.
-     * 
+     *
      * @param visible Whether to show the progress bars in the title.
      */
     public final void setProgressBarVisibility(boolean visible) {
@@ -5009,14 +5054,14 @@
         getWindow().setFeatureInt(Window.FEATURE_INDETERMINATE_PROGRESS,
                 visible ? Window.PROGRESS_VISIBILITY_ON : Window.PROGRESS_VISIBILITY_OFF);
     }
-    
+
     /**
      * Sets whether the horizontal progress bar in the title should be indeterminate (the circular
      * is always indeterminate).
      * <p>
      * In order for the progress bar to be shown, the feature must be requested
      * via {@link #requestWindowFeature(int)}.
-     * 
+     *
      * @param indeterminate Whether the horizontal progress bar should be indeterminate.
      */
     public final void setProgressBarIndeterminate(boolean indeterminate) {
@@ -5024,13 +5069,13 @@
                 indeterminate ? Window.PROGRESS_INDETERMINATE_ON
                         : Window.PROGRESS_INDETERMINATE_OFF);
     }
-    
+
     /**
      * Sets the progress for the progress bars in the title.
      * <p>
      * In order for the progress bar to be shown, the feature must be requested
      * via {@link #requestWindowFeature(int)}.
-     * 
+     *
      * @param progress The progress for the progress bar. Valid ranges are from
      *            0 to 10000 (both inclusive). If 10000 is given, the progress
      *            bar will be completely filled and will fade out.
@@ -5038,7 +5083,7 @@
     public final void setProgress(int progress) {
         getWindow().setFeatureInt(Window.FEATURE_PROGRESS, progress + Window.PROGRESS_START);
     }
-    
+
     /**
      * Sets the secondary progress for the progress bar in the title. This
      * progress is drawn between the primary progress (set via
@@ -5048,7 +5093,7 @@
      * <p>
      * In order for the progress bar to be shown, the feature must be requested
      * via {@link #requestWindowFeature(int)}.
-     * 
+     *
      * @param secondaryProgress The secondary progress for the progress bar. Valid ranges are from
      *            0 to 10000 (both inclusive).
      */
@@ -5062,16 +5107,16 @@
      * volume controls.
      * <p>
      * The suggested audio stream will be tied to the window of this Activity.
-     * If the Activity is switched, the stream set here is no longer the
-     * suggested stream. The client does not need to save and restore the old
-     * suggested stream value in onPause and onResume.
-     * 
+     * Volume requests which are received while the Activity is in the
+     * foreground will affect this stream.
+     * <p>
+     * It is not guaranteed that the hardware volume controls will always change
+     * this stream's volume (for example, if a call is in progress, its stream's
+     * volume may be changed instead). To reset back to the default, use
+     * {@link AudioManager#USE_DEFAULT_STREAM_TYPE}.
+     *
      * @param streamType The type of the audio stream whose volume should be
-     *        changed by the hardware volume controls. It is not guaranteed that
-     *        the hardware volume controls will always change this stream's
-     *        volume (for example, if a call is in progress, its stream's volume
-     *        may be changed instead). To reset back to the default, use
-     *        {@link AudioManager#USE_DEFAULT_STREAM_TYPE}.
+     *            changed by the hardware volume controls.
      */
     public final void setVolumeControlStream(int streamType) {
         getWindow().setVolumeControlStream(streamType);
@@ -5080,7 +5125,7 @@
     /**
      * Gets the suggested audio stream whose volume should be changed by the
      * hardware volume controls.
-     * 
+     *
      * @return The suggested audio stream type whose volume should be changed by
      *         the hardware volume controls.
      * @see #setVolumeControlStream(int)
@@ -5088,7 +5133,40 @@
     public final int getVolumeControlStream() {
         return getWindow().getVolumeControlStream();
     }
-    
+
+    /**
+     * Sets a {@link MediaController} to send media keys and volume changes to.
+     * <p>
+     * The controller will be tied to the window of this Activity. Media key and
+     * volume events which are received while the Activity is in the foreground
+     * will be forwarded to the controller and used to invoke transport controls
+     * or adjust the volume. This may be used instead of or in addition to
+     * {@link #setVolumeControlStream} to affect a specific session instead of a
+     * specific stream.
+     * <p>
+     * It is not guaranteed that the hardware volume controls will always change
+     * this session's volume (for example, if a call is in progress, its
+     * stream's volume may be changed instead). To reset back to the default use
+     * null as the controller.
+     *
+     * @param controller The controller for the session which should receive
+     *            media keys and volume changes.
+     */
+    public final void setMediaController(MediaController controller) {
+        getWindow().setMediaController(controller);
+    }
+
+    /**
+     * Gets the controller which should be receiving media key and volume events
+     * while this activity is in the foreground.
+     *
+     * @return The controller which should receive events.
+     * @see #setMediaController(android.media.session.MediaController)
+     */
+    public final MediaController getMediaController() {
+        return getWindow().getMediaController();
+    }
+
     /**
      * Runs the specified action on the UI thread. If the current thread is the UI
      * thread, then the action is executed immediately. If the current thread is
@@ -5134,7 +5212,7 @@
         if (!"fragment".equals(name)) {
             return onCreateView(name, context, attrs);
         }
-        
+
         return mFragments.onCreateView(parent, name, context, attrs);
     }
 
@@ -5695,7 +5773,7 @@
     }
 
     // ------------------ Internal API ------------------
-    
+
     final void setParent(Activity parent) {
         mParent = parent;
     }
@@ -5709,7 +5787,7 @@
         attachBaseContext(context);
 
         mFragments.attachActivity(this, mContainer, null);
-        
+
         mWindow = PolicyManager.makeNewWindow(this);
         mWindow.setCallback(this);
         mWindow.setOnWindowDismissedCallback(this);
@@ -5804,7 +5882,7 @@
         }
         mActivityTransitionState.enterReady(this);
     }
-    
+
     final void performRestart() {
         mFragments.noteStateNotSaved();
 
@@ -5843,14 +5921,14 @@
             performStart();
         }
     }
-    
+
     final void performResume() {
         performRestart();
-        
+
         mFragments.execPendingActions();
-        
+
         mLastNonConfigurationInstances = null;
-        
+
         mCalled = false;
         // mResumed is set by the instrumentation
         mInstrumentation.callActivityOnResume(this);
@@ -5862,10 +5940,10 @@
 
         // Now really resume, and install the current status bar and menu.
         mCalled = false;
-        
+
         mFragments.dispatchResume();
         mFragments.execPendingActions();
-        
+
         onPostResume();
         if (!mCalled) {
             throw new SuperNotCalledException(
@@ -5888,12 +5966,12 @@
         }
         mResumed = false;
     }
-    
+
     final void performUserLeaving() {
         onUserInteraction();
         onUserLeaveHint();
     }
-    
+
     final void performStop() {
         mDoReportFullyDrawn = false;
         if (mLoadersStarted) {
@@ -5906,7 +5984,7 @@
                 }
             }
         }
-        
+
         if (!mStopped) {
             if (mWindow != null) {
                 mWindow.closeAllPanels();
@@ -5915,9 +5993,9 @@
             if (mToken != null && mParent == null) {
                 WindowManagerGlobal.getInstance().setStoppedState(mToken, true);
             }
-            
+
             mFragments.dispatchStop();
-            
+
             mCalled = false;
             mInstrumentation.callActivityOnStop(this);
             if (!mCalled) {
@@ -5925,7 +6003,7 @@
                     "Activity " + mComponent.toShortString() +
                     " did not call through to super.onStop()");
             }
-    
+
             synchronized (mManagedCursors) {
                 final int N = mManagedCursors.size();
                 for (int i=0; i<N; i++) {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index e1d0b86..c8cab6f 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -2456,7 +2456,10 @@
     }
 
     /**
-     * @hide
+     * Return whether currently in lock task mode.  When in this mode
+     * no new tasks can be created or switched to.
+     *
+     * @see Activity#startLockTask()
      */
     public boolean isInLockTaskMode() {
         try {
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index 15be9b1..e074219 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -403,20 +403,18 @@
         view.layout(left, top, right, bottom);
     }
 
-    protected ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> setSharedElementState(
+    protected ArrayList<SharedElementOriginalState> setSharedElementState(
             Bundle sharedElementState, final ArrayList<View> snapshots) {
-        ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageState =
-                new ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>>();
+        ArrayList<SharedElementOriginalState> originalImageState =
+                new ArrayList<SharedElementOriginalState>();
         if (sharedElementState != null) {
             int[] tempLoc = new int[2];
             for (int i = 0; i < mSharedElementNames.size(); i++) {
                 View sharedElement = mSharedElements.get(i);
                 String name = mSharedElementNames.get(i);
-                Pair<ImageView.ScaleType, Matrix> originalState = getOldImageState(sharedElement,
+                SharedElementOriginalState originalState = getOldSharedElementState(sharedElement,
                         name, sharedElementState);
-                if (originalState != null) {
-                    originalImageState.put((ImageView) sharedElement, originalState);
-                }
+                originalImageState.add(originalState);
                 View parent = (View) sharedElement.getParent();
                 parent.getLocationOnScreen(tempLoc);
                 setSharedElementState(sharedElement, name, sharedElementState, tempLoc);
@@ -438,29 +436,34 @@
         return originalImageState;
     }
 
-    private static Pair<ImageView.ScaleType, Matrix> getOldImageState(View view, String name,
+    private static SharedElementOriginalState getOldSharedElementState(View view, String name,
             Bundle transitionArgs) {
+
+        SharedElementOriginalState state = new SharedElementOriginalState();
+        state.mLeft = view.getLeft();
+        state.mTop = view.getTop();
+        state.mRight = view.getRight();
+        state.mBottom = view.getBottom();
+        state.mMeasuredWidth = view.getMeasuredWidth();
+        state.mMeasuredHeight = view.getMeasuredHeight();
         if (!(view instanceof ImageView)) {
-            return null;
+            return state;
         }
         Bundle bundle = transitionArgs.getBundle(name);
         if (bundle == null) {
-            return null;
+            return state;
         }
         int scaleTypeInt = bundle.getInt(KEY_SCALE_TYPE, -1);
         if (scaleTypeInt < 0) {
-            return null;
+            return state;
         }
 
         ImageView imageView = (ImageView) view;
-        ImageView.ScaleType originalScaleType = imageView.getScaleType();
-
-        Matrix originalMatrix = null;
-        if (originalScaleType == ImageView.ScaleType.MATRIX) {
-            originalMatrix = new Matrix(imageView.getImageMatrix());
+        state.mScaleType = imageView.getScaleType();
+        if (state.mScaleType == ImageView.ScaleType.MATRIX) {
+            state.mMatrix = new Matrix(imageView.getImageMatrix());
         }
-
-        return Pair.create(originalScaleType, originalMatrix);
+        return state;
     }
 
     protected ArrayList<View> createSnapshots(Bundle state, Collection<String> names) {
@@ -489,13 +492,26 @@
         return snapshots;
     }
 
-    protected static void setOriginalImageViewState(
-            ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalState) {
+    protected static void setOriginalSharedElementState(ArrayList<View> sharedElements,
+            ArrayList<SharedElementOriginalState> originalState) {
         for (int i = 0; i < originalState.size(); i++) {
-            ImageView imageView = originalState.keyAt(i);
-            Pair<ImageView.ScaleType, Matrix> state = originalState.valueAt(i);
-            imageView.setScaleType(state.first);
-            imageView.setImageMatrix(state.second);
+            View view = sharedElements.get(i);
+            SharedElementOriginalState state = originalState.get(i);
+            if (view instanceof ImageView && state.mScaleType != null) {
+                ImageView imageView = (ImageView) view;
+                imageView.setScaleType(state.mScaleType);
+                if (state.mScaleType == ImageView.ScaleType.MATRIX) {
+                  imageView.setImageMatrix(state.mMatrix);
+                }
+            }
+            // origignal widthspec might be AT_MOST,  but it should work for most
+            // cases.
+            int widthSpec = View.MeasureSpec.makeMeasureSpec(state.mMeasuredWidth,
+                    View.MeasureSpec.EXACTLY);
+            int heightSpec = View.MeasureSpec.makeMeasureSpec(state.mMeasuredHeight,
+                    View.MeasureSpec.EXACTLY);
+            view.measure(widthSpec, heightSpec);
+            view.layout(state.mLeft, state.mTop, state.mRight, state.mBottom);
         }
     }
 
@@ -622,4 +638,15 @@
         }
     }
 
+    static class SharedElementOriginalState {
+        int mLeft;
+        int mTop;
+        int mRight;
+        int mBottom;
+        int mMeasuredWidth;
+        int mMeasuredHeight;
+        ImageView.ScaleType mScaleType;
+        Matrix mMatrix;
+    }
+
 }
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 2935b8e..264553b 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -33,6 +33,7 @@
 import android.content.pm.IPackageMoveObserver;
 import android.content.pm.IPackageStatsObserver;
 import android.content.pm.InstrumentationInfo;
+import android.content.pm.KeySet;
 import android.content.pm.ManifestDigest;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
@@ -52,6 +53,7 @@
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.IBinder;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -59,6 +61,7 @@
 import android.util.ArrayMap;
 import android.util.Log;
 import android.view.Display;
+import com.android.internal.util.Preconditions;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
@@ -1447,6 +1450,62 @@
         return false;
     }
 
+    @Override
+    public KeySet getKeySetByAlias(String packageName, String alias) {
+        Preconditions.checkNotNull(packageName);
+        Preconditions.checkNotNull(alias);
+        IBinder keySetToken;
+        try {
+            keySetToken = mPM.getKeySetByAlias(packageName, alias);
+        } catch (RemoteException e) {
+            return null;
+        }
+        if (keySetToken == null) {
+            return null;
+        }
+        return new KeySet(keySetToken);
+    }
+
+    @Override
+    public KeySet getSigningKeySet(String packageName) {
+        Preconditions.checkNotNull(packageName);
+        IBinder keySetToken;
+        try {
+            keySetToken = mPM.getSigningKeySet(packageName);
+        } catch (RemoteException e) {
+            return null;
+        }
+        if (keySetToken == null) {
+            return null;
+        }
+        return new KeySet(keySetToken);
+    }
+
+
+    @Override
+    public boolean isSignedBy(String packageName, KeySet ks) {
+        Preconditions.checkNotNull(packageName);
+        Preconditions.checkNotNull(ks);
+        IBinder keySetToken = ks.getToken();
+        try {
+            return mPM.isPackageSignedByKeySet(packageName, keySetToken);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    @Override
+    public boolean isSignedByExactly(String packageName, KeySet ks) {
+        Preconditions.checkNotNull(packageName);
+        Preconditions.checkNotNull(ks);
+        IBinder keySetToken = ks.getToken();
+        try {
+            return mPM.isPackageSignedByKeySetExactly(packageName, keySetToken);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
     /**
      * @hide
      */
@@ -1461,7 +1520,7 @@
     }
 
     @Override
-    public PackageInstaller getPackageInstaller() {
+    public PackageInstaller getInstaller() {
         try {
             return new PackageInstaller(this, mPM.getPackageInstaller(), mContext.getPackageName(),
                     mContext.getUserId());
@@ -1470,6 +1529,15 @@
         }
     }
 
+    @Override
+    public boolean isPackageAvailable(String packageName) {
+        try {
+            return mPM.isPackageAvailable(packageName, mContext.getUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
     /**
      * @hide
      */
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index bbfb05e..b9de220 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -247,6 +247,8 @@
     @GuardedBy("mSync")
     private File mFilesDir;
     @GuardedBy("mSync")
+    private File mNoBackupFilesDir;
+    @GuardedBy("mSync")
     private File mCacheDir;
 
     @GuardedBy("mSync")
@@ -558,9 +560,7 @@
 
         registerService(TELECOMM_SERVICE, new ServiceFetcher() {
                 public Object createService(ContextImpl ctx) {
-                    IBinder b = ServiceManager.getService(TELECOMM_SERVICE);
-                    return new TelecommManager(ctx.getOuterContext(),
-                            ITelecommService.Stub.asInterface(b));
+                    return new TelecommManager(ctx.getOuterContext());
                 }});
 
         registerService(PHONE_SERVICE, new ServiceFetcher() {
@@ -963,27 +963,42 @@
         return f.delete();
     }
 
+    // Common-path handling of app data dir creation
+    private static File createFilesDirLocked(File file) {
+        if (!file.exists()) {
+            if (!file.mkdirs()) {
+                if (file.exists()) {
+                    // spurious failure; probably racing with another process for this app
+                    return file;
+                }
+                Log.w(TAG, "Unable to create files subdir " + file.getPath());
+                return null;
+            }
+            FileUtils.setPermissions(
+                    file.getPath(),
+                    FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
+                    -1, -1);
+        }
+        return file;
+    }
+
     @Override
     public File getFilesDir() {
         synchronized (mSync) {
             if (mFilesDir == null) {
                 mFilesDir = new File(getDataDirFile(), "files");
             }
-            if (!mFilesDir.exists()) {
-                if(!mFilesDir.mkdirs()) {
-                    if (mFilesDir.exists()) {
-                        // spurious failure; probably racing with another process for this app
-                        return mFilesDir;
-                    }
-                    Log.w(TAG, "Unable to create files directory " + mFilesDir.getPath());
-                    return null;
-                }
-                FileUtils.setPermissions(
-                        mFilesDir.getPath(),
-                        FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
-                        -1, -1);
+            return createFilesDirLocked(mFilesDir);
+        }
+    }
+
+    @Override
+    public File getNoBackupFilesDir() {
+        synchronized (mSync) {
+            if (mNoBackupFilesDir == null) {
+                mNoBackupFilesDir = new File(getDataDirFile(), "no_backup");
             }
-            return mFilesDir;
+            return createFilesDirLocked(mNoBackupFilesDir);
         }
     }
 
@@ -1035,22 +1050,8 @@
             if (mCacheDir == null) {
                 mCacheDir = new File(getDataDirFile(), "cache");
             }
-            if (!mCacheDir.exists()) {
-                if(!mCacheDir.mkdirs()) {
-                    if (mCacheDir.exists()) {
-                        // spurious failure; probably racing with another process for this app
-                        return mCacheDir;
-                    }
-                    Log.w(TAG, "Unable to create cache directory " + mCacheDir.getAbsolutePath());
-                    return null;
-                }
-                FileUtils.setPermissions(
-                        mCacheDir.getPath(),
-                        FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
-                        -1, -1);
-            }
+            return createFilesDirLocked(mCacheDir);
         }
-        return mCacheDir;
     }
 
     @Override
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index f50c93b..1326064 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -282,7 +282,7 @@
         ArrayList<View> sharedElementSnapshots = createSnapshots(sharedElementState,
                 mSharedElementNames);
         setTransitionAlpha(mSharedElements, 1);
-        ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageViewState =
+        ArrayList<SharedElementOriginalState> originalImageViewState =
                 setSharedElementState(sharedElementState, sharedElementSnapshots);
         requestLayoutForSharedElements();
 
@@ -294,7 +294,7 @@
             startEnterTransition(transition);
         }
 
-        setOriginalImageViewState(originalImageViewState);
+        setOriginalSharedElementState(mSharedElements, originalImageViewState);
 
         if (mResultReceiver != null) {
             // We can't trust that the view will disappear on the same frame that the shared
diff --git a/core/java/android/app/PackageInstallObserver.java b/core/java/android/app/PackageInstallObserver.java
index 7117111..1b2504e 100644
--- a/core/java/android/app/PackageInstallObserver.java
+++ b/core/java/android/app/PackageInstallObserver.java
@@ -25,7 +25,7 @@
         @Override
         public void packageInstalled(String basePackageName, Bundle extras, int returnCode,
                 String msg) {
-            PackageInstallObserver.this.packageInstalled(basePackageName, extras, returnCode);
+            PackageInstallObserver.this.packageInstalled(basePackageName, extras, returnCode, msg);
         }
     };
 
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 886f1a6..e2a86e8 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -247,12 +247,40 @@
             throws IOException;
 
     /**
-     * The default implementation backs up the entirety of the application's "owned"
-     * file system trees to the output.
+     * The application is having its entire file system contents backed up.  {@code data}
+     * points to the backup destination, and the app has the opportunity to choose which
+     * files are to be stored.  To commit a file as part of the backup, call the
+     * {@link #fullBackupFile(File, FullBackupDataOutput)} helper method.  After all file
+     * data is written to the output, the agent returns from this method and the backup
+     * operation concludes.
+     *
+     * <p>Certain parts of the app's data are never backed up even if the app explicitly
+     * sends them to the output:
+     *
+     * <ul>
+     * <li>The contents of the {@link #getCacheDir()} directory</li>
+     * <li>The contents of the {@link #getNoBackupFilesDir()} directory</li>
+     * <li>The contents of the app's shared library directory</li>
+     * </ul>
+     *
+     * <p>The default implementation of this method backs up the entirety of the
+     * application's "owned" file system trees to the output other than the few exceptions
+     * listed above.  Apps only need to override this method if they need to impose special
+     * limitations on which files are being stored beyond the control that
+     * {@link #getNoBackupFilesDir()} offers.
+     *
+     * @param data A structured wrapper pointing to the backup destination.
+     * @throws IOException
+     *
+     * @see Context#getNoBackupFilesDir()
+     * @see #fullBackupFile(File, FullBackupDataOutput)
+     * @see #onRestoreFile(ParcelFileDescriptor, long, File, int, long, long)
      */
     public void onFullBackup(FullBackupDataOutput data) throws IOException {
         ApplicationInfo appInfo = getApplicationInfo();
 
+        // Note that we don't need to think about the no_backup dir because it's outside
+        // all of the ones we will be traversing
         String rootDir = new File(appInfo.dataDir).getCanonicalPath();
         String filesDir = getFilesDir().getCanonicalPath();
         String databaseDir = getDatabasePath("foo").getParentFile().getCanonicalPath();
@@ -311,6 +339,10 @@
      * to place it with the proper location and permissions on the device where the
      * data is restored.
      *
+     * <p class="note">It is safe to explicitly back up files underneath your application's
+     * {@link #getNoBackupFilesDir()} directory, and they will be restored to that
+     * location correctly.
+     *
      * @param file The file to be backed up.  The file must exist and be readable by
      *     the caller.
      * @param output The destination to which the backed-up file data will be sent.
@@ -319,6 +351,7 @@
         // Look up where all of our various well-defined dir trees live on this device
         String mainDir;
         String filesDir;
+        String nbFilesDir;
         String dbDir;
         String spDir;
         String cacheDir;
@@ -331,6 +364,7 @@
         try {
             mainDir = new File(appInfo.dataDir).getCanonicalPath();
             filesDir = getFilesDir().getCanonicalPath();
+            nbFilesDir = getNoBackupFilesDir().getCanonicalPath();
             dbDir = getDatabasePath("foo").getParentFile().getCanonicalPath();
             spDir = getSharedPrefsFile("foo").getParentFile().getCanonicalPath();
             cacheDir = getCacheDir().getCanonicalPath();
@@ -354,8 +388,10 @@
             return;
         }
 
-        if (filePath.startsWith(cacheDir) || filePath.startsWith(libDir)) {
-            Log.w(TAG, "lib and cache files are not backed up");
+        if (filePath.startsWith(cacheDir)
+                || filePath.startsWith(libDir)
+                || filePath.startsWith(nbFilesDir)) {
+            Log.w(TAG, "lib, cache, and no_backup files are not backed up");
             return;
         }
 
@@ -508,6 +544,8 @@
                     mode = -1;  // < 0 is a token to skip attempting a chmod()
                 }
             }
+        } else if (domain.equals(FullBackup.NO_BACKUP_TREE_TOKEN)) {
+            basePath = getNoBackupFilesDir().getCanonicalPath();
         } else {
             // Not a supported location
             Log.i(TAG, "Unrecognized domain " + domain);
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index 6ebb6c4..e5b47c6 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -40,6 +40,7 @@
     public static final String OBB_TREE_TOKEN = "obb";
     public static final String ROOT_TREE_TOKEN = "r";
     public static final String DATA_TREE_TOKEN = "f";
+    public static final String NO_BACKUP_TREE_TOKEN = "nb";
     public static final String DATABASE_TREE_TOKEN = "db";
     public static final String SHAREDPREFS_TREE_TOKEN = "sp";
     public static final String MANAGED_EXTERNAL_TREE_TOKEN = "ef";
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 535aaa1..a52cbdd 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -647,6 +647,26 @@
     public abstract File getFilesDir();
 
     /**
+     * Returns the absolute path to the directory on the filesystem similar to
+     * {@link #getFilesDir()}.  The difference is that files placed under this
+     * directory will be excluded from automatic backup to remote storage.  See
+     * {@link android.app.backup.BackupAgent BackupAgent} for a full discussion
+     * of the automatic backup mechanism in Android.
+     *
+     * <p>No permissions are required to read or write to the returned path, since this
+     * path is internal storage.
+     *
+     * @return The path of the directory holding application files that will not be
+     *         automatically backed up to remote storage.
+     *
+     * @see #openFileOutput
+     * @see #getFileStreamPath
+     * @see #getDir
+     * @see android.app.backup.BackupAgent
+     */
+    public abstract File getNoBackupFilesDir();
+
+    /**
      * Returns the absolute path to the directory on the primary external filesystem
      * (that is somewhere on {@link android.os.Environment#getExternalStorageDirectory()
      * Environment.getExternalStorageDirectory()}) where the application can
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index dbf9122..13eed07 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -200,7 +200,12 @@
     public File getFilesDir() {
         return mBase.getFilesDir();
     }
-    
+
+    @Override
+    public File getNoBackupFilesDir() {
+        return mBase.getNoBackupFilesDir();
+    }
+
     @Override
     public File getExternalFilesDir(String type) {
         return mBase.getExternalFilesDir(type);
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 58d3526..3a98f5d 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -429,4 +429,9 @@
 
     boolean setBlockUninstallForUser(String packageName, boolean blockUninstall, int userId);
     boolean getBlockUninstallForUser(String packageName, int userId);
+
+    IBinder getKeySetByAlias(String packageName, String alias);
+    IBinder getSigningKeySet(String packageName);
+    boolean isPackageSignedByKeySet(String packageName, IBinder ks);
+    boolean isPackageSignedByKeySetExactly(String packageName, IBinder ks);
 }
diff --git a/core/java/android/content/pm/InstallSessionInfo.java b/core/java/android/content/pm/InstallSessionInfo.java
index 45606d1..3336727 100644
--- a/core/java/android/content/pm/InstallSessionInfo.java
+++ b/core/java/android/content/pm/InstallSessionInfo.java
@@ -20,15 +20,25 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
-/** {@hide} */
+/**
+ * Details for an active install session.
+ */
 public class InstallSessionInfo implements Parcelable {
+
+    /** {@hide} */
     public int sessionId;
+    /** {@hide} */
     public String installerPackageName;
+    /** {@hide} */
     public int progress;
 
-    public boolean fullInstall;
+    /** {@hide} */
+    public int mode;
+    /** {@hide} */
     public String packageName;
+    /** {@hide} */
     public Bitmap icon;
+    /** {@hide} */
     public CharSequence title;
 
     /** {@hide} */
@@ -41,12 +51,62 @@
         installerPackageName = source.readString();
         progress = source.readInt();
 
-        fullInstall = source.readInt() != 0;
+        mode = source.readInt();
         packageName = source.readString();
         icon = source.readParcelable(null);
         title = source.readString();
     }
 
+    /**
+     * Return the ID for this session.
+     */
+    public int getSessionId() {
+        return sessionId;
+    }
+
+    /**
+     * Return the package name of the app that owns this session.
+     */
+    public String getInstallerPackageName() {
+        return installerPackageName;
+    }
+
+    /**
+     * Return current overall progress of this session, between 0 and 100.
+     * <p>
+     * Note that this progress may not directly correspond to the value reported
+     * by {@link PackageInstaller.Session#setProgress(int)}, as the system may
+     * carve out a portion of the overall progress to represent its own internal
+     * installation work.
+     */
+    public int getProgress() {
+        return progress;
+    }
+
+    /**
+     * Return the package name this session is working with. May be {@code null}
+     * if unknown.
+     */
+    public String getPackageName() {
+        return packageName;
+    }
+
+    /**
+     * Return an icon representing the app being installed. May be {@code null}
+     * if unavailable.
+     */
+    public Bitmap getIcon() {
+        return icon;
+    }
+
+    /**
+     * Return a title representing the app being installed. May be {@code null}
+     * if unavailable.
+     */
+    public CharSequence getTitle() {
+        return title;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -58,7 +118,7 @@
         dest.writeString(installerPackageName);
         dest.writeInt(progress);
 
-        dest.writeInt(fullInstall ? 1 : 0);
+        dest.writeInt(mode);
         dest.writeString(packageName);
         dest.writeParcelable(icon, flags);
         dest.writeString(title != null ? title.toString() : null);
diff --git a/core/java/android/content/pm/InstallSessionParams.java b/core/java/android/content/pm/InstallSessionParams.java
index 43e3314..e039699 100644
--- a/core/java/android/content/pm/InstallSessionParams.java
+++ b/core/java/android/content/pm/InstallSessionParams.java
@@ -16,6 +16,7 @@
 
 package android.content.pm;
 
+import android.content.Intent;
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.Parcel;
@@ -23,13 +24,22 @@
 
 import com.android.internal.util.IndentingPrintWriter;
 
-/** {@hide} */
+/**
+ * Parameters for creating a new {@link PackageInstaller.Session}.
+ */
 public class InstallSessionParams implements Parcelable {
 
     // TODO: extend to support remaining VerificationParams
 
     /** {@hide} */
-    public boolean fullInstall;
+    public static final int MODE_INVALID = 0;
+    /** {@hide} */
+    public static final int MODE_FULL_INSTALL = 1;
+    /** {@hide} */
+    public static final int MODE_INHERIT_EXISTING = 2;
+
+    /** {@hide} */
+    public int mode = MODE_INVALID;
     /** {@hide} */
     public int installFlags;
     /** {@hide} */
@@ -58,7 +68,7 @@
 
     /** {@hide} */
     public InstallSessionParams(Parcel source) {
-        fullInstall = source.readInt() != 0;
+        mode = source.readInt();
         installFlags = source.readInt();
         installLocation = source.readInt();
         signatures = (Signature[]) source.readParcelableArray(null);
@@ -72,53 +82,108 @@
         abiOverride = source.readString();
     }
 
-    public void setFullInstall(boolean fullInstall) {
-        this.fullInstall = fullInstall;
+    /**
+     * Set session mode indicating that it should fully replace any existing
+     * APKs for this application.
+     */
+    public void setModeFullInstall() {
+        this.mode = MODE_FULL_INSTALL;
     }
 
-    public void setInstallFlags(int installFlags) {
-        this.installFlags = installFlags;
+    /**
+     * Set session mode indicating that it should inherit any existing APKs for
+     * this application, unless they are explicitly overridden (by split name)
+     * in the session.
+     */
+    public void setModeInheritExisting() {
+        this.mode = MODE_INHERIT_EXISTING;
     }
 
+    /**
+     * Provide value of {@link PackageInfo#installLocation}, which may be used
+     * to determine where the app will be staged. Defaults to
+     * {@link PackageInfo#INSTALL_LOCATION_INTERNAL_ONLY}.
+     */
     public void setInstallLocation(int installLocation) {
         this.installLocation = installLocation;
     }
 
+    /**
+     * Optionally provide the required value of {@link PackageInfo#signatures}.
+     * This can be used to assert that all staged APKs have been signed with
+     * this set of specific certificates. Regardless of this value, all APKs in
+     * the application must have the same signing certificates.
+     */
     public void setSignatures(Signature[] signatures) {
         this.signatures = signatures;
     }
 
+    /**
+     * Indicate the expected growth in disk usage resulting from this session.
+     * This may be used to ensure enough disk space exists before proceeding, or
+     * to estimate container size for installations living on external storage.
+     * <p>
+     * This value should only reflect APK sizes.
+     */
     public void setDeltaSize(long deltaSize) {
         this.deltaSize = deltaSize;
     }
 
+    /**
+     * Set the maximum progress for this session, used for normalization
+     * purposes.
+     *
+     * @see PackageInstaller.Session#setProgress(int)
+     */
     public void setProgressMax(int progressMax) {
         this.progressMax = progressMax;
     }
 
+    /**
+     * Optionally set the package name this session will be working with. It's
+     * strongly recommended that you provide this value when known.
+     */
     public void setPackageName(String packageName) {
         this.packageName = packageName;
     }
 
+    /**
+     * Optionally set an icon representing the app being installed.
+     */
     public void setIcon(Bitmap icon) {
         this.icon = icon;
     }
 
+    /**
+     * Optionally set a title representing the app being installed.
+     */
     public void setTitle(CharSequence title) {
         this.title = title;
     }
 
+    /**
+     * Optionally set the URI where this package was downloaded from. Used for
+     * verification purposes.
+     *
+     * @see Intent#EXTRA_ORIGINATING_URI
+     */
     public void setOriginatingUri(Uri originatingUri) {
         this.originatingUri = originatingUri;
     }
 
+    /**
+     * Optionally set the URI that referred you to install this package. Used
+     * for verification purposes.
+     *
+     * @see Intent#EXTRA_REFERRER
+     */
     public void setReferrerUri(Uri referrerUri) {
         this.referrerUri = referrerUri;
     }
 
     /** {@hide} */
     public void dump(IndentingPrintWriter pw) {
-        pw.printPair("fullInstall", fullInstall);
+        pw.printPair("mode", mode);
         pw.printHexPair("installFlags", installFlags);
         pw.printPair("installLocation", installLocation);
         pw.printPair("signatures", (signatures != null));
@@ -140,7 +205,7 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(fullInstall ? 1 : 0);
+        dest.writeInt(mode);
         dest.writeInt(installFlags);
         dest.writeInt(installLocation);
         dest.writeParcelableArray(signatures, flags);
diff --git a/core/java/android/content/pm/KeySet.java b/core/java/android/content/pm/KeySet.java
index 0ef09a4..fcdaa18 100644
--- a/core/java/android/content/pm/KeySet.java
+++ b/core/java/android/content/pm/KeySet.java
@@ -16,19 +16,36 @@
 
 package android.content.pm;
 
-import android.os.Binder;
+import android.os.IBinder;
 
-/** @hide */
+/**
+ * Represents a {@code KeySet} that has been declared in the AndroidManifest.xml
+ * file for the application.  A {@code KeySet} can be used explicitly to
+ * represent a trust relationship with other applications on the device.
+ */
 public class KeySet {
 
-    private Binder token;
+    private IBinder token;
 
     /** @hide */
-    public KeySet(Binder token) {
+    public KeySet(IBinder token) {
+        if (token == null) {
+            throw new NullPointerException("null value for KeySet IBinder token");
+        }
         this.token = token;
     }
 
-    Binder getToken() {
+    /** @hide */
+    public IBinder getToken() {
         return token;
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof KeySet) {
+            KeySet ks = (KeySet) o;
+            return token == ks.token;
+        }
+        return false;
+    }
 }
\ No newline at end of file
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index ef0c4d5..8f0c249 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -31,6 +31,11 @@
     public String packageName;
 
     /**
+     * The names of any installed split APKs for this package.
+     */
+    public String[] splitNames;
+
+    /**
      * The version number of this package, as specified by the &lt;manifest&gt;
      * tag's {@link android.R.styleable#AndroidManifest_versionCode versionCode}
      * attribute.
@@ -190,24 +195,25 @@
      * @hide
      */
     public static final int INSTALL_LOCATION_UNSPECIFIED = -1;
+
     /**
-     * Constant corresponding to <code>auto</code> in
-     * the {@link android.R.attr#installLocation} attribute.
-     * @hide
+     * Constant corresponding to <code>auto</code> in the
+     * {@link android.R.attr#installLocation} attribute.
      */
     public static final int INSTALL_LOCATION_AUTO = 0;
+
     /**
-     * Constant corresponding to <code>internalOnly</code> in
-     * the {@link android.R.attr#installLocation} attribute.
-     * @hide
+     * Constant corresponding to <code>internalOnly</code> in the
+     * {@link android.R.attr#installLocation} attribute.
      */
     public static final int INSTALL_LOCATION_INTERNAL_ONLY = 1;
+
     /**
-     * Constant corresponding to <code>preferExternal</code> in
-     * the {@link android.R.attr#installLocation} attribute.
-     * @hide
+     * Constant corresponding to <code>preferExternal</code> in the
+     * {@link android.R.attr#installLocation} attribute.
      */
     public static final int INSTALL_LOCATION_PREFER_EXTERNAL = 2;
+
     /**
      * Flag for {@link #requiredForProfile}
      * The application will always be installed for a restricted profile.
@@ -222,12 +228,10 @@
     public static final int MANAGED_PROFILE = 2;
 
     /**
-     * The install location requested by the activity.  From the
+     * The install location requested by the package. From the
      * {@link android.R.attr#installLocation} attribute, one of
-     * {@link #INSTALL_LOCATION_AUTO},
-     * {@link #INSTALL_LOCATION_INTERNAL_ONLY},
+     * {@link #INSTALL_LOCATION_AUTO}, {@link #INSTALL_LOCATION_INTERNAL_ONLY},
      * {@link #INSTALL_LOCATION_PREFER_EXTERNAL}
-     * @hide
      */
     public int installLocation = INSTALL_LOCATION_INTERNAL_ONLY;
 
@@ -269,6 +273,7 @@
 
     public void writeToParcel(Parcel dest, int parcelableFlags) {
         dest.writeString(packageName);
+        dest.writeStringArray(splitNames);
         dest.writeInt(versionCode);
         dest.writeString(versionName);
         dest.writeString(sharedUserId);
@@ -314,6 +319,7 @@
 
     private PackageInfo(Parcel source) {
         packageName = source.readString();
+        splitNames = source.readStringArray();
         versionCode = source.readInt();
         versionName = source.readString();
         sharedUserId = source.readString();
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 401be06..348a7ad 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -30,7 +30,31 @@
 import java.io.OutputStream;
 import java.util.List;
 
-/** {@hide} */
+/**
+ * Offers the ability to install, upgrade, and remove applications on the
+ * device. This includes support for apps packaged either as a single
+ * "monolithic" APK, or apps packaged as multiple "split" APKs.
+ * <p>
+ * An app is delivered for installation through a
+ * {@link PackageInstaller.Session}, which any app can create. Once the session
+ * is created, the installer can stream one or more APKs into place until it
+ * decides to either commit or destroy the session. Committing may require user
+ * intervention to complete the installation.
+ * <p>
+ * Sessions can install brand new apps, upgrade existing apps, or add new splits
+ * onto an existing app.
+ * <p>
+ * Apps packaged into multiple split APKs always consist of a single "base" APK
+ * (with a {@code null} split name) and zero or more "split" APKs (with unique
+ * split names). Any subset of these APKs can be installed together, as long as
+ * the following constraints are met:
+ * <ul>
+ * <li>All APKs must have the exact same package name, version code, and signing
+ * certificates.
+ * <li>All installations must contain a single base APK.
+ * <li>All APKs must have unique split names.
+ * </ul>
+ */
 public class PackageInstaller {
     private final PackageManager mPm;
     private final IPackageInstaller mInstaller;
@@ -46,16 +70,18 @@
         mUserId = userId;
     }
 
+    /**
+     * Quickly test if the given package is already available on the device.
+     * This is typically used in multi-user scenarios where another user on the
+     * device has already installed the package.
+     *
+     * @hide
+     */
     public boolean isPackageAvailable(String packageName) {
-        try {
-            final ApplicationInfo info = mPm.getApplicationInfo(packageName,
-                    PackageManager.GET_UNINSTALLED_PACKAGES);
-            return ((info.flags & ApplicationInfo.FLAG_INSTALLED) != 0);
-        } catch (NameNotFoundException e) {
-            return false;
-        }
+        return mPm.isPackageAvailable(packageName);
     }
 
+    /** {@hide} */
     public void installAvailablePackage(String packageName, PackageInstallObserver observer) {
         int returnCode;
         try {
@@ -66,6 +92,18 @@
         observer.packageInstalled(packageName, null, returnCode);
     }
 
+    /**
+     * Create a new session using the given parameters, returning a unique ID
+     * that represents the session. Once created, the session can be opened
+     * multiple times across multiple device boots.
+     * <p>
+     * The system may automatically destroy sessions that have not been
+     * finalized (either committed or abandoned) within a reasonable period of
+     * time, typically on the order of a day.
+     *
+     * @throws IOException if parameters were unsatisfiable, such as lack of
+     *             disk space or unavailable media.
+     */
     public int createSession(InstallSessionParams params) throws IOException {
         try {
             return mInstaller.createSession(mInstallerPackageName, params, mUserId);
@@ -77,6 +115,9 @@
         }
     }
 
+    /**
+     * Open an existing session to actively perform work.
+     */
     public Session openSession(int sessionId) {
         try {
             return new Session(mInstaller.openSession(sessionId));
@@ -85,7 +126,10 @@
         }
     }
 
-    public List<InstallSessionInfo> getSessions() {
+    /**
+     * Return list of all active install sessions on the device.
+     */
+    public List<InstallSessionInfo> getActiveSessions() {
         // TODO: filter based on caller
         // TODO: let launcher app see all active sessions
         try {
@@ -95,6 +139,11 @@
         }
     }
 
+    /**
+     * Uninstall the given package, removing it completely from the device. This
+     * method is only available to the current "installer of record" for the
+     * package.
+     */
     public void uninstall(String packageName, UninstallResultCallback callback) {
         try {
             mInstaller.uninstall(packageName, 0,
@@ -104,6 +153,11 @@
         }
     }
 
+    /**
+     * Uninstall only a specific split from the given package.
+     *
+     * @hide
+     */
     public void uninstall(String packageName, String splitName, UninstallResultCallback callback) {
         try {
             mInstaller.uninstallSplit(packageName, splitName, 0,
@@ -113,6 +167,9 @@
         }
     }
 
+    /**
+     * Events for observing session lifecycle.
+     */
     public static abstract class SessionObserver {
         private final IPackageInstallerObserver.Stub mBinder = new IPackageInstallerObserver.Stub() {
             @Override
@@ -127,7 +184,7 @@
 
             @Override
             public void onSessionFinished(int sessionId, boolean success) {
-                SessionObserver.this.onFinished(sessionId, success);
+                SessionObserver.this.onFinalized(sessionId, success);
             }
         };
 
@@ -136,11 +193,30 @@
             return mBinder;
         }
 
+        /**
+         * New session has been created.
+         */
         public abstract void onCreated(InstallSessionInfo info);
+
+        /**
+         * Progress for given session has been updated.
+         * <p>
+         * Note that this progress may not directly correspond to the value
+         * reported by {@link PackageInstaller.Session#setProgress(int)}, as the
+         * system may carve out a portion of the overall progress to represent
+         * its own internal installation work.
+         */
         public abstract void onProgress(int sessionId, int progress);
-        public abstract void onFinished(int sessionId, boolean success);
+
+        /**
+         * Session has been finalized, either with success or failure.
+         */
+        public abstract void onFinalized(int sessionId, boolean success);
     }
 
+    /**
+     * Register to watch for session lifecycle events.
+     */
     public void registerSessionObserver(SessionObserver observer) {
         try {
             mInstaller.registerObserver(observer.getBinder(), mUserId);
@@ -149,6 +225,9 @@
         }
     }
 
+    /**
+     * Unregister an existing observer.
+     */
     public void unregisterSessionObserver(SessionObserver observer) {
         try {
             mInstaller.unregisterObserver(observer.getBinder(), mUserId);
@@ -177,6 +256,10 @@
             mSession = session;
         }
 
+        /**
+         * Set current progress. Valid values are anywhere between 0 and
+         * {@link InstallSessionParams#setProgressMax(int)}.
+         */
         public void setProgress(int progress) {
             try {
                 mSession.setClientProgress(progress);
@@ -197,7 +280,7 @@
         /**
          * Open an APK file for writing, starting at the given offset. You can
          * then stream data into the file, periodically calling
-         * {@link OutputStream#flush()} to ensure bytes have been written to
+         * {@link #fsync(OutputStream)} to ensure bytes have been written to
          * disk.
          */
         public OutputStream openWrite(String splitName, long offsetBytes, long lengthBytes)
@@ -214,6 +297,11 @@
             }
         }
 
+        /**
+         * Ensure that any outstanding data for given stream has been committed
+         * to disk. This is only valid for streams returned from
+         * {@link #openWrite(String, long, long)}.
+         */
         public void fsync(OutputStream out) throws IOException {
             if (out instanceof FileBridge.FileBridgeOutputStream) {
                 ((FileBridge.FileBridgeOutputStream) out).fsync();
@@ -222,6 +310,15 @@
             }
         }
 
+        /**
+         * Attempt to commit everything staged in this session. This may require
+         * user intervention, and so it may not happen immediately. The final
+         * result of the commit will be reported through the given callback.
+         * <p>
+         * Once this method is called, no additional mutations may be performed
+         * on the session. If the device reboots before the session has been
+         * finalized, you may commit the session again.
+         */
         public void commit(CommitResultCallback callback) {
             try {
                 mSession.install(new CommitResultCallbackDelegate(callback).getBinder());
@@ -230,11 +327,18 @@
             }
         }
 
+        /**
+         * Release this session object. You can open the session again if it
+         * hasn't been finalized.
+         */
         @Override
         public void close() {
             // No resources to release at the moment
         }
 
+        /**
+         * Completely destroy this session, rendering it invalid.
+         */
         public void destroy() {
             try {
                 mSession.destroy();
@@ -244,11 +348,15 @@
         }
     }
 
+    /**
+     * Final result of an uninstall request.
+     */
     public static abstract class UninstallResultCallback {
         public abstract void onSuccess();
         public abstract void onFailure(String msg);
     }
 
+    /** {@hide} */
     private static class UninstallResultCallbackDelegate extends PackageUninstallObserver {
         private final UninstallResultCallback target;
 
@@ -271,8 +379,18 @@
         }
     }
 
+    /**
+     * Final result of a session commit request.
+     */
     public static abstract class CommitResultCallback {
         public abstract void onSuccess();
+
+        /**
+         * Generic failure occurred. You can override methods (such as
+         * {@link #onFailureInvalid(String)}) to handle more specific categories
+         * of failure. By default, those specific categories all flow into this
+         * generic failure.
+         */
         public abstract void onFailure(String msg);
 
         /**
@@ -286,12 +404,16 @@
         }
 
         /**
-         * This install session conflicts (or is inconsistent with) with
-         * another package already installed on the device. For example, an
-         * existing permission, incompatible certificates, etc. The user may
-         * be able to uninstall another app to fix the issue.
+         * This install session conflicts (or is inconsistent with) with another
+         * package already installed on the device. For example, an existing
+         * permission, incompatible certificates, etc. The user may be able to
+         * uninstall another app to fix the issue.
+         *
+         * @param otherPackageName if one specific package was identified as the
+         *            cause of the conflict, it's named here. If unknown, or
+         *            multiple packages, this may be {@code null}.
          */
-        public void onFailureConflict(String msg, String packageName) {
+        public void onFailureConflict(String msg, String otherPackageName) {
             onFailure(msg);
         }
 
@@ -317,6 +439,7 @@
         }
     }
 
+    /** {@hide} */
     private static class CommitResultCallbackDelegate extends PackageInstallObserver {
         private final CommitResultCallback target;
 
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 03d4701..052c2ca 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1167,6 +1167,22 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device includes a relative humidity sensor.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SENSOR_RELATIVE_HUMIDITY =
+            "android.hardware.sensor.relative_humidity";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device includes an ambient temperature sensor.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SENSOR_AMBIENT_TEMPERATURE =
+            "android.hardware.sensor.ambient_temperature";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device has a telephony radio with data
      * communication support.
      */
@@ -3602,6 +3618,33 @@
     public abstract boolean isSafeMode();
 
     /**
+     * Return the {@link KeySet} associated with the String alias for this
+     * application.
+     *
+     * @param alias The alias for a given {@link KeySet} as defined in the
+     *        application's AndroidManifest.xml.
+     */
+    public abstract KeySet getKeySetByAlias(String packageName, String alias);
+
+    /** Return the signing {@link KeySet} for this application. */
+    public abstract KeySet getSigningKeySet(String packageName);
+
+    /**
+     * Return whether the package denoted by packageName has been signed by all
+     * of the keys specified by the {@link KeySet} ks.  This will return true if
+     * the package has been signed by additional keys (a superset) as well.
+     * Compare to {@link #isSignedByExactly(String packageName, KeySet ks)}.
+     */
+    public abstract boolean isSignedBy(String packageName, KeySet ks);
+
+    /**
+     * Return whether the package denoted by packageName has been signed by all
+     * of, and only, the keys specified by the {@link KeySet} ks. Compare to
+     * {@link #isSignedBy(String packageName, KeySet ks)}.
+     */
+    public abstract boolean isSignedByExactly(String packageName, KeySet ks);
+
+    /**
      * Attempts to move package resources from internal to external media or vice versa.
      * Since this may take a little while, the result will
      * be posted back to the given observer.   This call may fail if the calling context
@@ -3629,8 +3672,11 @@
      */
     public abstract VerifierDeviceIdentity getVerifierDeviceIdentity();
 
-    /** {@hide} */
-    public abstract PackageInstaller getPackageInstaller();
+    /**
+     * Return interface that offers the ability to install, upgrade, and remove
+     * applications on the device.
+     */
+    public abstract PackageInstaller getInstaller();
 
     /**
      * Returns the data directory for a particular user and package, given the uid of the package.
@@ -3676,4 +3722,7 @@
      * @hide
      */
     public abstract Drawable loadItemIcon(PackageItemInfo itemInfo, ApplicationInfo appInfo);
+
+    /** {@hide} */
+    public abstract boolean isPackageAvailable(String packageName);
 }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index d92a90d..db2da42 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -380,6 +380,7 @@
         }
         PackageInfo pi = new PackageInfo();
         pi.packageName = p.packageName;
+        pi.splitNames = p.splitNames;
         pi.versionCode = p.mVersionCode;
         pi.versionName = p.mVersionName;
         pi.sharedUserId = p.mSharedUserId;
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index e9793c4..5f29e5c 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -17,6 +17,7 @@
 package android.hardware.camera2.legacy;
 
 import android.graphics.ImageFormat;
+import android.graphics.SurfaceTexture;
 import android.hardware.Camera;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureRequest;
@@ -463,6 +464,24 @@
         return ids.contains(id);
     }
 
+    static void setSurfaceOrientation(Surface surface, int facing, int sensorOrientation)
+            throws BufferQueueAbandonedException {
+        checkNotNull(surface);
+        LegacyExceptionUtils.throwOnError(nativeSetSurfaceOrientation(surface, facing,
+                sensorOrientation));
+    }
+
+    static Size getTextureSize(SurfaceTexture surfaceTexture)
+            throws BufferQueueAbandonedException {
+        checkNotNull(surfaceTexture);
+
+        int[] dimens = new int[2];
+        LegacyExceptionUtils.throwOnError(nativeDetectTextureDimens(surfaceTexture,
+                /*out*/dimens));
+
+        return new Size(dimens[0], dimens[1]);
+    }
+
     private static native int nativeDetectSurfaceType(Surface surface);
 
     private static native int nativeDetectSurfaceDimens(Surface surface,
@@ -479,4 +498,11 @@
     private static native int nativeSetSurfaceDimens(Surface surface, int width, int height);
 
     private static native long nativeGetSurfaceId(Surface surface);
+
+    private static native int nativeSetSurfaceOrientation(Surface surface, int facing,
+                                                             int sensorOrientation);
+
+    private static native int nativeDetectTextureDimens(SurfaceTexture surfaceTexture,
+            /*out*/int[/*2*/] dimens);
+
 }
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index e6d84c5..7d1be3b 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -292,10 +292,13 @@
         mInFlightPreview = null;
         mInFlightJpeg = null;
 
+        int facing = mCharacteristics.get(CameraCharacteristics.LENS_FACING);
+        int orientation = mCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
         if (outputs != null) {
             for (Surface s : outputs) {
                 try {
                     int format = LegacyCameraDevice.detectSurfaceType(s);
+                    LegacyCameraDevice.setSurfaceOrientation(s, facing, orientation);
                     switch (format) {
                         case CameraMetadataNative.NATIVE_JPEG_FORMAT:
                             mCallbackOutputs.add(s);
diff --git a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
index daa64c0..fdf9ba0 100644
--- a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
+++ b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
@@ -1,18 +1,18 @@
 /*
-* Copyright (C) 2014 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 package android.hardware.camera2.legacy;
 
 import android.graphics.ImageFormat;
@@ -98,14 +98,14 @@
      */
     private static final String VERTEX_SHADER =
             "uniform mat4 uMVPMatrix;\n" +
-                    "uniform mat4 uSTMatrix;\n" +
-                    "attribute vec4 aPosition;\n" +
-                    "attribute vec4 aTextureCoord;\n" +
-                    "varying vec2 vTextureCoord;\n" +
-                    "void main() {\n" +
-                    "  gl_Position = uMVPMatrix * aPosition;\n" +
-                    "  vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" +
-                    "}\n";
+            "uniform mat4 uSTMatrix;\n" +
+            "attribute vec4 aPosition;\n" +
+            "attribute vec4 aTextureCoord;\n" +
+            "varying vec2 vTextureCoord;\n" +
+            "void main() {\n" +
+            "  gl_Position = uMVPMatrix * aPosition;\n" +
+            "  vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" +
+            "}\n";
 
     /**
      * This fragment shader simply draws the color in the 2D texture at
@@ -113,12 +113,12 @@
      */
     private static final String FRAGMENT_SHADER =
             "#extension GL_OES_EGL_image_external : require\n" +
-                    "precision mediump float;\n" +
-                    "varying vec2 vTextureCoord;\n" +
-                    "uniform samplerExternalOES sTexture;\n" +
-                    "void main() {\n" +
-                    "  gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
-                    "}\n";
+            "precision mediump float;\n" +
+            "varying vec2 vTextureCoord;\n" +
+            "uniform samplerExternalOES sTexture;\n" +
+            "void main() {\n" +
+            "  gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
+            "}\n";
 
     private float[] mMVPMatrix = new float[GL_MATRIX_SIZE];
     private float[] mSTMatrix = new float[GL_MATRIX_SIZE];
@@ -189,12 +189,56 @@
         return program;
     }
 
-    private void drawFrame(SurfaceTexture st) {
+    private void drawFrame(SurfaceTexture st, int width, int height) {
         checkGlError("onDrawFrame start");
         st.getTransformMatrix(mSTMatrix);
 
+        Size dimens;
+        try {
+            dimens = LegacyCameraDevice.getTextureSize(st);
+        } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
+            // Should never hit this.
+            throw new IllegalStateException("Surface abandoned, skipping drawFrame...", e);
+        }
+
+        Matrix.setIdentityM(mMVPMatrix, /*smOffset*/0);
+
+        float texWidth = dimens.getWidth();
+        float texHeight = dimens.getHeight();
+
+        if (texWidth <= 0 || texHeight <= 0) {
+            throw new IllegalStateException("Illegal intermediate texture with dimension of 0");
+        }
+
+        // Find largest scaling factor from the intermediate texture dimension to the
+        // output surface dimension.  Scaling the intermediate texture by this allows
+        // us to letterbox/pillerbox the output surface into the intermediate texture.
+        float widthRatio = width / texWidth;
+        float heightRatio = height / texHeight;
+        float actual = (widthRatio < heightRatio) ? heightRatio : widthRatio;
+
         if (DEBUG) {
-            GLES20.glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
+            Log.d(TAG, "Scaling factor " + actual + " used for " + width + "x" + height +
+                    " surface, intermediate buffer size is " + texWidth + "x" + texHeight);
+        }
+
+        // Set the viewport height and width to be the scaled intermediate texture dimensions.
+        int viewportW = (int) (actual * texWidth);
+        int viewportH = (int) (actual * texHeight);
+
+        // Set the offset of the viewport so that the output surface is centered in the viewport.
+        float dx = (width - viewportW) / 2f;
+        float dy = (height - viewportH) / 2f;
+
+        if (DEBUG) {
+            Log.d(TAG, "Translation " + dx + "," + dy + " used for " + width + "x" + height +
+                    " surface");
+        }
+
+        GLES20.glViewport((int) dx, (int) dy, viewportW, viewportH);
+
+        if (DEBUG) {
+            GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
             GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
         }
 
@@ -218,7 +262,6 @@
         GLES20.glEnableVertexAttribArray(maTextureHandle);
         checkGlError("glEnableVertexAttribArray maTextureHandle");
 
-        Matrix.setIdentityM(mMVPMatrix, 0);
         GLES20.glUniformMatrix4fv(muMVPMatrixHandle, /*count*/ 1, /*transpose*/ false, mMVPMatrix,
                 /*offset*/ 0);
         GLES20.glUniformMatrix4fv(muSTMatrixHandle, /*count*/ 1, /*transpose*/ false, mSTMatrix,
@@ -589,18 +632,19 @@
         for (EGLSurfaceHolder holder : mSurfaces) {
             if (LegacyCameraDevice.containsSurfaceId(holder.surface, targetSurfaceIds)) {
                 makeCurrent(holder.eglSurface);
-                drawFrame(mSurfaceTexture);
+                drawFrame(mSurfaceTexture, holder.width, holder.height);
                 swapBuffers(holder.eglSurface);
             }
         }
         for (EGLSurfaceHolder holder : mConversionSurfaces) {
             if (LegacyCameraDevice.containsSurfaceId(holder.surface, targetSurfaceIds)) {
                 makeCurrent(holder.eglSurface);
-                drawFrame(mSurfaceTexture);
+                drawFrame(mSurfaceTexture, holder.width, holder.height);
                 mPBufferPixels.clear();
-                GLES20.glReadPixels(/*x*/ 0, /*y*/ 0, holder.width, holder.height, GLES20.GL_RGBA,
-                        GLES20.GL_UNSIGNED_BYTE, mPBufferPixels);
+                GLES20.glReadPixels(/*x*/ 0, /*y*/ 0, holder.width, holder.height,
+                        GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, mPBufferPixels);
                 checkGlError("glReadPixels");
+
                 try {
                     int format = LegacyCameraDevice.detectSurfaceType(holder.surface);
                     LegacyCameraDevice.produceFrame(holder.surface, mPBufferPixels.array(),
diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java
index 1557ab0..c5aee7b 100644
--- a/core/java/android/printservice/PrintService.java
+++ b/core/java/android/printservice/PrintService.java
@@ -230,7 +230,7 @@
      *
      * @see #EXTRA_PRINT_JOB_INFO
      */
-    public static final String EXTRA_PRINTER_INFO = "android.intent.extra.print.PRINTER_INFO";
+    public static final String EXTRA_PRINTER_INFO = "android.intent.extra.print.EXTRA_PRINTER_INFO";
 
     private Handler mHandler;
 
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 07397973..bfb5a26 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -266,6 +266,21 @@
             "android.settings.WIFI_DISPLAY_SETTINGS";
 
     /**
+     * Activity Action: Show settings to allow configuration of
+     * {@link android.media.routing.MediaRouteService media route providers}.
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you
+     * safeguard against this.
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_CAST_SETTINGS =
+            "android.settings.CAST_SETTINGS";
+
+    /**
      * Activity Action: Show settings to allow configuration of date and time.
      * <p>
      * In some cases, a matching Activity may not exist, so ensure you
@@ -809,6 +824,15 @@
     public static final String ACTION_BATTERY_SAVER_SETTINGS
             = "android.settings.BATTERY_SAVER_SETTINGS";
 
+    /**
+     * Activity Action: Show Home selection settings. If there are multiple activities
+     * that can satisfy the {@link Intent#CATEGORY_HOME} intent, this screen allows you
+     * to pick your preferred activity.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_HOME_SETTINGS
+            = "android.settings.HOME_SETTINGS";
+
     // End of Intent actions for Settings
 
     /**
diff --git a/core/java/android/provider/VoicemailContract.java b/core/java/android/provider/VoicemailContract.java
index 6787fd0..d71ad03 100644
--- a/core/java/android/provider/VoicemailContract.java
+++ b/core/java/android/provider/VoicemailContract.java
@@ -188,6 +188,12 @@
          */
         public static final String MIME_TYPE = "mime_type";
         /**
+         * The transcription of the voicemail entry. This will only be populated if the voicemail
+         * entry has a valid transcription.
+         * <P>Type: TEXT</P>
+         */
+        public static final String TRANSCRIPTION = "transcription";
+        /**
          * Path to the media content file. Internal only field.
          * @hide
          */
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7fab808..b554548 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -461,6 +461,11 @@
                     }
                 }
 
+                // Compute surface insets required to draw at specified Z value.
+                // TODO: Use real shadow insets for a constant max Z.
+                final int surfaceInset = (int) Math.ceil(view.getZ() * 2);
+                attrs.surfaceInsets.set(surfaceInset, surfaceInset, surfaceInset, surfaceInset);
+
                 CompatibilityInfo compatibilityInfo = mDisplayAdjustments.getCompatibilityInfo();
                 mTranslator = compatibilityInfo.getTranslator();
                 mDisplayAdjustments.setActivityToken(attrs.token);
@@ -1713,8 +1718,8 @@
                 if (hwInitialized ||
                         mWidth != mAttachInfo.mHardwareRenderer.getWidth() ||
                         mHeight != mAttachInfo.mHardwareRenderer.getHeight()) {
-                    final Rect shadowInsets = params != null ? params.shadowInsets : null;
-                    mAttachInfo.mHardwareRenderer.setup(mWidth, mHeight, shadowInsets);
+                    final Rect surfaceInsets = params != null ? params.surfaceInsets : null;
+                    mAttachInfo.mHardwareRenderer.setup(mWidth, mHeight, surfaceInsets);
                     if (!hwInitialized) {
                         mAttachInfo.mHardwareRenderer.invalidate(mSurface);
                         mFullRedrawNeeded = true;
@@ -2371,7 +2376,7 @@
         }
 
         final WindowManager.LayoutParams params = mWindowAttributes;
-        final Rect surfaceInsets = params != null ? params.shadowInsets : null;
+        final Rect surfaceInsets = params != null ? params.surfaceInsets : null;
         boolean animating = mScroller != null && mScroller.computeScrollOffset();
         final int curScrollY;
         if (animating) {
@@ -3155,7 +3160,7 @@
                             mFullRedrawNeeded = true;
                             try {
                                 final WindowManager.LayoutParams lp = mWindowAttributes;
-                                final Rect surfaceInsets = lp != null ? lp.shadowInsets : null;
+                                final Rect surfaceInsets = lp != null ? lp.surfaceInsets : null;
                                 mAttachInfo.mHardwareRenderer.initializeIfNeeded(
                                         mWidth, mHeight, mSurface, surfaceInsets);
                             } catch (OutOfResourcesException e) {
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index aa71ed8..c169d35 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -23,6 +23,8 @@
 import android.content.res.TypedArray;
 import android.graphics.PixelFormat;
 import android.graphics.drawable.Drawable;
+import android.media.session.MediaController;
+import android.media.session.MediaSession;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -157,7 +159,7 @@
     private static final String PROPERTY_HARDWARE_UI = "persist.sys.ui.hw";
 
     private final Context mContext;
-    
+
     private TypedArray mWindowStyle;
     private Callback mCallback;
     private OnWindowDismissedCallback mOnWindowDismissedCallback;
@@ -181,7 +183,7 @@
     private int mDefaultWindowFormat = PixelFormat.OPAQUE;
 
     private boolean mHasSoftInputMode = false;
-    
+
     private boolean mDestroyed;
 
     // The current window attributes.
@@ -227,7 +229,7 @@
          * @return boolean Return true if this event was consumed.
          */
         public boolean dispatchTouchEvent(MotionEvent event);
-        
+
         /**
          * Called to process trackball events.  At the very least your
          * implementation must call
@@ -313,14 +315,14 @@
          * Called when a panel's menu is opened by the user. This may also be
          * called when the menu is changing from one type to another (for
          * example, from the icon menu to the expanded menu).
-         * 
+         *
          * @param featureId The panel that the menu is in.
          * @param menu The menu that is opened.
          * @return Return true to allow the menu to open, or false to prevent
          *         the menu from opening.
          */
         public boolean onMenuOpened(int featureId, Menu menu);
-        
+
         /**
          * Called when a panel's menu item has been selected by the user.
          *
@@ -364,31 +366,31 @@
          * for more information.
          */
         public void onAttachedToWindow();
-        
+
         /**
          * Called when the window has been attached to the window manager.
          * See {@link View#onDetachedFromWindow() View.onDetachedFromWindow()}
          * for more information.
          */
         public void onDetachedFromWindow();
-        
+
         /**
          * Called when a panel is being closed.  If another logical subsequent
          * panel is being opened (and this panel is being closed to make room for the subsequent
          * panel), this method will NOT be called.
-         * 
+         *
          * @param featureId The panel that is being displayed.
          * @param menu If onCreatePanelView() returned null, this is the Menu
          *            being displayed in the panel.
          */
         public void onPanelClosed(int featureId, Menu menu);
-        
+
         /**
          * Called when the user signals the desire to start a search.
-         * 
+         *
          * @return true if search launched, false if activity refuses (blocks)
-         * 
-         * @see android.app.Activity#onSearchRequested() 
+         *
+         * @see android.app.Activity#onSearchRequested()
          */
         public boolean onSearchRequested();
 
@@ -457,7 +459,7 @@
             return mWindowStyle;
         }
     }
-    
+
     /**
      * Set the container for this window.  If not set, the DecorWindow
      * operates as a top-level window; otherwise, it negotiates with the
@@ -488,7 +490,7 @@
     public final boolean hasChildren() {
         return mHasChildren;
     }
-    
+
     /** @hide */
     public final void destroy() {
         mDestroyed = true;
@@ -622,14 +624,14 @@
      * callback will be used to tell you about state changes to the surface.
      */
     public abstract void takeSurface(SurfaceHolder.Callback2 callback);
-    
+
     /**
      * Take ownership of this window's InputQueue.  The window will no
      * longer read and dispatch input events from the queue; it is your
      * responsibility to do so.
      */
     public abstract void takeInputQueue(InputQueue.Callback callback);
-    
+
     /**
      * Return whether this window is being displayed with a floating style
      * (based on the {@link android.R.attr#windowIsFloating} attribute in
@@ -740,7 +742,7 @@
         }
         dispatchWindowAttributesChanged(attrs);
     }
-    
+
     /**
      * Convenience function to set the flag bits as specified in flags, as
      * per {@link #setFlags}.
@@ -756,7 +758,7 @@
     public void addPrivateFlags(int flags) {
         setPrivateFlags(flags, flags);
     }
-    
+
     /**
      * Convenience function to clear the flag bits as specified in flags, as
      * per {@link #setFlags}.
@@ -772,7 +774,7 @@
      * Set the flags of the window, as per the
      * {@link WindowManager.LayoutParams WindowManager.LayoutParams}
      * flags.
-     * 
+     *
      * <p>Note that some flags must be set before the window decoration is
      * created (by the first call to
      * {@link #setContentView(View, android.view.ViewGroup.LayoutParams)} or
@@ -859,20 +861,20 @@
     protected final int getForcedWindowFlags() {
         return mForcedWindowFlags;
     }
-    
+
     /**
      * Has the app specified their own soft input mode?
      */
     protected final boolean hasSoftInputMode() {
         return mHasSoftInputMode;
     }
-    
+
     /** @hide */
     public void setCloseOnTouchOutside(boolean close) {
         mCloseOnTouchOutside = close;
         mSetCloseOnTouchOutside = true;
     }
-    
+
     /** @hide */
     public void setCloseOnTouchOutsideIfNotSet(boolean close) {
         if (!mSetCloseOnTouchOutside) {
@@ -880,10 +882,10 @@
             mSetCloseOnTouchOutside = true;
         }
     }
-    
+
     /** @hide */
     public abstract void alwaysReadCloseOnTouchAttr();
-    
+
     /** @hide */
     public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
         if (mCloseOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN
@@ -892,7 +894,7 @@
         }
         return false;
     }
-    
+
     private boolean isOutOfBounds(Context context, MotionEvent event) {
         final int x = (int) event.getX();
         final int y = (int) event.getY();
@@ -902,7 +904,7 @@
                 || (x > (decorView.getWidth()+slop))
                 || (y > (decorView.getHeight()+slop));
     }
-    
+
     /**
      * Enable extended screen features.  This must be called before
      * setContentView().  May be called as many times as desired as long as it
@@ -989,7 +991,7 @@
      * of the window that can not, from this point forward, be changed: the
      * features that have been requested with {@link #requestFeature(int)},
      * and certain window flags as described in {@link #setFlags(int, int)}.
-     * 
+     *
      * @param view The desired content to display.
      * @param params Layout parameters for the view.
      */
@@ -1037,7 +1039,7 @@
     public abstract void togglePanel(int featureId, KeyEvent event);
 
     public abstract void invalidatePanelMenu(int featureId);
-    
+
     public abstract boolean performPanelShortcut(int featureId,
                                                  int keyCode,
                                                  KeyEvent event,
@@ -1052,17 +1054,17 @@
 
     /**
      * Should be called when the configuration is changed.
-     * 
+     *
      * @param newConfig The new configuration.
      */
     public abstract void onConfigurationChanged(Configuration newConfig);
-    
+
     /**
      * Change the background of this window to a Drawable resource. Setting the
      * background to null will make the window be opaque. To make the window
      * transparent, you can use an empty drawable (for instance a ColorDrawable
      * with the color 0 or the system drawable android:drawable/empty.)
-     * 
+     *
      * @param resid The resource identifier of a drawable resource which will be
      *              installed as the new background.
      */
@@ -1173,7 +1175,7 @@
      *
      */
     public abstract boolean superDispatchTouchEvent(MotionEvent event);
-    
+
     /**
      * Used by custom windows, such as Dialog, to pass the trackball event
      * further down the view hierarchy. Application developers should
@@ -1181,7 +1183,7 @@
      *
      */
     public abstract boolean superDispatchTrackballEvent(MotionEvent event);
-    
+
     /**
      * Used by custom windows, such as Dialog, to pass the generic motion event
      * further down the view hierarchy. Application developers should
@@ -1194,11 +1196,11 @@
      * Retrieve the top-level window decor view (containing the standard
      * window frame/decorations and the client's content inside of that), which
      * can be added as a window to the window manager.
-     * 
+     *
      * <p><em>Note that calling this function for the first time "locks in"
      * various window characteristics as described in
      * {@link #setContentView(View, android.view.ViewGroup.LayoutParams)}.</em></p>
-     * 
+     *
      * @return Returns the top-level window decor view.
      */
     public abstract View getDecorView();
@@ -1206,16 +1208,16 @@
     /**
      * Retrieve the current decor view, but only if it has already been created;
      * otherwise returns null.
-     * 
+     *
      * @return Returns the top-level window decor or null.
      * @see #getDecorView
      */
     public abstract View peekDecorView();
 
     public abstract Bundle saveHierarchyState();
-    
+
     public abstract void restoreHierarchyState(Bundle savedInstanceState);
-    
+
     protected abstract void onActive();
 
     /**
@@ -1233,10 +1235,10 @@
     {
         return mFeatures;
     }
-    
+
     /**
      * Query for the availability of a certain feature.
-     * 
+     *
      * @param feature The feature ID to check
      * @return true if the feature is enabled, false otherwise.
      */
@@ -1290,9 +1292,9 @@
      * @param event the {@link android.view.KeyEvent} to use to help check.
      */
     public abstract boolean isShortcutKey(int keyCode, KeyEvent event);
-    
+
     /**
-     * @see android.app.Activity#setVolumeControlStream(int) 
+     * @see android.app.Activity#setVolumeControlStream(int)
      */
     public abstract void setVolumeControlStream(int streamType);
 
@@ -1302,6 +1304,19 @@
     public abstract int getVolumeControlStream();
 
     /**
+     * @see android.app.Activity#setMediaController(android.media.session.MediaController)
+     */
+    public void setMediaController(MediaController controller) {
+    }
+
+    /**
+     * @see android.app.Activity#getMediaController()
+     */
+    public MediaController getMediaController() {
+        return null;
+    }
+
+    /**
      * Set extra options that will influence the UI for this window.
      * @param uiOptions Flags specifying extra options for this window.
      */
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index c06b5d8..034778f 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1298,7 +1298,7 @@
          *
          * @hide
          */
-        public Rect shadowInsets = new Rect();
+        public Rect surfaceInsets = new Rect();
     
         /**
          * The desired bitmap format.  May be one of the constants in
@@ -1580,10 +1580,10 @@
             out.writeInt(hasSystemUiListeners ? 1 : 0);
             out.writeInt(inputFeatures);
             out.writeLong(userActivityTimeout);
-            out.writeInt(shadowInsets.left);
-            out.writeInt(shadowInsets.top);
-            out.writeInt(shadowInsets.right);
-            out.writeInt(shadowInsets.bottom);
+            out.writeInt(surfaceInsets.left);
+            out.writeInt(surfaceInsets.top);
+            out.writeInt(surfaceInsets.right);
+            out.writeInt(surfaceInsets.bottom);
         }
         
         public static final Parcelable.Creator<LayoutParams> CREATOR
@@ -1626,7 +1626,10 @@
             hasSystemUiListeners = in.readInt() != 0;
             inputFeatures = in.readInt();
             userActivityTimeout = in.readLong();
-            shadowInsets.set(in.readInt(), in.readInt(), in.readInt(), in.readInt());
+            surfaceInsets.left = in.readInt();
+            surfaceInsets.top = in.readInt();
+            surfaceInsets.right = in.readInt();
+            surfaceInsets.bottom = in.readInt();
         }
     
         @SuppressWarnings({"PointlessBitwiseExpression"})
@@ -1658,7 +1661,7 @@
         /** {@hide} */
         public static final int TRANSLUCENT_FLAGS_CHANGED = 1<<19;
         /** {@hide} */
-        public static final int SHADOW_INSETS_CHANGED = 1<<20;
+        public static final int SURFACE_INSETS_CHANGED = 1<<20;
         /** {@hide} */
         public static final int EVERYTHING_CHANGED = 0xffffffff;
 
@@ -1794,9 +1797,9 @@
                 changes |= USER_ACTIVITY_TIMEOUT_CHANGED;
             }
 
-            if (!shadowInsets.equals(o.shadowInsets)) {
-                shadowInsets.set(o.shadowInsets);
-                changes |= SHADOW_INSETS_CHANGED;
+            if (!surfaceInsets.equals(o.surfaceInsets)) {
+                surfaceInsets.set(o.surfaceInsets);
+                changes |= SURFACE_INSETS_CHANGED;
             }
 
             return changes;
@@ -1898,8 +1901,8 @@
             if (userActivityTimeout >= 0) {
                 sb.append(" userActivityTimeout=").append(userActivityTimeout);
             }
-            if (!shadowInsets.equals(Insets.NONE)) {
-                sb.append(" shadowInsets=").append(shadowInsets);
+            if (!surfaceInsets.equals(Insets.NONE)) {
+                sb.append(" surfaceInsets=").append(surfaceInsets);
             }
             sb.append('}');
             return sb.toString();
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 9701c6f..aa0b94f 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -36,7 +36,6 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.LongSparseArray;
-import android.util.MathUtils;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.util.StateSet;
@@ -61,8 +60,6 @@
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.CollectionInfo;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.view.animation.LinearInterpolator;
 import android.view.inputmethod.BaseInputConnection;
@@ -7269,290 +7266,4 @@
             }
         }
     }
-
-    /**
-     * Abstract position scroller that handles sub-position scrolling but has no
-     * understanding of layout.
-     */
-    abstract class AbsSubPositionScroller extends AbsPositionScroller {
-        private static final int DURATION_AUTO = -1;
-
-        private static final int DURATION_AUTO_MIN = 100;
-        private static final int DURATION_AUTO_MAX = 500;
-
-        private final SubScroller mSubScroller = new SubScroller();
-
-        /**
-         * The target offset in pixels between the top of the list and the top
-         * of the target position.
-         */
-        private int mOffset;
-
-        /**
-         * Scroll the minimum amount to get the target view entirely on-screen.
-         */
-        private void scrollToPosition(final int targetPosition, final boolean useOffset,
-                final int offset, final int boundPosition, final int duration) {
-            stop();
-
-            if (mDataChanged) {
-                // Wait until we're back in a stable state to try this.
-                mPositionScrollAfterLayout = new Runnable() {
-                    @Override
-                    public void run() {
-                        scrollToPosition(
-                                targetPosition, useOffset, offset, boundPosition, duration);
-                    }
-                };
-                return;
-            }
-
-            if (mAdapter == null) {
-                // Can't scroll anywhere without an adapter.
-                return;
-            }
-
-            final int itemCount = getCount();
-            final int clampedPosition = MathUtils.constrain(targetPosition, 0, itemCount - 1);
-            final int clampedBoundPosition = MathUtils.constrain(boundPosition, -1, itemCount - 1);
-            final int firstPosition = getFirstVisiblePosition();
-            final int lastPosition = firstPosition + getChildCount();
-            final int targetRow = getRowForPosition(clampedPosition);
-            final int firstRow = getRowForPosition(firstPosition);
-            final int lastRow = getRowForPosition(lastPosition);
-            if (useOffset || targetRow <= firstRow) {
-                // Offset so the target row is top-aligned.
-                mOffset = offset;
-            } else if (targetRow >= lastRow - 1) {
-                // Offset so the target row is bottom-aligned.
-                final int listHeight = getHeight() - getPaddingTop() - getPaddingBottom();
-                mOffset = getHeightForPosition(clampedPosition) - listHeight;
-            } else {
-                // Don't scroll, target is entirely on-screen.
-                return;
-            }
-
-            float endSubRow = targetRow;
-            if (clampedBoundPosition != INVALID_POSITION) {
-                final int boundRow = getRowForPosition(clampedBoundPosition);
-                if (boundRow >= firstRow && boundRow < lastRow && boundRow != targetRow) {
-                    endSubRow = computeBoundSubRow(targetRow, boundRow);
-                }
-            }
-
-            final View firstChild = getChildAt(0);
-            if (firstChild == null) {
-                return;
-            }
-
-            final int firstChildHeight = firstChild.getHeight();
-            final float startOffsetRatio;
-            if (firstChildHeight == 0) {
-                startOffsetRatio = 0;
-            } else {
-                startOffsetRatio = -firstChild.getTop() / (float) firstChildHeight;
-            }
-
-            final float startSubRow = MathUtils.constrain(
-                    firstRow + startOffsetRatio, 0, getCount());
-            if (startSubRow == endSubRow && mOffset == 0) {
-                // Don't scroll, target is already in position.
-                return;
-            }
-
-            final int durationMillis;
-            if (duration == DURATION_AUTO) {
-                final float subRowDelta = Math.abs(startSubRow - endSubRow);
-                durationMillis = (int) MathUtils.lerp(
-                        DURATION_AUTO_MIN, DURATION_AUTO_MAX, subRowDelta / getCount());
-            } else {
-                durationMillis = duration;
-            }
-
-            mSubScroller.startScroll(startSubRow, endSubRow, durationMillis);
-
-            postOnAnimation(mAnimationFrame);
-        }
-
-        /**
-         * Given a target row and offset, computes the sub-row position that
-         * aligns with the top of the list. If the offset is negative, the
-         * resulting sub-row will be smaller than the target row.
-         */
-        private float resolveOffset(int targetRow, int offset) {
-            // Compute the target sub-row position by finding the actual row
-            // indicated by the target and offset.
-            int remainingOffset = offset;
-            int targetHeight = getHeightForRow(targetRow);
-            if (offset < 0) {
-                // Subtract row heights until we find the right row.
-                while (targetRow > 0 && remainingOffset < 0) {
-                    remainingOffset += targetHeight;
-                    targetRow--;
-                    targetHeight = getHeightForRow(targetRow);
-                }
-            } else if (offset > 0) {
-                // Add row heights until we find the right row.
-                while (targetRow < getCount() - 1 && remainingOffset > targetHeight) {
-                    remainingOffset -= targetHeight;
-                    targetRow++;
-                    targetHeight = getHeightForRow(targetRow);
-                }
-            }
-
-            final float targetOffsetRatio;
-            if (remainingOffset < 0 || targetHeight == 0) {
-                targetOffsetRatio = 0;
-            } else {
-                targetOffsetRatio = remainingOffset / (float) targetHeight;
-            }
-
-            return targetRow + targetOffsetRatio;
-        }
-
-        private float computeBoundSubRow(int targetRow, int boundRow) {
-            final float targetSubRow = resolveOffset(targetRow, mOffset);
-            mOffset = 0;
-
-            // The target row is below the bound row, so the end position would
-            // push the bound position above the list. Abort!
-            if (targetSubRow >= boundRow) {
-                return boundRow;
-            }
-
-            // Compute the closest possible sub-position that wouldn't push the
-            // bound position's view further below the list.
-            final int listHeight = getHeight() - getPaddingTop() - getPaddingBottom();
-            final int boundHeight = getHeightForRow(boundRow);
-            final float boundSubRow = resolveOffset(boundRow, -listHeight + boundHeight);
-
-            return Math.max(boundSubRow, targetSubRow);
-        }
-
-        @Override
-        public void start(int position) {
-            scrollToPosition(position, false, 0, INVALID_POSITION, DURATION_AUTO);
-        }
-
-        @Override
-        public void start(int position, int boundPosition) {
-            scrollToPosition(position, false, 0, boundPosition, DURATION_AUTO);
-        }
-
-        @Override
-        public void startWithOffset(int position, int offset) {
-            scrollToPosition(position, true, offset, INVALID_POSITION, DURATION_AUTO);
-        }
-
-        @Override
-        public void startWithOffset(int position, int offset, int duration) {
-            scrollToPosition(position, true, offset, INVALID_POSITION, duration);
-        }
-
-        @Override
-        public void stop() {
-            removeCallbacks(mAnimationFrame);
-        }
-
-        /**
-         * Returns the height of a row, which is computed as the maximum height of
-         * the items in the row.
-         *
-         * @param row the row index
-         * @return row height in pixels
-         */
-        public abstract int getHeightForRow(int row);
-
-        /**
-         * Returns the row for the specified item position.
-         *
-         * @param position the item position
-         * @return the row index
-         */
-        public abstract int getRowForPosition(int position);
-
-        /**
-         * Returns the first item position within the specified row.
-         *
-         * @param row the row
-         * @return the position of the first item in the row
-         */
-        public abstract int getFirstPositionForRow(int row);
-
-        private void onAnimationFrame() {
-            final boolean shouldPost = mSubScroller.computePosition();
-            final float subRow = mSubScroller.getPosition();
-
-            final int row = (int) subRow;
-            final int position = getFirstPositionForRow(row);
-            if (position >= getCount()) {
-                // Invalid position, abort scrolling.
-                return;
-            }
-
-            final int rowHeight = getHeightForRow(row);
-            final int offset = (int) (rowHeight * (subRow - row));
-            final int addOffset = (int) (mOffset * mSubScroller.getInterpolatedValue());
-            setSelectionFromTop(position, -offset - addOffset);
-
-            if (shouldPost) {
-                postOnAnimation(mAnimationFrame);
-            }
-        }
-
-        private Runnable mAnimationFrame = new Runnable() {
-            @Override
-            public void run() {
-                onAnimationFrame();
-            }
-        };
-    }
-
-    /**
-     * Scroller capable of returning floating point positions.
-     */
-    static class SubScroller {
-        private static final Interpolator INTERPOLATOR = new AccelerateDecelerateInterpolator();
-
-        private float mStartPosition;
-        private float mEndPosition;
-        private long mStartTime;
-        private long mDuration;
-
-        private float mPosition;
-        private float mInterpolatedValue;
-
-        public void startScroll(float startPosition, float endPosition, int duration) {
-            mStartPosition = startPosition;
-            mEndPosition = endPosition;
-            mDuration = duration;
-
-            mStartTime = AnimationUtils.currentAnimationTimeMillis();
-            mPosition = startPosition;
-            mInterpolatedValue = 0;
-        }
-
-        public boolean computePosition() {
-            final long elapsed = AnimationUtils.currentAnimationTimeMillis() - mStartTime;
-            final float value;
-            if (mDuration <= 0) {
-                value = 1;
-            } else {
-                value = MathUtils.constrain(elapsed / (float) mDuration, 0, 1);
-            }
-
-            mInterpolatedValue = INTERPOLATOR.getInterpolation(value);
-            mPosition = (mEndPosition - mStartPosition) * mInterpolatedValue + mStartPosition;
-
-            return elapsed < mDuration;
-        }
-
-        public float getPosition() {
-            return mPosition;
-        }
-
-        public float getInterpolatedValue() {
-            return mInterpolatedValue;
-        }
-    }
 }
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index 93810b3..33cc66e 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -1029,11 +1029,6 @@
     }
 
     @Override
-    AbsPositionScroller createPositionScroller() {
-        return new GridViewPositionScroller();
-    }
-
-    @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         // Sets up mListPadding
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -2392,33 +2387,4 @@
                 column, 1, row, 1, isHeading, isSelected);
         info.setCollectionItemInfo(itemInfo);
     }
-
-    /**
-     * Sub-position scroller that understands the layout of a GridView.
-     */
-    class GridViewPositionScroller extends AbsSubPositionScroller {
-        @Override
-        public int getRowForPosition(int position) {
-            return position / mNumColumns;
-        }
-
-        @Override
-        public int getFirstPositionForRow(int row) {
-            return row * mNumColumns;
-        }
-
-        @Override
-        public int getHeightForRow(int row) {
-            final int firstRowPosition = row * mNumColumns;
-            final int lastRowPosition = Math.min(getCount(), firstRowPosition + mNumColumns);
-            int maxHeight = 0;
-            for (int i = firstRowPosition; i < lastRowPosition; i++) {
-                final int height = getHeightForPosition(i);
-                if (height > maxHeight) {
-                    maxHeight = height;
-                }
-            }
-            return maxHeight;
-        }
-    }
 }
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 1baeca8..9db1e05 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -3872,11 +3872,6 @@
     }
 
     @Override
-    AbsPositionScroller createPositionScroller() {
-        return new ListViewPositionScroller();
-    }
-
-    @Override
     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
         super.onInitializeAccessibilityEvent(event);
         event.setClassName(ListView.class.getName());
@@ -3905,24 +3900,4 @@
                 0, 1, position, 1, isHeading, isSelected);
         info.setCollectionItemInfo(itemInfo);
     }
-
-    /**
-     * Sub-position scroller that understands the layout of a ListView.
-     */
-    class ListViewPositionScroller extends AbsSubPositionScroller {
-        @Override
-        public int getRowForPosition(int position) {
-            return position;
-        }
-
-        @Override
-        public int getFirstPositionForRow(int row) {
-            return row;
-        }
-
-        @Override
-        public int getHeightForRow(int row) {
-            return getHeightForPosition(row);
-        }
-    }
 }
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index a35d447..41d3e320 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1096,10 +1096,6 @@
         p.softInputMode = mSoftInputMode;
         p.setTitle("PopupWindow:" + Integer.toHexString(hashCode()));
 
-        // TODO: Use real shadow insets once that algorithm is finalized.
-        final int shadowInset = (int) Math.ceil(mElevation * 2);
-        p.shadowInsets.set(shadowInset, shadowInset, shadowInset, shadowInset);
-
         return p;
     }
 
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index 8b56ceb..7e58351 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -56,7 +56,7 @@
 
 public class LocalTransport extends BackupTransport {
     private static final String TAG = "LocalTransport";
-    private static final boolean DEBUG = true;
+    private static final boolean DEBUG = false;
 
     private static final String TRANSPORT_DIR_NAME
             = "com.android.internal.backup.LocalTransport";
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index b8fcff6..a890eb4 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -39,14 +39,12 @@
 jfieldID gOptions_mimeFieldID;
 jfieldID gOptions_mCancelID;
 jfieldID gOptions_bitmapFieldID;
-jfieldID gBitmap_nativeBitmapFieldID;
-jfieldID gBitmap_layoutBoundsFieldID;
 
-#if 0
-    #define TRACE_BITMAP(code)  code
-#else
-    #define TRACE_BITMAP(code)
-#endif
+jfieldID gBitmap_nativeBitmapFieldID;
+jfieldID gBitmap_ninePatchInsetsFieldID;
+
+jclass gInsetStruct_class;
+jmethodID gInsetStruct_constructorMethodID;
 
 using namespace android;
 
@@ -195,8 +193,7 @@
     const unsigned int mSize;
 };
 
-static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding,
-        jobject options) {
+static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding, jobject options) {
 
     int sampleSize = 1;
 
@@ -331,12 +328,12 @@
     }
 
     jbyteArray ninePatchChunk = NULL;
-    if (peeker.fPatch != NULL) {
+    if (peeker.mPatch != NULL) {
         if (willScale) {
-            scaleNinePatchChunk(peeker.fPatch, scale);
+            scaleNinePatchChunk(peeker.mPatch, scale);
         }
 
-        size_t ninePatchArraySize = peeker.fPatch->serializedSize();
+        size_t ninePatchArraySize = peeker.mPatch->serializedSize();
         ninePatchChunk = env->NewByteArray(ninePatchArraySize);
         if (ninePatchChunk == NULL) {
             return nullObjectReturn("ninePatchChunk == null");
@@ -347,28 +344,18 @@
             return nullObjectReturn("primitive array == null");
         }
 
-        memcpy(array, peeker.fPatch, peeker.fPatchSize);
+        memcpy(array, peeker.mPatch, peeker.mPatchSize);
         env->ReleasePrimitiveArrayCritical(ninePatchChunk, array, 0);
     }
 
-    jintArray layoutBounds = NULL;
-    if (peeker.fLayoutBounds != NULL) {
-        layoutBounds = env->NewIntArray(4);
-        if (layoutBounds == NULL) {
-            return nullObjectReturn("layoutBounds == null");
-        }
-
-        jint scaledBounds[4];
-        if (willScale) {
-            for (int i=0; i<4; i++) {
-                scaledBounds[i] = (jint)((((jint*)peeker.fLayoutBounds)[i]*scale) + .5f);
-            }
-        } else {
-            memcpy(scaledBounds, (jint*)peeker.fLayoutBounds, sizeof(scaledBounds));
-        }
-        env->SetIntArrayRegion(layoutBounds, 0, 4, scaledBounds);
+    jobject ninePatchInsets = NULL;
+    if (peeker.mHasInsets) {
+        ninePatchInsets = env->NewObject(gInsetStruct_class, gInsetStruct_constructorMethodID,
+                peeker.mOpticalInsets[0], peeker.mOpticalInsets[1], peeker.mOpticalInsets[2], peeker.mOpticalInsets[3],
+                peeker.mOutlineInsets[0], peeker.mOutlineInsets[1], peeker.mOutlineInsets[2], peeker.mOutlineInsets[3],
+                peeker.mOutlineRadius, peeker.mOutlineFilled, scale);
         if (javaBitmap != NULL) {
-            env->SetObjectField(javaBitmap, gBitmap_layoutBoundsFieldID, layoutBounds);
+            env->SetObjectField(javaBitmap, gBitmap_ninePatchInsetsFieldID, ninePatchInsets);
         }
     }
 
@@ -409,10 +396,10 @@
     }
 
     if (padding) {
-        if (peeker.fPatch != NULL) {
+        if (peeker.mPatch != NULL) {
             GraphicsJNI::set_jrect(env, padding,
-                    peeker.fPatch->paddingLeft, peeker.fPatch->paddingTop,
-                    peeker.fPatch->paddingRight, peeker.fPatch->paddingBottom);
+                    peeker.mPatch->paddingLeft, peeker.mPatch->paddingTop,
+                    peeker.mPatch->paddingRight, peeker.mPatch->paddingBottom);
         } else {
             GraphicsJNI::set_jrect(env, padding, -1, -1, -1, -1);
         }
@@ -446,7 +433,7 @@
 
     // now create the java bitmap
     return GraphicsJNI::createBitmap(env, outputBitmap, javaAllocator.getStorageObj(),
-            bitmapCreateFlags, ninePatchChunk, layoutBounds, -1);
+            bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1);
 }
 
 // Need to buffer enough input to be able to rewind as much as might be read by a decoder
@@ -576,7 +563,7 @@
     jclass options_class = env->FindClass("android/graphics/BitmapFactory$Options");
     SkASSERT(options_class);
     gOptions_bitmapFieldID = getFieldIDCheck(env, options_class, "inBitmap",
-        "Landroid/graphics/Bitmap;");
+            "Landroid/graphics/Bitmap;");
     gOptions_justBoundsFieldID = getFieldIDCheck(env, options_class, "inJustDecodeBounds", "Z");
     gOptions_sampleSizeFieldID = getFieldIDCheck(env, options_class, "inSampleSize", "I");
     gOptions_configFieldID = getFieldIDCheck(env, options_class, "inPreferredConfig",
@@ -598,7 +585,12 @@
     jclass bitmap_class = env->FindClass("android/graphics/Bitmap");
     SkASSERT(bitmap_class);
     gBitmap_nativeBitmapFieldID = getFieldIDCheck(env, bitmap_class, "mNativeBitmap", "J");
-    gBitmap_layoutBoundsFieldID = getFieldIDCheck(env, bitmap_class, "mOpticalInsets", "[I");
+    gBitmap_ninePatchInsetsFieldID = getFieldIDCheck(env, bitmap_class, "mNinePatchInsets",
+            "Landroid/graphics/NinePatch$InsetStruct;");
+
+    gInsetStruct_class = (jclass) env->NewGlobalRef(env->FindClass("android/graphics/NinePatch$InsetStruct"));
+    gInsetStruct_constructorMethodID = env->GetMethodID(gInsetStruct_class, "<init>", "(IIIIIIIIFZF)V");
+
     int ret = AndroidRuntime::registerNativeMethods(env,
                                     "android/graphics/BitmapFactory$Options",
                                     gOptionsMethods,
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 9177696..5cc2b95 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -415,7 +415,7 @@
 }
 
 jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, jbyteArray buffer,
-        int bitmapCreateFlags, jbyteArray ninepatch, jintArray layoutbounds, int density)
+        int bitmapCreateFlags, jbyteArray ninePatchChunk, jobject ninePatchInsets, int density)
 {
     SkASSERT(bitmap);
     SkASSERT(bitmap->pixelRef());
@@ -429,17 +429,11 @@
     jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID,
             reinterpret_cast<jlong>(bitmap), buffer,
             bitmap->width(), bitmap->height(), density, isMutable, isPremultiplied,
-            ninepatch, layoutbounds);
+            ninePatchChunk, ninePatchInsets);
     hasException(env); // For the side effect of logging.
     return obj;
 }
 
-jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, int bitmapCreateFlags,
-        jbyteArray ninepatch, int density)
-{
-    return createBitmap(env, bitmap, NULL, bitmapCreateFlags, ninepatch, NULL, density);
-}
-
 void GraphicsJNI::reinitBitmap(JNIEnv* env, jobject javaBitmap, SkBitmap* bitmap,
         bool isPremultiplied)
 {
@@ -720,7 +714,7 @@
 
     gBitmap_class = make_globalref(env, "android/graphics/Bitmap");
     gBitmap_nativeInstanceID = getFieldIDCheck(env, gBitmap_class, "mNativeBitmap", "J");
-    gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>", "(J[BIIIZZ[B[I)V");
+    gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>", "(J[BIIIZZ[BLandroid/graphics/NinePatch$InsetStruct;)V");
     gBitmap_reinitMethodID = env->GetMethodID(gBitmap_class, "reinit", "(IIZ)V");
     gBitmap_getAllocationByteCountMethodID = env->GetMethodID(gBitmap_class, "getAllocationByteCount", "()I");
     gBitmapRegionDecoder_class = make_globalref(env, "android/graphics/BitmapRegionDecoder");
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index a03391d..8150edf 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -76,10 +76,12 @@
         bitmap's SkAlphaType must already be in sync with bitmapCreateFlags.
     */
     static jobject createBitmap(JNIEnv* env, SkBitmap* bitmap, jbyteArray buffer,
-            int bitmapCreateFlags, jbyteArray ninepatch, jintArray layoutbounds, int density = -1);
+            int bitmapCreateFlags, jbyteArray ninePatch, jobject ninePatchInsets, int density = -1);
 
     static jobject createBitmap(JNIEnv* env, SkBitmap* bitmap, int bitmapCreateFlags,
-            jbyteArray ninepatch, int density = -1);
+            jbyteArray ninePatch, int density = -1) {
+        return createBitmap(env, bitmap, NULL, bitmapCreateFlags, ninePatch, NULL, density);
+    }
 
     /** Reinitialize a bitmap. bitmap must already have its SkAlphaType set in
         sync with isPremultiplied
diff --git a/core/jni/android/graphics/NinePatchPeeker.cpp b/core/jni/android/graphics/NinePatchPeeker.cpp
index 5daa1ad..ea5193b 100644
--- a/core/jni/android/graphics/NinePatchPeeker.cpp
+++ b/core/jni/android/graphics/NinePatchPeeker.cpp
@@ -21,7 +21,7 @@
 using namespace android;
 
 bool NinePatchPeeker::peek(const char tag[], const void* data, size_t length) {
-    if (strcmp("npTc", tag) == 0 && length >= sizeof(Res_png_9patch)) {
+    if (!strcmp("npTc", tag) && length >= sizeof(Res_png_9patch)) {
         Res_png_9patch* patch = (Res_png_9patch*) data;
         size_t patchSize = patch->serializedSize();
         assert(length == patchSize);
@@ -30,12 +30,9 @@
         memcpy(patchNew, patch, patchSize);
         Res_png_9patch::deserialize(patchNew);
         patchNew->fileToDevice();
-        free(fPatch);
-        fPatch = patchNew;
-        fPatchSize = patchSize;
-        //printf("9patch: (%d,%d)-(%d,%d)\n",
-        //       fPatch.sizeLeft, fPatch.sizeTop,
-        //       fPatch.sizeRight, fPatch.sizeBottom);
+        free(mPatch);
+        mPatch = patchNew;
+        mPatchSize = patchSize;
 
         // now update our host to force index or 32bit config
         // 'cause we don't want 565 predithered, since as a 9patch, we know
@@ -47,10 +44,15 @@
         table.fPrefFor_8bpc_NoAlpha_src     = SkBitmap::kARGB_8888_Config;
         table.fPrefFor_8bpc_YesAlpha_src    = SkBitmap::kARGB_8888_Config;
 
-        fHost->setPrefConfigTable(table);
-    } else if (strcmp("npLb", tag) == 0 && length == sizeof(int) * 4) {
-        fLayoutBounds = new int[4];
-        memcpy(fLayoutBounds, data, sizeof(int) * 4);
+        mHost->setPrefConfigTable(table);
+    } else if (!strcmp("npLb", tag) && length == sizeof(int32_t) * 4) {
+        mHasInsets = true;
+        memcpy(&mOpticalInsets, data, sizeof(int32_t) * 4);
+    } else if (!strcmp("npOl", tag) && length == 24) { // 4 int32_ts, 1 float, 1 int32_t sized bool
+        mHasInsets = true;
+        memcpy(&mOutlineInsets, data, sizeof(int32_t) * 4);
+        mOutlineRadius = ((const float*)data)[4];
+        mOutlineFilled = ((const int32_t*)data)[5] & 0x01;
     }
     return true;    // keep on decoding
 }
diff --git a/core/jni/android/graphics/NinePatchPeeker.h b/core/jni/android/graphics/NinePatchPeeker.h
index 2043862..8d3e6cf 100644
--- a/core/jni/android/graphics/NinePatchPeeker.h
+++ b/core/jni/android/graphics/NinePatchPeeker.h
@@ -23,26 +23,34 @@
 using namespace android;
 
 class NinePatchPeeker : public SkImageDecoder::Peeker {
-    SkImageDecoder* fHost;
+private:
+    // the host lives longer than we do, so a raw ptr is safe
+    SkImageDecoder* mHost;
 public:
-    NinePatchPeeker(SkImageDecoder* host) {
-        // the host lives longer than we do, so a raw ptr is safe
-        fHost = host;
-        fPatch = NULL;
-        fPatchSize = 0;
-        fLayoutBounds = NULL;
+    NinePatchPeeker(SkImageDecoder* host)
+            : mHost(host)
+            , mPatch(NULL)
+            , mPatchSize(0)
+            , mHasInsets(false)
+            , mOutlineRadius(0)
+            , mOutlineFilled(false) {
+        memset(mOpticalInsets, 0, 4 * sizeof(int32_t));
+        memset(mOutlineInsets, 0, 4 * sizeof(int32_t));
     }
 
     ~NinePatchPeeker() {
-        free(fPatch);
-        delete fLayoutBounds;
+        free(mPatch);
     }
 
-    Res_png_9patch*  fPatch;
-    size_t fPatchSize;
-    int    *fLayoutBounds;
-
     virtual bool peek(const char tag[], const void* data, size_t length);
+
+    Res_png_9patch* mPatch;
+    size_t mPatchSize;
+    bool mHasInsets;
+    int32_t mOpticalInsets[4];
+    int32_t mOutlineInsets[4];
+    float mOutlineRadius;
+    bool mOutlineFilled;
 };
 
 #endif // NinePatchPeeker_h
diff --git a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
index 9621bb2..d2f5b5d 100644
--- a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
+++ b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
@@ -19,17 +19,20 @@
 #include <utils/Log.h>
 #include <utils/Errors.h>
 #include <utils/Trace.h>
+#include <camera/CameraUtils.h>
 
 #include "jni.h"
 #include "JNIHelp.h"
 #include "android_runtime/AndroidRuntime.h"
 #include "android_runtime/android_view_Surface.h"
+#include "android_runtime/android_graphics_SurfaceTexture.h"
 
 #include <gui/Surface.h>
 #include <gui/IGraphicBufferProducer.h>
 #include <ui/GraphicBuffer.h>
 #include <system/window.h>
 #include <hardware/camera3.h>
+#include <system/camera_metadata.h>
 
 #include <stdint.h>
 #include <inttypes.h>
@@ -335,6 +338,25 @@
     return anw;
 }
 
+static sp<ANativeWindow> getNativeWindowFromTexture(JNIEnv* env, jobject surfaceTexture) {
+    sp<ANativeWindow> anw;
+    if (surfaceTexture) {
+        anw = android_SurfaceTexture_getNativeWindow(env, surfaceTexture);
+        if (env->ExceptionCheck()) {
+            return NULL;
+        }
+    } else {
+        jniThrowNullPointerException(env, "surfaceTexture");
+        return NULL;
+    }
+    if (anw == NULL) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                "SurfaceTexture had no valid native window.");
+        return NULL;
+    }
+    return anw;
+}
+
 static sp<Surface> getSurface(JNIEnv* env, jobject surface) {
     sp<Surface> s;
     if (surface) {
@@ -376,6 +398,17 @@
 static jint LegacyCameraDevice_nativeDetectSurfaceDimens(JNIEnv* env, jobject thiz,
           jobject surface, jintArray dimens) {
     ALOGV("nativeGetSurfaceDimens");
+
+    if (dimens == NULL) {
+        ALOGE("%s: Null dimens argument passed to nativeDetectSurfaceDimens", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    if (env->GetArrayLength(dimens) < 2) {
+        ALOGE("%s: Invalid length of dimens argument in nativeDetectSurfaceDimens", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
     sp<ANativeWindow> anw;
     if ((anw = getNativeWindow(env, surface)) == NULL) {
         ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__);
@@ -398,6 +431,37 @@
     return NO_ERROR;
 }
 
+static jint LegacyCameraDevice_nativeDetectTextureDimens(JNIEnv* env, jobject thiz,
+        jobject surfaceTexture, jintArray dimens) {
+    ALOGV("nativeDetectTextureDimens");
+    sp<ANativeWindow> anw;
+    if ((anw = getNativeWindowFromTexture(env, surfaceTexture)) == NULL) {
+        ALOGE("%s: Could not retrieve native window from SurfaceTexture.", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    int32_t dimenBuf[2];
+    status_t err = anw->query(anw.get(), NATIVE_WINDOW_WIDTH, dimenBuf);
+    if(err != NO_ERROR) {
+        ALOGE("%s: Error while querying SurfaceTexture width %s (%d)", __FUNCTION__,
+                strerror(-err), err);
+        return err;
+    }
+
+    err = anw->query(anw.get(), NATIVE_WINDOW_HEIGHT, dimenBuf + 1);
+    if(err != NO_ERROR) {
+        ALOGE("%s: Error while querying SurfaceTexture height %s (%d)", __FUNCTION__,
+                strerror(-err), err);
+        return err;
+    }
+
+    env->SetIntArrayRegion(dimens, /*start*/0, /*length*/ARRAY_SIZE(dimenBuf), dimenBuf);
+    if (env->ExceptionCheck()) {
+        return BAD_VALUE;
+    }
+    return NO_ERROR;
+}
+
 static jint LegacyCameraDevice_nativeConfigureSurface(JNIEnv* env, jobject thiz, jobject surface,
         jint width, jint height, jint pixelFormat) {
     ALOGV("nativeConfigureSurface");
@@ -504,6 +568,42 @@
     return reinterpret_cast<jlong>(b.get());
 }
 
+static jint LegacyCameraDevice_nativeSetSurfaceOrientation(JNIEnv* env, jobject thiz,
+        jobject surface, jint facing, jint orientation) {
+    ALOGV("nativeSetSurfaceOrientation");
+    sp<ANativeWindow> anw;
+    if ((anw = getNativeWindow(env, surface)) == NULL) {
+        ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    status_t err = NO_ERROR;
+    CameraMetadata staticMetadata;
+
+    int32_t orientVal = static_cast<int32_t>(orientation);
+    uint8_t facingVal = static_cast<uint8_t>(facing);
+    staticMetadata.update(ANDROID_SENSOR_ORIENTATION, &orientVal, 1);
+    staticMetadata.update(ANDROID_LENS_FACING, &facingVal, 1);
+
+    int32_t transform = 0;
+
+    if ((err = CameraUtils::getRotationTransform(staticMetadata, /*out*/&transform)) != OK) {
+        ALOGE("%s: Invalid rotation transform %s (%d)", __FUNCTION__, strerror(-err),
+                err);
+        return err;
+    }
+
+    ALOGV("%s: Setting buffer sticky transform to %d", __FUNCTION__, transform);
+
+    if ((err = native_window_set_buffers_sticky_transform(anw.get(), transform)) != OK) {
+        ALOGE("%s: Unable to configure surface transform, error %s (%d)", __FUNCTION__,
+                strerror(-err), err);
+        return err;
+    }
+
+    return NO_ERROR;
+}
+
 } // extern "C"
 
 static JNINativeMethod gCameraDeviceMethods[] = {
@@ -528,6 +628,12 @@
     { "nativeGetSurfaceId",
     "(Landroid/view/Surface;)J",
     (void *)LegacyCameraDevice_nativeGetSurfaceId },
+    { "nativeDetectTextureDimens",
+    "(Landroid/graphics/SurfaceTexture;[I)I",
+    (void *)LegacyCameraDevice_nativeDetectTextureDimens },
+    { "nativeSetSurfaceOrientation",
+    "(Landroid/view/Surface;II)I",
+    (void *)LegacyCameraDevice_nativeSetSurfaceOrientation },
 };
 
 // Get all the required offsets in java class and register native functions
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 989b60ee..64366e5 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -439,8 +439,11 @@
     if (!is_system_server) {
         int rc = createProcessGroup(uid, getpid());
         if (rc != 0) {
-            ALOGE("createProcessGroup(%d, %d) failed: %s", uid, pid, strerror(-rc));
-            RuntimeAbort(env);
+            if (rc == -EROFS) {
+                ALOGW("createProcessGroup failed, kernel missing CONFIG_CGROUP_CPUACCT?");
+            } else {
+                ALOGE("createProcessGroup(%d, %d) failed: %s", uid, pid, strerror(-rc));
+            }
         }
     }
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4725cfb..350660b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2149,7 +2149,7 @@
         android:description="@string/permdesc_bindTvInput"
         android:protectionLevel="signature|system" />
 
-    <!-- Must be required by a {@link android.media.routeprovider.RouteProviderService}
+    <!-- Must be required by a {@link android.media.routing.MediaRouteService}
          to ensure that only the system can interact with it.
          @hide -->
     <permission android:name="android.permission.BIND_ROUTE_PROVIDER"
@@ -2720,6 +2720,13 @@
         android:description="@string/permdesc_bindConditionProviderService"
         android:protectionLevel="signature" />
 
+    <!-- Must be required by a {@link android.media.routing.MediaRouteService},
+         to ensure that only the system can bind to it. -->
+    <permission android:name="android.permission.BIND_MEDIA_ROUTE_SERVICE"
+        android:label="@string/permlab_bindMediaRouteService"
+        android:description="@string/permdesc_bindMediaRouteService"
+        android:protectionLevel="signature" />
+
     <!-- Must be required by an {@link android.service.dreams.DreamService},
          to ensure that only the system can bind to it. -->
     <permission android:name="android.permission.BIND_DREAM_SERVICE"
diff --git a/core/res/res/layout/alert_dialog_material.xml b/core/res/res/layout/alert_dialog_material.xml
index 93acc3f..6a435b2 100644
--- a/core/res/res/layout/alert_dialog_material.xml
+++ b/core/res/res/layout/alert_dialog_material.xml
@@ -20,13 +20,7 @@
     android:id="@+id/parentPanel"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:orientation="vertical"
-    android:background="@drawable/dialog_background_material"
-    android:translationZ="@dimen/floating_window_z"
-    android:layout_marginLeft="@dimen/floating_window_margin_left"
-    android:layout_marginTop="@dimen/floating_window_margin_top"
-    android:layout_marginRight="@dimen/floating_window_margin_right"
-    android:layout_marginBottom="@dimen/floating_window_margin_bottom">
+    android:orientation="vertical">
 
     <LinearLayout android:id="@+id/topPanel"
         android:layout_width="match_parent"
diff --git a/core/res/res/layout/screen_action_bar.xml b/core/res/res/layout/screen_action_bar.xml
index 5acb588..b3a3478 100644
--- a/core/res/res/layout/screen_action_bar.xml
+++ b/core/res/res/layout/screen_action_bar.xml
@@ -35,6 +35,7 @@
         android:layout_alignParentTop="true"
         style="?attr/actionBarStyle"
         android:transitionName="android:action_bar"
+        android:touchscreenBlocksFocus="true"
         android:gravity="top">
         <com.android.internal.widget.ActionBarView
             android:id="@+id/action_bar"
@@ -53,5 +54,6 @@
                   android:layout_height="wrap_content"
                   style="?attr/actionBarSplitStyle"
                   android:visibility="gone"
+                  android:touchscreenBlocksFocus="true"
                   android:gravity="center"/>
 </com.android.internal.widget.ActionBarOverlayLayout>
diff --git a/core/res/res/layout/screen_toolbar.xml b/core/res/res/layout/screen_toolbar.xml
index 56815f8..039e89f 100644
--- a/core/res/res/layout/screen_toolbar.xml
+++ b/core/res/res/layout/screen_toolbar.xml
@@ -35,6 +35,7 @@
         android:layout_alignParentTop="true"
         style="?attr/actionBarStyle"
         android:transitionName="android:action_bar"
+        android:touchscreenBlocksFocus="true"
         android:gravity="top">
         <Toolbar
             android:id="@+id/action_bar"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 5fdadb7..ed228e4a 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1826,6 +1826,9 @@
              Activity Transition. Corresponds to
              {@link android.view.Window#setTransitionBackgroundFadeDuration(long)}. -->
         <attr name="windowTransitionBackgroundFadeDuration" />
+
+        <!-- Elevation to use for the window. -->
+        <attr name="windowElevation" format="dimension" />
     </declare-styleable>
 
     <!-- The set of attributes that describe a AlertDialog's theme. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 53d9cee..c9c29ca 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1578,6 +1578,9 @@
     <!-- Package name for default network scorer app; overridden by product overlays. -->
     <string name="config_defaultNetworkScorerPackageName"></string>
 
+    <!-- default device has recents property -->
+    <bool name="config_hasRecents">true</bool>
+
     <!-- Defines the default set of global actions. Actions may still be disabled or hidden based
          on the current state of the device.
          Each item must be one of the following strings:
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index a0ca06e..fee5c43 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2233,6 +2233,9 @@
   <public type="attr" name="buttonBarNegativeButtonStyle" />
   <public type="attr" name="popupElevation" />
   <public type="attr" name="actionBarPopupTheme" />
+  <public type="attr" name="multiArch" />
+  <public type="attr" name="touchscreenBlocksFocus" />
+  <public type="attr" name="windowElevation" />
 
   <public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
 
@@ -2509,6 +2512,4 @@
   <!-- A transition that moves views in or out of the scene to or from the left edge when
        a view visibility changes. -->
   <public type="transition" name="slide_left"/>
-  <public type="attr" name="multiArch" />
-  <public type="attr" name="touchscreenBlocksFocus" />
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 5d57262..2ecaa5c 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2124,6 +2124,11 @@
     <string name="permdesc_bindConditionProviderService">Allows the holder to bind to the top-level interface of a condition provider service. Should never be needed for normal apps.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_bindMediaRouteService">bind to a media route service</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_bindMediaRouteService">Allows the holder to bind to the top-level interface of a media route service. Should never be needed for normal apps.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_bindDreamService">bind to a dream service</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_bindDreamService">Allows the holder to bind to the top-level interface of a dream service. Should never be needed for normal apps.</string>
@@ -3622,13 +3627,13 @@
     <!-- See SIM_REMOVED_DIALOG.  This is the title of that dialog. -->
     <string name="sim_removed_title">SIM card removed</string>
     <!-- See SIM_REMOVED_DIALOG.  This is the message of that dialog. -->
-    <string name="sim_removed_message">The mobile network will be unavailable until you restart with a valid SIM card inserted.</string>
+    <string name="sim_removed_message">The cellular network will be unavailable until you restart with a valid SIM card inserted.</string>
     <!-- See SIM_REMOVED_DIALOG.  This is the button of that dialog. -->
     <string name="sim_done_button">Done</string>
     <!-- See SIM_ADDED_DIALOG.  This is the title of that dialog. -->
     <string name="sim_added_title">SIM card added</string>
     <!-- See SIM_ADDED_DIALOG.  This is the message of that dialog. -->
-    <string name="sim_added_message">Restart your device to access the mobile network.</string>
+    <string name="sim_added_message">Restart your device to access the cellular network.</string>
     <!-- See SIM_ADDED_DIALOG.  This is the button of that dialog. -->
     <string name="sim_restart_button">Restart</string>
 
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 6aae968..32065a18 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -824,15 +824,9 @@
         <item name="textColorLink">?textColorLinkInverse</item>
     </style>
 
-    <style name="TextAppearance.Theme.Dialog" parent="TextAppearance.Theme">
-    </style>
+    <style name="TextAppearance.Theme.Dialog" parent="TextAppearance.Theme" />
 
-    <style name="TextAppearance.Theme.Dialog.AppError">
-        <item name="textColor">#ffffc0c0</item>
-    </style>
-
-    <style name="TextAppearance.Widget">
-    </style>
+    <style name="TextAppearance.Widget" />
 
     <style name="TextAppearance.Widget.Button" parent="TextAppearance.Small.Inverse">
         <item name="textColor">@color/primary_text_light_nodisable</item>
@@ -1206,6 +1200,7 @@
         <item name="navigationButtonStyle">@style/Widget.Toolbar.Button.Navigation</item>
         <item name="collapseIcon">?attr/homeAsUpIndicator</item>
         <item name="contentInsetStart">16dp</item>
+        <item name="touchscreenBlocksFocus">true</item>
     </style>
 
     <style name="Widget.Toolbar.Button.Navigation" parent="Widget">
@@ -1365,8 +1360,8 @@
         <item name="lightY">-200dp</item>
         <item name="lightZ">800dp</item>
         <item name="lightRadius">800dp</item>
-        <item name="ambientShadowAlpha">0.125</item>
-        <item name="spotShadowAlpha">0.25</item>
+        <item name="ambientShadowAlpha">0.0471</item>
+        <item name="spotShadowAlpha">0.1765</item>
     </style>
 
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b22155c..29829e8 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -289,6 +289,7 @@
   <java-symbol type="bool" name="config_enableMultiUserUI"/>
   <java-symbol type="bool" name="config_disableUsbPermissionDialogs"/>
   <java-symbol type="bool" name="config_windowIsRound" />
+  <java-symbol type="bool" name="config_hasRecents" />
 
   <java-symbol type="integer" name="config_bluetooth_max_advertisers" />
   <java-symbol type="integer" name="config_bluetooth_max_scan_filters" />
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 6ada975..a519c37 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -820,21 +820,14 @@
     <eat-comment />
 
     <!-- Theme for the dialog shown when an app crashes or ANRs. -->
-    <style name="Theme.Dialog.AppError" parent="Theme.DeviceDefault.Light.Dialog">
-        <item name="windowFrame">@null</item>
-        <item name="windowTitleStyle">@style/DialogWindowTitle</item>
-        <item name="windowBackground">@color/transparent</item>
-        <item name="windowIsFloating">true</item>
-        <item name="windowContentOverlay">@null</item>
+    <style name="Theme.Dialog.AppError" parent="Theme.DeviceDefault.Light.Dialog.Alert">
         <item name="windowContentTransitions">false</item>
-        <item name="textAppearance">@style/TextAppearance.Theme.Dialog.AppError</item>
         <item name="windowCloseOnTouchOutside">false</item>
     </style>
 
     <!-- Special theme for the recent apps dialog, to allow customization
          with overlays. -->
     <style name="Theme.Dialog.RecentApplications" parent="Theme.DeviceDefault.Light.Dialog">
-        <item name="windowFrame">@null</item>
         <item name="windowBackground">@color/transparent</item>
         <item name="windowAnimationStyle">@style/Animation.RecentApplications</item>
         <item name="textColor">@color/secondary_text_nofocus</item>
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 47f3778..efc92d9 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -1011,7 +1011,8 @@
     <style name="Theme.Material.Dialog">
         <item name="windowFrame">@null</item>
         <item name="windowTitleStyle">@style/DialogWindowTitle.Material</item>
-        <item name="windowBackground">@drawable/dialog_background_shadow_material</item>
+        <item name="windowBackground">@drawable/dialog_background_material</item>
+        <item name="windowElevation">@dimen/floating_window_z</item>
         <item name="windowIsFloating">true</item>
         <item name="windowContentOverlay">@null</item>
         <item name="windowAnimationStyle">@style/Animation.Material.Dialog</item>
@@ -1077,6 +1078,7 @@
          its pixels. -->
     <style name="Theme.Material.Dialog.NoFrame">
         <item name="windowBackground">@color/transparent</item>
+        <item name="windowElevation">0dp</item>
         <item name="windowAnimationStyle">@null</item>
         <item name="backgroundDimEnabled">false</item>
         <item name="windowIsTranslucent">true</item>
@@ -1085,7 +1087,6 @@
     </style>
 
     <style name="Theme.Material.Dialog.BaseAlert">
-        <item name="windowBackground">@color/transparent</item>
         <item name="windowTitleStyle">@style/DialogWindowTitle.Material</item>
         <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
         <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
@@ -1100,6 +1101,7 @@
 
     <style name="Theme.Material.Dialog.BaseTimePicker">
         <item name="windowBackground">@color/transparent</item>
+        <item name="windowElevation">0dp</item>
         <item name="windowTitleStyle">@style/DialogWindowTitle.Material</item>
         <item name="windowContentOverlay">@null</item>
     </style>
@@ -1131,7 +1133,8 @@
     <style name="Theme.Material.Light.Dialog">
         <item name="windowFrame">@null</item>
         <item name="windowTitleStyle">@style/DialogWindowTitle.Material.Light</item>
-        <item name="windowBackground">@drawable/dialog_background_shadow_material</item>
+        <item name="windowBackground">@drawable/dialog_background_material</item>
+        <item name="windowElevation">@dimen/floating_window_z</item>
         <item name="windowIsFloating">true</item>
         <item name="windowContentOverlay">@null</item>
         <item name="windowAnimationStyle">@style/Animation.Material.Dialog</item>
@@ -1203,7 +1206,6 @@
     <style name="Theme.Material.Light.DialogWhenLarge.NoActionBar" parent="@style/Theme.Material.Light.NoActionBar" />
 
     <style name="Theme.Material.Light.Dialog.BaseAlert">
-        <item name="windowBackground">@color/transparent</item>
         <item name="windowTitleStyle">@style/DialogWindowTitle.Material.Light</item>
         <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
         <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
@@ -1218,6 +1220,7 @@
 
     <style name="Theme.Material.Light.Dialog.BaseTimePicker">
         <item name="windowBackground">@color/transparent</item>
+        <item name="windowElevation">0dp</item>
         <item name="windowTitleStyle">@style/DialogWindowTitle.Material.Light</item>
     </style>
 
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index a2cc40c..b524177 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1257,4 +1257,11 @@
     <instrumentation android:name="android.test.InstrumentationTestRunner"
             android:targetPackage="com.android.frameworks.coretests"
             android:label="Frameworks Core Tests" />
+    <key-sets>
+        <key-set android:name="A" >
+          <public-key android:name="keyA"
+                      android:value="MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJoN1Nsgqf0V4C/bbN8wo8O2X/S5D76+5Mb9mlIsHkUTUTbHCNk+LxHIUYLm89YbP9zImrV0bUHLUAZUyoMUCiMCAwEAAQ=="/>
+        </key-set>
+        <upgrade-key-set android:name="A"/>
+    </key-sets>
 </manifest>
diff --git a/core/tests/coretests/apks/keyset/Android.mk b/core/tests/coretests/apks/keyset/Android.mk
index e44ac6c..306dc90 100644
--- a/core/tests/coretests/apks/keyset/Android.mk
+++ b/core/tests/coretests/apks/keyset/Android.mk
@@ -88,4 +88,21 @@
 LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_A
 LOCAL_ADDITIONAL_CERTIFICATES := $(LOCAL_PATH)/../../certs/keyset_B
 LOCAL_MANIFEST_FILE := uB/AndroidManifest.xml
+include $(FrameworkCoreTests_BUILD_PACKAGE)
+
+#apks signed by platform only
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_PACKAGE_NAME := keyset_splat_api
+LOCAL_CERTIFICATE := platform
+LOCAL_MANIFEST_FILE := api_test/AndroidManifest.xml
+include $(FrameworkCoreTests_BUILD_PACKAGE)
+
+#apks signed by platform and keyset_A
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_PACKAGE_NAME := keyset_splata_api
+LOCAL_CERTIFICATE := platform
+LOCAL_ADDITIONAL_CERTIFICATES := $(LOCAL_PATH)/../../certs/keyset_A
+LOCAL_MANIFEST_FILE := api_test/AndroidManifest.xml
 include $(FrameworkCoreTests_BUILD_PACKAGE)
\ No newline at end of file
diff --git a/core/tests/coretests/apks/keyset/api_test/AndroidManifest.xml b/core/tests/coretests/apks/keyset/api_test/AndroidManifest.xml
new file mode 100644
index 0000000..4c7e968
--- /dev/null
+++ b/core/tests/coretests/apks/keyset/api_test/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.frameworks.coretests.keysets_api">
+    <application android:hasCode="false">
+    </application>
+</manifest>
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index 0244425..3a80309 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -26,6 +26,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.KeySet;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -33,6 +34,7 @@
 import android.content.res.Resources;
 import android.content.res.Resources.NotFoundException;
 import android.net.Uri;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.FileUtils;
@@ -3328,6 +3330,174 @@
     }
 
     /**
+     * The following tests are related to testing KeySets-based API
+     */
+
+    /*
+     * testGetSigningKeySetNull - ensure getSigningKeySet() returns null on null
+     * input and when calling a package other than that which made the call.
+     */
+    public void testGetSigningKeySet() throws Exception {
+        PackageManager pm = getPm();
+        String mPkgName = mContext.getPackageName();
+        String otherPkgName = "com.android.frameworks.coretests.keysets_api";
+        KeySet ks;
+        try {
+            ks = pm.getSigningKeySet(null);
+            assertTrue(false); // should have thrown
+        } catch (NullPointerException e) {
+        }
+        try {
+            ks = pm.getSigningKeySet("keysets.test.bogus.package");
+            assertTrue(false); // should have thrown
+        } catch (IllegalArgumentException e) {
+        }
+        installFromRawResource("keysetApi.apk", R.raw.keyset_splat_api,
+                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        try {
+            ks = pm.getSigningKeySet(otherPkgName);
+            assertTrue(false); // should have thrown
+        } catch (SecurityException e) {
+        }
+        cleanUpInstall(otherPkgName);
+        ks = pm.getSigningKeySet(mContext.getPackageName());
+        assertNotNull(ks);
+    }
+
+    /*
+     * testGetKeySetByAlias - same as getSigningKeySet, but for keysets defined
+     * by this package.
+     */
+    public void testGetKeySetByAlias() throws Exception {
+        PackageManager pm = getPm();
+        String mPkgName = mContext.getPackageName();
+        String otherPkgName = "com.android.frameworks.coretests.keysets_api";
+        KeySet ks;
+        try {
+            ks = pm.getKeySetByAlias(null, null);
+            assertTrue(false); // should have thrown
+        } catch (NullPointerException e) {
+        }
+        try {
+            ks = pm.getKeySetByAlias(null, "keysetBogus");
+            assertTrue(false); // should have thrown
+        } catch (NullPointerException e) {
+        }
+        try {
+            ks = pm.getKeySetByAlias("keysets.test.bogus.package", null);
+            assertTrue(false); // should have thrown
+        } catch (NullPointerException e) {
+        }
+        try {
+            ks = pm.getKeySetByAlias("keysets.test.bogus.package", "A");
+            assertTrue(false); // should have thrown
+        } catch(IllegalArgumentException e) {
+        }
+        try {
+            ks = pm.getKeySetByAlias(mPkgName, "keysetBogus");
+            assertTrue(false); // should have thrown
+        } catch(IllegalArgumentException e) {
+        }
+        installFromRawResource("keysetApi.apk", R.raw.keyset_splat_api,
+                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        try {
+            ks = pm.getKeySetByAlias(otherPkgName, "A");
+            assertTrue(false); // should have thrown
+        } catch (SecurityException e) {
+        }
+        cleanUpInstall(otherPkgName);
+        ks = pm.getKeySetByAlias(mPkgName, "A");
+        assertNotNull(ks);
+    }
+
+    public void testIsSignedBy() throws Exception {
+        PackageManager pm = getPm();
+        String mPkgName = mContext.getPackageName();
+        String otherPkgName = "com.android.frameworks.coretests.keysets_api";
+        KeySet mSigningKS = pm.getSigningKeySet(mPkgName);
+        KeySet mDefinedKS = pm.getKeySetByAlias(mPkgName, "A");
+
+        try {
+            assertFalse(pm.isSignedBy(null, null));
+            assertTrue(false); // should have thrown
+        } catch (NullPointerException e) {
+        }
+        try {
+            assertFalse(pm.isSignedBy(null, mSigningKS));
+            assertTrue(false); // should have thrown
+        } catch (NullPointerException e) {
+        }
+        try {
+            assertFalse(pm.isSignedBy(mPkgName, null));
+            assertTrue(false); // should have thrown
+        } catch (NullPointerException e) {
+        }
+        try {
+            assertFalse(pm.isSignedBy("keysets.test.bogus.package", mDefinedKS));
+        } catch(IllegalArgumentException e) {
+        }
+        assertFalse(pm.isSignedBy(mPkgName, mDefinedKS));
+        assertFalse(pm.isSignedBy(mPkgName, new KeySet(new Binder())));
+        assertTrue(pm.isSignedBy(mPkgName, mSigningKS));
+
+        installFromRawResource("keysetApi.apk", R.raw.keyset_splat_api,
+                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        assertFalse(pm.isSignedBy(otherPkgName, mDefinedKS));
+        assertTrue(pm.isSignedBy(otherPkgName, mSigningKS));
+        cleanUpInstall(otherPkgName);
+
+        installFromRawResource("keysetApi.apk", R.raw.keyset_splata_api,
+                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        assertTrue(pm.isSignedBy(otherPkgName, mDefinedKS));
+        assertTrue(pm.isSignedBy(otherPkgName, mSigningKS));
+        cleanUpInstall(otherPkgName);
+    }
+
+    public void testIsSignedByExactly() throws Exception {
+        PackageManager pm = getPm();
+        String mPkgName = mContext.getPackageName();
+        String otherPkgName = "com.android.frameworks.coretests.keysets_api";
+        KeySet mSigningKS = pm.getSigningKeySet(mPkgName);
+        KeySet mDefinedKS = pm.getKeySetByAlias(mPkgName, "A");
+        try {
+            assertFalse(pm.isSignedBy(null, null));
+            assertTrue(false); // should have thrown
+        } catch (NullPointerException e) {
+        }
+        try {
+            assertFalse(pm.isSignedBy(null, mSigningKS));
+            assertTrue(false); // should have thrown
+        } catch (NullPointerException e) {
+        }
+        try {
+            assertFalse(pm.isSignedBy(mPkgName, null));
+            assertTrue(false); // should have thrown
+        } catch (NullPointerException e) {
+        }
+        try {
+            assertFalse(pm.isSignedByExactly("keysets.test.bogus.package", mDefinedKS));
+        } catch(IllegalArgumentException e) {
+        }
+        assertFalse(pm.isSignedByExactly(mPkgName, mDefinedKS));
+        assertFalse(pm.isSignedByExactly(mPkgName, new KeySet(new Binder())));
+        assertTrue(pm.isSignedByExactly(mPkgName, mSigningKS));
+
+        installFromRawResource("keysetApi.apk", R.raw.keyset_splat_api,
+                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        assertFalse(pm.isSignedByExactly(otherPkgName, mDefinedKS));
+        assertTrue(pm.isSignedByExactly(otherPkgName, mSigningKS));
+        cleanUpInstall(otherPkgName);
+
+        installFromRawResource("keysetApi.apk", R.raw.keyset_splata_api,
+                0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED);
+        assertFalse(pm.isSignedByExactly(otherPkgName, mDefinedKS));
+        assertFalse(pm.isSignedByExactly(otherPkgName, mSigningKS));
+        cleanUpInstall(otherPkgName);
+    }
+
+
+
+    /**
      * The following tests are related to testing the checkSignatures api.
      */
     private void checkSignatures(int apk1, int apk2, int expMatchResult) throws Exception {
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 9561ac4..4b00e22 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -73,8 +73,8 @@
      */
     private boolean mRequestPremultiplied;
 
-    private byte[] mNinePatchChunk;   // may be null
-    private int[] mOpticalInsets;     // may be null
+    private byte[] mNinePatchChunk; // may be null
+    private NinePatch.InsetStruct mNinePatchInsets; // may be null
     private int mWidth;
     private int mHeight;
     private boolean mRecycled;
@@ -111,7 +111,7 @@
     @SuppressWarnings({"UnusedDeclaration"}) // called from JNI
     Bitmap(long nativeBitmap, byte[] buffer, int width, int height, int density,
             boolean isMutable, boolean requestPremultiplied,
-            byte[] ninePatchChunk, int[] opticalInsets) {
+            byte[] ninePatchChunk, NinePatch.InsetStruct ninePatchInsets) {
         if (nativeBitmap == 0) {
             throw new RuntimeException("internal error: native bitmap is 0");
         }
@@ -126,7 +126,7 @@
         mFinalizer = new BitmapFinalizer(nativeBitmap);
 
         mNinePatchChunk = ninePatchChunk;
-        mOpticalInsets = opticalInsets;
+        mNinePatchInsets = ninePatchInsets;
         if (density >= 0) {
             mDensity = density;
         }
@@ -946,16 +946,18 @@
      * @hide
      */
     public void getOpticalInsets(@NonNull Rect outInsets) {
-        if (mOpticalInsets == null) {
+        if (mNinePatchInsets == null) {
             outInsets.setEmpty();
         } else {
-            outInsets.left = mOpticalInsets[0];
-            outInsets.top = mOpticalInsets[1];
-            outInsets.right = mOpticalInsets[2];
-            outInsets.bottom = mOpticalInsets[3];
+            outInsets.set(mNinePatchInsets.opticalRect);
         }
     }
 
+    /** @hide */
+    public NinePatch.InsetStruct getNinePatchInsets() {
+        return mNinePatchInsets;
+    }
+
     /**
      * Specifies the known formats a bitmap can be compressed into
      */
diff --git a/graphics/java/android/graphics/NinePatch.java b/graphics/java/android/graphics/NinePatch.java
index befac92..335bce0 100644
--- a/graphics/java/android/graphics/NinePatch.java
+++ b/graphics/java/android/graphics/NinePatch.java
@@ -16,7 +16,6 @@
 
 package android.graphics;
 
-
 /**
  * The NinePatch class permits drawing a bitmap in nine or more sections.
  * Essentially, it allows the creation of custom graphics that will scale the
@@ -32,6 +31,39 @@
  * </p>
  */
 public class NinePatch {
+    /**
+     * Struct of inset information attached to a 9 patch bitmap.
+     *
+     * Present on a 9 patch bitmap if it optical insets were manually included,
+     * or if outline insets were automatically included by aapt.
+     *
+     * @hide
+     */
+    public static class InsetStruct {
+        @SuppressWarnings({"UnusedDeclaration"}) // called from JNI
+        InsetStruct(int opticalLeft, int opticalTop, int opticalRight, int opticalBottom,
+                int outlineLeft, int outlineTop, int outlineRight, int outlineBottom,
+                float outlineRadius, boolean outlineFilled, float decodeScale) {
+            opticalRect = new Rect(opticalLeft, opticalTop, opticalRight, opticalBottom);
+            outlineRect = new Rect(outlineLeft, outlineTop, outlineRight, outlineBottom);
+
+            if (decodeScale != 1.0f) {
+                // if bitmap was scaled when decoded, scale the insets from the metadata values
+                opticalRect.scale(decodeScale);
+
+                // round inward while scaling outline, as the outline should always be conservative
+                outlineRect.scaleRoundIn(decodeScale);
+            }
+            this.outlineRadius = outlineRadius * decodeScale;
+            this.outlineFilled = outlineFilled;
+        }
+
+        public final Rect opticalRect;
+        public final Rect outlineRect;
+        public final float outlineRadius;
+        public final boolean outlineFilled;
+    }
+
     private final Bitmap mBitmap;
 
     /**
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index 437d2f4..a9a8f37 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -609,4 +609,17 @@
             bottom = (int) (bottom * scale + 0.5f);
         }
     }
+
+    /**
+     * Scales up the rect by the given scale, rounding values toward the inside.
+     * @hide
+     */
+    public void scaleRoundIn(float scale) {
+        if (scale != 1.0f) {
+            left = (int) Math.ceil(left * scale);
+            top = (int) Math.ceil(top * scale);
+            right = (int) Math.floor(right * scale);
+            bottom = (int) Math.floor(bottom * scale);
+        }
+    }
 }
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 1cecef3..b5fc628 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -209,6 +209,7 @@
                     if (drawableRes != 0) {
                         mAnimatedVectorState.mVectorDrawable = (VectorDrawable) res.getDrawable(
                                 drawableRes, theme).mutate();
+                        mAnimatedVectorState.mVectorDrawable.setAllowCaching(false);
                     }
                     a.recycle();
                 } else if (TARGET.equals(tagName)) {
@@ -258,6 +259,7 @@
                 mChangingConfigurations = copy.mChangingConfigurations;
                 // TODO: Make sure the constant state are handled correctly.
                 mVectorDrawable = new VectorDrawable();
+                mVectorDrawable.setAllowCaching(false);
                 mAnimators = new ArrayList<Animator>();
             }
         }
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 24bbf7c..c0110c9 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -16,6 +16,7 @@
 
 package android.graphics.drawable;
 
+import android.annotation.NonNull;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
@@ -26,6 +27,7 @@
 import android.graphics.ColorFilter;
 import android.graphics.Insets;
 import android.graphics.NinePatch;
+import android.graphics.Outline;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
 import android.graphics.PorterDuff;
@@ -281,14 +283,35 @@
         return false;
     }
 
+    @Override
+    public boolean getOutline(@NonNull Outline outline) {
+        final Rect bounds = getBounds();
+        if (bounds.isEmpty()) return false;
+
+        if (mNinePatchState != null) {
+            NinePatch.InsetStruct insets = mNinePatchState.getBitmap().getNinePatchInsets();
+            if (insets != null) {
+                final Rect outlineInsets = insets.outlineRect;
+                outline.setRoundRect(bounds.left + outlineInsets.left,
+                        bounds.top + outlineInsets.top,
+                        bounds.right - outlineInsets.right,
+                        bounds.bottom - outlineInsets.bottom,
+                        insets.outlineRadius);
+                outline.setFilled(insets.outlineFilled);
+                return true;
+            }
+        }
+        return super.getOutline(outline);
+    }
+
     /**
      * @hide
      */
     @Override
     public Insets getOpticalInsets() {
         if (needsMirroring()) {
-            return Insets.of(mOpticalInsets.right, mOpticalInsets.top, mOpticalInsets.right,
-                    mOpticalInsets.bottom);
+            return Insets.of(mOpticalInsets.right, mOpticalInsets.top,
+                    mOpticalInsets.left, mOpticalInsets.bottom);
         } else {
             return mOpticalInsets;
         }
@@ -574,7 +597,7 @@
         }
 
         NinePatchState(NinePatch ninePatch, Rect padding) {
-            this(ninePatch, padding, new Rect(), DEFAULT_DITHER, false);
+            this(ninePatch, padding, null, DEFAULT_DITHER, false);
         }
 
         NinePatchState(NinePatch ninePatch, Rect padding, Rect opticalInsets) {
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index 8783994..8c907b2 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -18,6 +18,7 @@
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
+import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.ColorFilter;
@@ -142,6 +143,10 @@
 
     private boolean mMutated;
 
+    // AnimatedVectorDrawable needs to turn off the cache all the time, otherwise,
+    // caching the bitmap by default is allowed.
+    private boolean mAllowCaching = true;
+
     public VectorDrawable() {
         mVectorState = new VectorDrawableState();
     }
@@ -183,7 +188,23 @@
         final int saveCount = canvas.save();
         final Rect bounds = getBounds();
         canvas.translate(bounds.left, bounds.top);
-        mVectorState.mVPathRenderer.draw(canvas, bounds.width(), bounds.height());
+
+        if (!mAllowCaching) {
+            mVectorState.mVPathRenderer.draw(canvas, bounds.width(), bounds.height());
+        } else {
+            Bitmap bitmap = mVectorState.mCachedBitmap;
+            if (bitmap == null || !mVectorState.canReuseCache(bounds.width(),
+                    bounds.height())) {
+                bitmap = Bitmap.createBitmap(bounds.width(), bounds.height(),
+                        Bitmap.Config.ARGB_8888);
+                Canvas tmpCanvas = new Canvas(bitmap);
+                mVectorState.mVPathRenderer.draw(tmpCanvas, bounds.width(), bounds.height());
+                mVectorState.mCachedBitmap = bitmap;
+
+                mVectorState.updateCacheStates();
+            }
+            canvas.drawBitmap(bitmap, null, bounds, null);
+        }
         canvas.restoreToCount(saveCount);
     }
 
@@ -444,6 +465,10 @@
         return super.getChangingConfigurations() | mVectorState.mChangingConfigurations;
     }
 
+    void setAllowCaching(boolean allowCaching) {
+        mAllowCaching = allowCaching;
+    }
+
     private static class VectorDrawableState extends ConstantState {
         int[] mThemeAttrs;
         int mChangingConfigurations;
@@ -451,6 +476,12 @@
         ColorStateList mTint;
         Mode mTintMode;
 
+        Bitmap mCachedBitmap;
+        int[] mCachedThemeAttrs;
+        ColorStateList mCachedTint;
+        Mode mCachedTintMode;
+        int mCachedRootAlpha;
+
         // Deep copy for mutate() or implicitly mutate.
         public VectorDrawableState(VectorDrawableState copy) {
             if (copy != null) {
@@ -462,6 +493,27 @@
             }
         }
 
+        public boolean canReuseCache(int width, int height) {
+            if (mCachedThemeAttrs == mThemeAttrs
+                    && mCachedTint == mTint
+                    && mCachedTintMode == mTintMode
+                    && width == mCachedBitmap.getWidth()
+                    && height == mCachedBitmap.getHeight()
+                    && mCachedRootAlpha == mVPathRenderer.getRootAlpha())  {
+                return true;
+            }
+            return false;
+        }
+
+        public void updateCacheStates() {
+            // Use shallow copy here and shallow comparison in canReuseCache(),
+            // likely hit cache miss more, but practically not much difference.
+            mCachedThemeAttrs = mThemeAttrs;
+            mCachedTint = mTint;
+            mCachedTintMode = mTintMode;
+            mCachedRootAlpha = mVPathRenderer.getRootAlpha();
+        }
+
         public VectorDrawableState() {
             mVPathRenderer = new VPathRenderer();
         }
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index eb6bf2c..c65961d 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -2912,6 +2912,20 @@
     }
 
     /**
+     * Notify audio manager about volume controller visibility changes.
+     * Currently limited to SystemUI.
+     *
+     * @hide
+     */
+    public void notifyVolumeControllerVisible(IVolumeController controller, boolean visible) {
+        try {
+            getService().notifyVolumeControllerVisible(controller, visible);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Error notifying about volume controller visibility", e);
+        }
+    }
+
+    /**
      * Only useful for volume controllers.
      * @hide
      */
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index b08d631..ab63145 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -61,6 +61,7 @@
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.Vibrator;
@@ -810,6 +811,9 @@
 
         // Restore the default media button receiver from the system settings
         mMediaFocusControl.restoreMediaButtonReceiver();
+
+        // Load settings for the volume controller
+        mVolumeController.loadSettings(cr);
     }
 
     private int rescaleIndex(int index, int srcStream, int dstStream) {
@@ -851,14 +855,23 @@
         } else {
             streamType = getActiveStreamType(suggestedStreamType);
         }
+        final int resolvedStream = mStreamVolumeAlias[streamType];
 
         // Play sounds on STREAM_RING and STREAM_REMOTE_MUSIC only.
         if ((streamType != STREAM_REMOTE_MUSIC) &&
                 (flags & AudioManager.FLAG_PLAY_SOUND) != 0 &&
-                (mStreamVolumeAlias[streamType] != AudioSystem.STREAM_RING)) {
+                resolvedStream != AudioSystem.STREAM_RING) {
             flags &= ~AudioManager.FLAG_PLAY_SOUND;
         }
 
+        // For notifications/ring, show the ui before making any adjustments
+        if (mVolumeController.suppressAdjustment(resolvedStream, flags)) {
+            direction = 0;
+            flags &= ~AudioManager.FLAG_PLAY_SOUND;
+            flags &= ~AudioManager.FLAG_VIBRATE;
+            if (DEBUG_VOL) Log.d(TAG, "Volume controller suppressed adjustment");
+        }
+
         if (streamType == STREAM_REMOTE_MUSIC) {
             // TODO bounce it to MediaSessionService to find an appropriate
             // session
@@ -4955,15 +4968,65 @@
             }
         }
         mVolumeController.setController(controller);
+        if (DEBUG_VOL) Log.d(TAG, "Volume controller: " + mVolumeController);
+    }
+
+    @Override
+    public void notifyVolumeControllerVisible(final IVolumeController controller, boolean visible) {
+        enforceSelfOrSystemUI("notify about volume controller visibility");
+
+        // return early if the controller is not current
+        if (!mVolumeController.isSameBinder(controller)) {
+            return;
+        }
+
+        mVolumeController.setVisible(visible);
+        if (DEBUG_VOL) Log.d(TAG, "Volume controller visible: " + visible);
     }
 
     public static class VolumeController {
         private static final String TAG = "VolumeController";
 
         private IVolumeController mController;
+        private boolean mVisible;
+        private long mNextLongPress;
+        private int mLongPressTimeout;
 
         public void setController(IVolumeController controller) {
             mController = controller;
+            mVisible = false;
+        }
+
+        public void loadSettings(ContentResolver cr) {
+            mLongPressTimeout = Settings.Secure.getIntForUser(cr,
+                    Settings.Secure.LONG_PRESS_TIMEOUT, 500, UserHandle.USER_CURRENT);
+        }
+
+        public boolean suppressAdjustment(int resolvedStream, int flags) {
+            boolean suppress = false;
+            if (resolvedStream == AudioSystem.STREAM_RING && mController != null) {
+                final long now = SystemClock.uptimeMillis();
+                if ((flags & AudioManager.FLAG_SHOW_UI) != 0 && !mVisible) {
+                    // ui will become visible
+                    if (mNextLongPress < now) {
+                        mNextLongPress = now + mLongPressTimeout;
+                    }
+                    suppress = true;
+                } else if (mNextLongPress > 0) {  // in a long-press
+                    if (now > mNextLongPress) {
+                        // long press triggered, no more suppression
+                        mNextLongPress = 0;
+                    } else {
+                        // keep suppressing until the long press triggers
+                        suppress = true;
+                    }
+                }
+            }
+            return suppress;
+        }
+
+        public void setVisible(boolean visible) {
+            mVisible = visible;
         }
 
         public boolean isSameBinder(IVolumeController controller) {
@@ -4980,7 +5043,7 @@
 
         @Override
         public String toString() {
-            return "VolumeController(" + asBinder() + ")";
+            return "VolumeController(" + asBinder() + ",mVisible=" + mVisible + ")";
         }
 
         public void postDisplaySafeVolumeWarning(int flags) {
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index e112a65..4f7021e 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -195,6 +195,8 @@
 
     void setVolumeController(in IVolumeController controller);
 
+    void notifyVolumeControllerVisible(in IVolumeController controller, boolean visible);
+
     boolean isStreamAffectedByRingerMode(int streamType);
 
     void disableSafeMediaVolume();
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index cb9776a..4abcb81 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -1157,6 +1157,9 @@
      * @deprecated Use the new {@link #getInputBuffer} method instead
      * each time an input buffer is dequeued.
      *
+     * <b>Note:</b>As of API 21, dequeued input buffers are
+     * automatically {@link java.nio.Buffer#clear cleared}.
+     *
      * @throws IllegalStateException if not in the Executing state.
      * @throws MediaCodec.CodecException upon codec error.
      */
@@ -1180,6 +1183,10 @@
      * each time an output buffer is dequeued.  This method is not
      * supported if codec is configured in asynchronous mode.
      *
+     * <b>Note:</b>As of API 21, the position and limit of output
+     * buffers that are dequeued will be set to the valid data
+     * range.
+     *
      * @throws IllegalStateException if not in the Executing state,
      *         or codec is configured in asynchronous mode.
      * @throws MediaCodec.CodecException upon codec error.
@@ -1213,8 +1220,8 @@
     }
 
     /**
-     * Returns a cleared, writable ByteBuffer object for a dequeued
-     * input buffer index to contain the input data.
+     * Returns a {@link java.nio.Buffer#clear cleared}, writable ByteBuffer
+     * object for a dequeued input buffer index to contain the input data.
      *
      * After calling this method any ByteBuffer or Image object
      * previously returned for the same input index MUST no longer
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index f2753ee..b23b540 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -297,8 +297,12 @@
 
     /**
      * Retrieve the current encoded sample and store it in the byte buffer
-     * starting at the given offset. Returns the sample size (or -1 if
-     * no more samples are available).
+     * starting at the given offset.
+     * <p>
+     * <b>Note:</b>As of API 21, on success the position and limit of
+     * {@code byteBuf} is updated to point to the data just read.
+     * @param byteBuf the destination byte buffer
+     * @return the sample size (or -1 if no more samples are available).
      */
     public native int readSampleData(ByteBuffer byteBuf, int offset);
 
@@ -316,7 +320,10 @@
 
     // Keep these in sync with their equivalents in NuMediaExtractor.h
     /**
-     * The sample is a sync sample
+     * The sample is a sync sample (or in {@link MediaCodec}'s terminology
+     * it is a key frame.)
+     *
+     * @see MediaCodec#BUFFER_FLAG_KEY_FRAME
      */
     public static final int SAMPLE_FLAG_SYNC      = 1;
 
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index 73bc61a..740a9d3 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -682,9 +682,13 @@
                 // USE_SESSIONS
                 if (mSession != null) {
                     int pbState = PlaybackState.getStateFromRccState(state);
-                    mSessionPlaybackState.setState(pbState, hasPosition ?
-                            mPlaybackPositionMs : PlaybackState.PLAYBACK_POSITION_UNKNOWN,
-                            playbackSpeed);
+                    long position = hasPosition ? mPlaybackPositionMs
+                            : PlaybackState.PLAYBACK_POSITION_UNKNOWN;
+
+                    PlaybackState.Builder bob = new PlaybackState.Builder(mSessionPlaybackState);
+                    bob.setState(pbState, position, playbackSpeed, SystemClock.elapsedRealtime());
+                    bob.setErrorMessage(null);
+                    mSessionPlaybackState = bob.build();
                     mSession.setPlaybackState(mSessionPlaybackState);
                 }
             }
@@ -745,8 +749,9 @@
 
             // USE_SESSIONS
             if (mSession != null) {
-                mSessionPlaybackState.setActions(PlaybackState
-                        .getActionsFromRccControlFlags(transportControlFlags));
+                PlaybackState.Builder bob = new PlaybackState.Builder(mSessionPlaybackState);
+                bob.setActions(PlaybackState.getActionsFromRccControlFlags(transportControlFlags));
+                mSessionPlaybackState = bob.build();
                 mSession.setPlaybackState(mSessionPlaybackState);
             }
         }
@@ -946,7 +951,7 @@
     /**
      * Cache for the current playback state using Session APIs.
      */
-    private final PlaybackState mSessionPlaybackState = new PlaybackState();
+    private PlaybackState mSessionPlaybackState = null;
 
     /**
      * Cache for metadata using Session APIs. This is re-initialized in apply().
diff --git a/media/java/android/media/RemoteController.java b/media/java/android/media/RemoteController.java
index 1f5b216..9ea3f26 100644
--- a/media/java/android/media/RemoteController.java
+++ b/media/java/android/media/RemoteController.java
@@ -1020,7 +1020,7 @@
                 l.onClientPlaybackStateUpdate(playstate);
             } else {
                 l.onClientPlaybackStateUpdate(playstate, state.getLastPositionUpdateTime(),
-                        state.getPosition(), state.getPlaybackRate());
+                        state.getPosition(), state.getPlaybackSpeed());
             }
             if (state != null) {
                 l.onClientTransportControlUpdate(PlaybackState.getRccControlFlagsFromActions(state
diff --git a/media/java/android/media/routeprovider/IRouteConnection.aidl b/media/java/android/media/routeprovider/IRouteConnection.aidl
deleted file mode 100644
index 15c8039..0000000
--- a/media/java/android/media/routeprovider/IRouteConnection.aidl
+++ /dev/null
@@ -1,28 +0,0 @@
-/* Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.routeprovider;
-
-import android.media.session.RouteCommand;
-import android.os.ResultReceiver;
-
-/**
- * Interface for a specific connected route.
- * @hide
- */
-oneway interface IRouteConnection {
-    void onCommand(in RouteCommand command, in ResultReceiver cb);
-    void disconnect();
-}
\ No newline at end of file
diff --git a/media/java/android/media/routeprovider/IRouteProvider.aidl b/media/java/android/media/routeprovider/IRouteProvider.aidl
deleted file mode 100644
index c36f6a7..0000000
--- a/media/java/android/media/routeprovider/IRouteProvider.aidl
+++ /dev/null
@@ -1,36 +0,0 @@
-/* Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.routeprovider;
-
-import android.media.routeprovider.IRouteConnection;
-import android.media.routeprovider.IRouteProviderCallback;
-import android.media.routeprovider.RouteRequest;
-import android.media.session.RouteInfo;
-import android.os.Bundle;
-import android.os.ResultReceiver;
-
-/**
- * Interface to an app's RouteProviderService.
- * @hide
- */
-oneway interface IRouteProvider {
-    void registerCallback(in IRouteProviderCallback cb);
-    void unregisterCallback(in IRouteProviderCallback cb);
-    void updateDiscoveryRequests(in List<RouteRequest> requests);
-
-    void getAvailableRoutes(in List<RouteRequest> requests, in ResultReceiver cb);
-    void connect(in RouteInfo route, in RouteRequest request, in ResultReceiver cb);
-}
\ No newline at end of file
diff --git a/media/java/android/media/routeprovider/IRouteProviderCallback.aidl b/media/java/android/media/routeprovider/IRouteProviderCallback.aidl
deleted file mode 100644
index 9185347..0000000
--- a/media/java/android/media/routeprovider/IRouteProviderCallback.aidl
+++ /dev/null
@@ -1,32 +0,0 @@
-/* Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.routeprovider;
-
-import android.media.routeprovider.IRouteConnection;
-import android.media.session.RouteEvent;
-import android.os.Bundle;
-import android.os.ResultReceiver;
-
-/**
- * System's provider callback interface.
- * @hide
- */
-oneway interface IRouteProviderCallback {
-    void onRoutesChanged();
-    void onConnectionStateChanged(in IRouteConnection connection, int state);
-    void onConnectionTerminated(in IRouteConnection connection);
-    void onRouteEvent(in RouteEvent event);
-}
\ No newline at end of file
diff --git a/media/java/android/media/routeprovider/RouteConnection.java b/media/java/android/media/routeprovider/RouteConnection.java
deleted file mode 100644
index 43692c1..0000000
--- a/media/java/android/media/routeprovider/RouteConnection.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.media.routeprovider;
-
-import android.media.routeprovider.IRouteConnection;
-import android.media.session.RouteCommand;
-import android.media.session.RouteEvent;
-import android.media.session.RouteInfo;
-import android.media.session.RouteInterface;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Represents an ongoing connection between an application and a media route
- * offered by a media route provider.
- * <p>
- * The media route provider should add interfaces to the connection before
- * returning it to the system in order to receive commands from clients on those
- * interfaces. Use {@link #addRouteInterface(String)} to add an interface and
- * {@link #getRouteInterface(String)} to retrieve the interface's handle anytime
- * after it has been added.
- * @hide
- */
-public final class RouteConnection {
-    private static final String TAG = "RouteConnection";
-    private final ConnectionStub mBinder;
-    private final ArrayList<String> mIfaceNames = new ArrayList<String>();
-    private final ArrayMap<String, RouteInterfaceHandler> mIfaces
-            = new ArrayMap<String, RouteInterfaceHandler>();
-    private final RouteProviderService mProvider;
-    private final RouteInfo mRoute;
-
-    private boolean mPublished;
-
-    /**
-     * Create a new connection for the given Provider and Route.
-     *
-     * @param provider The provider this route is associated with.
-     * @param route The route this is a connection to.
-     */
-    public RouteConnection(RouteProviderService provider, RouteInfo route) {
-        if (provider == null) {
-            throw new IllegalArgumentException("provider may not be null.");
-        }
-        if (route == null) {
-            throw new IllegalArgumentException("route may not be null.");
-        }
-        mBinder = new ConnectionStub(this);
-        mProvider = provider;
-        mRoute = route;
-    }
-
-    /**
-     * Add an interface to this route connection. All interfaces must be added
-     * to the connection before the connection is returned to the system.
-     *
-     * @param ifaceName The name of the interface to add
-     * @return The route interface that was registered
-     */
-    public RouteInterfaceHandler addRouteInterface(String ifaceName) {
-        if (TextUtils.isEmpty(ifaceName)) {
-            throw new IllegalArgumentException("The interface's name may not be empty");
-        }
-        if (mPublished) {
-            throw new IllegalStateException(
-                    "Connection has already been published to the system.");
-        }
-        RouteInterfaceHandler iface = mIfaces.get(ifaceName);
-        if (iface == null) {
-            iface = new RouteInterfaceHandler(this, ifaceName);
-            mIfaceNames.add(ifaceName);
-            mIfaces.put(ifaceName, iface);
-        } else {
-            Log.w(TAG, "Attempted to add an interface that already exists");
-        }
-        return iface;
-    }
-
-    /**
-     * Get the interface instance for the specified interface name. If the
-     * interface was not added to this connection null will be returned.
-     *
-     * @param ifaceName The name of the interface to get.
-     * @return The route interface with that name or null.
-     */
-    public RouteInterfaceHandler getRouteInterface(String ifaceName) {
-        return mIfaces.get(ifaceName);
-    }
-
-    /**
-     * Close the connection and inform the system that it may no longer be used.
-     */
-    public void shutDown() {
-        mProvider.disconnect(this);
-    }
-
-    /**
-     * @hide
-     */
-    public void sendEvent(String iface, String event, Bundle extras) {
-        RouteEvent e = new RouteEvent(mBinder, iface, event, extras);
-        mProvider.sendRouteEvent(e);
-    }
-
-    /**
-     * @hide
-     */
-    IRouteConnection.Stub getBinder() {
-        return mBinder;
-    }
-
-    /**
-     * @hide
-     */
-    void publish() {
-        mPublished = true;
-    }
-
-    private static class ConnectionStub extends IRouteConnection.Stub {
-        private final WeakReference<RouteConnection> mConnection;
-
-        public ConnectionStub(RouteConnection connection) {
-            mConnection = new WeakReference<RouteConnection>(connection);
-        }
-
-        @Override
-        public void onCommand(RouteCommand command, ResultReceiver cb) {
-            RouteConnection connection = mConnection.get();
-            if (connection != null) {
-                RouteInterfaceHandler iface = connection.mIfaces.get(command.getIface());
-                if (iface != null) {
-                    iface.onCommand(command.getEvent(), command.getExtras(), cb);
-                } else if (cb != null) {
-                    cb.send(RouteInterface.RESULT_INTERFACE_NOT_SUPPORTED, null);
-                }
-            }
-        }
-
-        @Override
-        public void disconnect() {
-            // TODO
-        }
-    }
-}
diff --git a/media/java/android/media/routeprovider/RouteInterfaceHandler.java b/media/java/android/media/routeprovider/RouteInterfaceHandler.java
deleted file mode 100644
index e7f8bbf..0000000
--- a/media/java/android/media/routeprovider/RouteInterfaceHandler.java
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.media.routeprovider;
-
-import android.media.session.Route;
-import android.media.session.MediaSession;
-import android.media.session.RouteInterface;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.ResultReceiver;
-import android.text.TextUtils;
-import android.util.Log;
-
-import java.util.ArrayList;
-
-/**
- * Represents an interface that an application may use to send requests to a
- * connected media route.
- * <p>
- * A {@link RouteProviderService} may expose multiple interfaces on a
- * {@link RouteConnection} for a {@link MediaSession} to interact with. A
- * provider creates an interface with
- * {@link RouteConnection#addRouteInterface(String)} to allow messages to be
- * routed appropriately. Events are then sent through a specific interface and
- * all commands being sent on the interface will be sent to any registered
- * {@link CommandListener}s.
- * <p>
- * An interface instance can only be registered on one {@link RouteConnection}.
- * To use the same interface on multiple connections a new instance must be
- * created for each connection.
- * <p>
- * It is recommended you wrap this interface with a standard implementation to
- * avoid errors, but for simple interfaces this class may be used directly. TODO
- * add link to sample code.
- * @hide
- */
-public final class RouteInterfaceHandler {
-    private static final String TAG = "RouteInterfaceHandler";
-
-    private final Object mLock = new Object();
-    private final RouteConnection mConnection;
-    private final String mName;
-
-    private ArrayList<MessageHandler> mListeners = new ArrayList<MessageHandler>();
-
-    /**
-     * Create a new RouteInterface for a given connection. This can be used to
-     * send events on the given interface and register listeners for commands
-     * from the connected session.
-     *
-     * @param connection The connection this interface sends events on
-     * @param ifaceName The name of this interface
-     * @hide
-     */
-    public RouteInterfaceHandler(RouteConnection connection, String ifaceName) {
-        if (connection == null) {
-            throw new IllegalArgumentException("connection may not be null");
-        }
-        if (TextUtils.isEmpty(ifaceName)) {
-            throw new IllegalArgumentException("ifaceName can not be empty");
-        }
-        mConnection = connection;
-        mName = ifaceName;
-    }
-
-    /**
-     * Send an event on this interface to the connected session.
-     *
-     * @param event The event to send
-     * @param extras Any extras for the event
-     */
-    public void sendEvent(String event, Bundle extras) {
-        mConnection.sendEvent(mName, event, extras);
-    }
-
-    /**
-     * Send a result from a command to the specified callback. The result codes
-     * in {@link RouteInterface} must be used. More information
-     * about the result, whether successful or an error, should be included in
-     * the extras.
-     *
-     * @param cb The callback to send the result to
-     * @param resultCode The result code for the call
-     * @param extras Any extras to include
-     */
-    public static void sendResult(ResultReceiver cb, int resultCode, Bundle extras) {
-        if (cb != null) {
-            cb.send(resultCode, extras);
-        }
-    }
-
-    /**
-     * Add a listener for this interface. If a handler is specified callbacks
-     * will be performed on the handler's thread, otherwise the callers thread
-     * will be used.
-     *
-     * @param listener The listener to receive calls on.
-     * @param handler The handler whose thread to post calls on or null.
-     */
-    public void addListener(CommandListener listener, Handler handler) {
-        if (listener == null) {
-            throw new IllegalArgumentException("listener may not be null");
-        }
-        Looper looper = handler != null ? handler.getLooper() : Looper.myLooper();
-        synchronized (mLock) {
-            if (findIndexOfListenerLocked(listener) != -1) {
-                Log.d(TAG, "Listener is already added, ignoring");
-                return;
-            }
-            mListeners.add(new MessageHandler(looper, listener));
-        }
-    }
-
-    /**
-     * Remove a listener from this interface.
-     *
-     * @param listener The listener to stop receiving commands on.
-     */
-    public void removeListener(CommandListener listener) {
-        if (listener == null) {
-            throw new IllegalArgumentException("listener may not be null");
-        }
-        synchronized (mLock) {
-            int index = findIndexOfListenerLocked(listener);
-            if (index != -1) {
-                mListeners.remove(index);
-            }
-        }
-    }
-
-    /**
-     * @hide
-     */
-    public void onCommand(String command, Bundle args, ResultReceiver cb) {
-        synchronized (mLock) {
-            Command cmd = new Command(command, args, cb);
-            for (int i = mListeners.size() - 1; i >= 0; i--) {
-                mListeners.get(i).post(MessageHandler.MSG_COMMAND, cmd);
-            }
-        }
-    }
-
-    /**
-     * Get the interface name.
-     *
-     * @return The name of this interface
-     */
-    public String getName() {
-        return mName;
-    }
-
-    private int findIndexOfListenerLocked(CommandListener listener) {
-        if (listener == null) {
-            throw new IllegalArgumentException("Callback cannot be null");
-        }
-        for (int i = mListeners.size() - 1; i >= 0; i--) {
-            MessageHandler handler = mListeners.get(i);
-            if (listener == handler.mListener) {
-                return i;
-            }
-        }
-        return -1;
-    }
-
-    /**
-     * Handles commands sent to the interface.
-     * <p>
-     * Register an InterfaceListener using {@link #addListener}.
-     */
-    public abstract static class CommandListener {
-        /**
-         * This is called when a command is received that matches this
-         * interface. Commands are sent by a {@link MediaSession} that is
-         * connected to the route this interface is registered with.
-         *
-         * @param iface The interface the command was received on.
-         * @param command The command or method to invoke.
-         * @param args Any args that were included with the command. May be
-         *            null.
-         * @param cb The callback provided to send a response on. May be null.
-         * @return true if the command was handled, false otherwise. If the
-         *         command was not handled an error will be sent automatically.
-         *         true may be returned if the command will be handled
-         *         asynchronously.
-         * @see Route
-         * @see MediaSession
-         */
-        public abstract boolean onCommand(RouteInterfaceHandler iface, String command, Bundle args,
-                ResultReceiver cb);
-    }
-
-    private class MessageHandler extends Handler {
-        private static final int MSG_COMMAND = 1;
-
-        private final CommandListener mListener;
-
-        public MessageHandler(Looper looper, CommandListener listener) {
-            super(looper, null, true /* async */);
-            mListener = listener;
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_COMMAND:
-                    Command cmd = (Command) msg.obj;
-                    if (!mListener.onCommand(RouteInterfaceHandler.this, cmd.command, cmd.args, cmd.cb)) {
-                        sendResult(cmd.cb, RouteInterface.RESULT_COMMAND_NOT_SUPPORTED,
-                                null);
-                    }
-                    break;
-            }
-        }
-
-        public void post(int what, Object obj) {
-            obtainMessage(what, obj).sendToTarget();
-        }
-    }
-
-    private final static class Command {
-        public final String command;
-        public final Bundle args;
-        public final ResultReceiver cb;
-
-        public Command(String command, Bundle args, ResultReceiver cb) {
-            this.command = command;
-            this.args = args;
-            this.cb = cb;
-        }
-    }
-}
diff --git a/media/java/android/media/routeprovider/RoutePlaybackControlsHandler.java b/media/java/android/media/routeprovider/RoutePlaybackControlsHandler.java
deleted file mode 100644
index f2c40d2..0000000
--- a/media/java/android/media/routeprovider/RoutePlaybackControlsHandler.java
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.media.routeprovider;
-
-import android.media.session.RoutePlaybackControls;
-import android.media.session.RouteInterface;
-import android.media.session.PlaybackState;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.ResultReceiver;
-import android.text.TextUtils;
-import android.util.Log;
-
-/**
- * Standard wrapper for using playback controls over a {@link RouteInterfaceHandler}.
- * This is the provider half of the interface. Sessions should use
- * {@link RoutePlaybackControls} to interact with this interface.
- * @hide
- */
-public final class RoutePlaybackControlsHandler {
-    private static final String TAG = "RoutePlaybackControls";
-
-    private final RouteInterfaceHandler mIface;
-
-    private RoutePlaybackControlsHandler(RouteInterfaceHandler iface) {
-        mIface = iface;
-    }
-
-    /**
-     * Add this interface to the specified route and return a handle for
-     * communicating on the interface.
-     *
-     * @param connection The connection to register this interface on.
-     * @return A handle for communicating on this interface.
-     */
-    public static RoutePlaybackControlsHandler addTo(RouteConnection connection) {
-        if (connection == null) {
-            throw new IllegalArgumentException("connection may not be null");
-        }
-        RouteInterfaceHandler iface = connection
-                .addRouteInterface(RoutePlaybackControls.NAME);
-
-        return new RoutePlaybackControlsHandler(iface);
-    }
-
-    /**
-     * Add a {@link Listener} to this interface. The listener will receive
-     * commands on the caller's thread.
-     *
-     * @param listener The listener to send commands to.
-     */
-    public void addListener(Listener listener) {
-        addListener(listener, null);
-    }
-
-    /**
-     * Add a {@link Listener} to this interface. The listener will receive
-     * updates on the handler's thread. If no handler is specified the caller's
-     * thread will be used instead.
-     *
-     * @param listener The listener to send commands to.
-     * @param handler The handler whose thread calls should be posted on. May be
-     *            null.
-     */
-    public void addListener(Listener listener, Handler handler) {
-        mIface.addListener(listener, handler);
-    }
-
-    /**
-     * Remove a {@link Listener} from this interface.
-     *
-     * @param listener The Listener to remove.
-     */
-    public void removeListener(Listener listener) {
-        mIface.removeListener(listener);
-    }
-
-    /**
-     * Publish the current playback state to the system and any controllers.
-     * Valid values are defined in {@link PlaybackState}. TODO create
-     * RoutePlaybackState.
-     *
-     * @param state
-     */
-    public void sendPlaybackChangeEvent(int state) {
-        Bundle extras = new Bundle();
-        extras.putInt(RoutePlaybackControls.KEY_VALUE1, state);
-        mIface.sendEvent(RoutePlaybackControls.EVENT_PLAYSTATE_CHANGE, extras);
-    }
-
-    /**
-     * Command handler for the RoutePlaybackControls interface. You can add a
-     * Listener to the interface using {@link #addListener}.
-     */
-    public static abstract class Listener extends RouteInterfaceHandler.CommandListener {
-
-        @Override
-        public final boolean onCommand(RouteInterfaceHandler iface, String method, Bundle extras,
-                ResultReceiver cb) {
-            if (RoutePlaybackControls.CMD_FAST_FORWARD.equals(method)) {
-                boolean success = fastForward();
-                // TODO specify type of error
-                RouteInterfaceHandler.sendResult(cb, success
-                        ? RouteInterface.RESULT_SUCCESS
-                        : RouteInterface.RESULT_ERROR, null);
-                return true;
-            } else if (RoutePlaybackControls.CMD_GET_CURRENT_POSITION.equals(method)) {
-                Bundle result = new Bundle();
-                result.putLong(RoutePlaybackControls.KEY_VALUE1, getCurrentPosition());
-                RouteInterfaceHandler.sendResult(cb, RouteInterface.RESULT_SUCCESS,
-                        result);
-                return true;
-            } else if (RoutePlaybackControls.CMD_GET_CAPABILITIES.equals(method)) {
-                Bundle result = new Bundle();
-                result.putLong(RoutePlaybackControls.KEY_VALUE1, getCapabilities());
-                RouteInterfaceHandler.sendResult(cb, RouteInterface.RESULT_SUCCESS,
-                        result);
-                return true;
-            } else if (RoutePlaybackControls.CMD_PLAY_NOW.equals(method)) {
-                playNow(extras.getString(RoutePlaybackControls.KEY_VALUE1, null), cb);
-                return true;
-            } else if (RoutePlaybackControls.CMD_RESUME.equals(method)) {
-                boolean success = resume();
-                RouteInterfaceHandler.sendResult(cb, success
-                        ? RouteInterface.RESULT_SUCCESS
-                        : RouteInterface.RESULT_ERROR, null);
-                return true;
-            } else if (RoutePlaybackControls.CMD_PAUSE.equals(method)) {
-                boolean success = pause();
-                RouteInterfaceHandler.sendResult(cb, success
-                        ? RouteInterface.RESULT_SUCCESS
-                        : RouteInterface.RESULT_ERROR, null);
-                return true;
-            } else {
-                // The command wasn't recognized
-            }
-            return false;
-        }
-
-        /**
-         * Override to handle fast forwarding.
-         *
-         * @return true if the request succeeded, false otherwise
-         */
-        public boolean fastForward() {
-            Log.w(TAG, "fastForward is not supported.");
-            return false;
-        }
-
-        /**
-         * Override to handle getting the current position of playback in
-         * millis.
-         *
-         * @return The current position in millis or -1
-         */
-        public long getCurrentPosition() {
-            Log.w(TAG, "getCurrentPosition is not supported");
-            return -1;
-        }
-
-        /**
-         * Override to handle getting the set of capabilities currently
-         * available.
-         *
-         * @return A bit mask of the supported capabilities
-         */
-        public long getCapabilities() {
-            Log.w(TAG, "getCapabilities is not supported");
-            return 0;
-        }
-
-        /**
-         * Override to handle play now requests.
-         *
-         * @param content The uri of the item to play.
-         * @param cb The callback to send the result to.
-         */
-        public void playNow(String content, ResultReceiver cb) {
-            Log.w(TAG, "playNow is not supported");
-            if (cb != null) {
-                // We do this directly since we don't have a reference to the
-                // iface
-                cb.send(RouteInterface.RESULT_COMMAND_NOT_SUPPORTED, null);
-            }
-        }
-
-        /**
-         * Override to handle resume requests. Return true if the call was
-         * handled, even if it was a no-op.
-         *
-         * @return true if the call was handled.
-         */
-        public boolean resume() {
-            Log.w(TAG, "resume is not supported");
-            return false;
-        }
-
-        /**
-         * Override to handle pause requests. Return true if the call was
-         * handled, even if it was a no-op.
-         *
-         * @return true if the call was handled.
-         */
-        public boolean pause() {
-            Log.w(TAG, "pause is not supported");
-            return false;
-        }
-    }
-}
diff --git a/media/java/android/media/routeprovider/RouteProviderService.java b/media/java/android/media/routeprovider/RouteProviderService.java
deleted file mode 100644
index a6ef0bb..0000000
--- a/media/java/android/media/routeprovider/RouteProviderService.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.media.routeprovider;
-
-import android.app.Service;
-import android.content.Intent;
-import android.media.routeprovider.IRouteProvider;
-import android.media.routeprovider.IRouteProviderCallback;
-import android.media.session.RouteEvent;
-import android.media.session.RouteInfo;
-import android.media.session.RouteOptions;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Base class for defining a route provider service.
- * <p>
- * A route provider offers media routes which represent destinations to which
- * applications may connect, control, and send content. This provides a means
- * for Android applications to interact with a variety of media streaming
- * devices such as speakers or television sets.
- * <p>
- * The system will bind to your provider when an active app is interested in
- * routes that may be discovered through your provider. After binding, the
- * system will send updates on which routes to discover through
- * {@link #updateDiscoveryRequests(List)}. The system will call
- * {@link #getMatchingRoutes(List)} with a subset of filters when a route is
- * needed for a specific app.
- * <p>
- * TODO add documentation for how the sytem knows an app is interested. Maybe
- * interface declarations in the manifest.
- * <p>
- * The system will only start a provider when an app may discover routes through
- * it. If your service needs to run at other times you are responsible for
- * managing its lifecycle.
- * <p>
- * Declare your route provider service in your application manifest like this:
- * <p>
- *
- * <pre>
- *   &lt;service android:name=".MyRouteProviderService"
- *           android:label="@string/my_route_provider_service">
- *       &lt;intent-filter>
- *           &lt;action android:name="com.android.media.session.MediaRouteProvider" />
- *       &lt;/intent-filter>
- *   &lt;/service>
- * </pre>
- * @hide
- */
-public abstract class RouteProviderService extends Service {
-    private static final String TAG = "RouteProvider";
-    /**
-     * A service that implements a RouteProvider must declare that it handles
-     * this action in its AndroidManifest.
-     */
-    public static final String SERVICE_INTERFACE =
-            "com.android.media.session.MediaRouteProvider";
-
-    /**
-     * @hide
-     */
-    public static final String KEY_ROUTES = "routes";
-    /**
-     * @hide
-     */
-    public static final String KEY_CONNECTION = "connection";
-    /**
-     * @hide
-     */
-    public static final int RESULT_FAILURE = -1;
-    /**
-     * @hide
-     */
-    public static final int RESULT_SUCCESS = 0;
-
-    // The system's callback once it has bound to the service
-    private IRouteProviderCallback mCb;
-
-    /**
-     * If your service overrides onBind it must return super.onBind() in
-     * response to the {@link #SERVICE_INTERFACE} action.
-     */
-    @Override
-    public IBinder onBind(Intent intent) {
-        if (intent != null && RouteProviderService.SERVICE_INTERFACE.equals(intent.getAction())) {
-            return mBinder;
-        }
-        return null;
-    }
-
-    /**
-     * Disconnect the specified RouteConnection. The system will stop sending
-     * commands to this connection.
-     *
-     * @param connection The connection to disconnect.
-     * @hide
-     */
-    public final void disconnect(RouteConnection connection) {
-        if (mCb != null) {
-            try {
-                mCb.onConnectionTerminated(connection.getBinder());
-            } catch (RemoteException e) {
-                Log.wtf(TAG, "Error in disconnect.", e);
-            }
-        }
-    }
-
-    /**
-     * @hide
-     */
-    public final void sendRouteEvent(RouteEvent event) {
-        if (mCb != null) {
-            try {
-                mCb.onRouteEvent(event);
-            } catch (RemoteException e) {
-                Log.wtf(TAG, "Unable to send MediaRouteEvent to system", e);
-            }
-        }
-    }
-
-    /**
-     * Override to handle updates to the routes that are of interest. Each
-     * {@link RouteRequest} will specify if it is an active or passive request.
-     * Route discovery may perform more aggressive discovery on behalf of active
-     * requests but should use low power discovery methods otherwise.
-     * <p>
-     * A single app may have more than one request. Your provider is responsible
-     * for deciding the set of features that are important for discovery given
-     * the set of requests. If your provider only has one method of discovery it
-     * may simply verify that one or more requests are valid before starting
-     * discovery.
-     *
-     * @param requests The route requests that are currently relevant.
-     */
-    public void updateDiscoveryRequests(List<RouteRequest> requests) {
-    }
-
-    /**
-     * Return a list of matching routes for the given set of requests. Returning
-     * null or an empty list indicates there are no matches. A route is
-     * considered matching if it supports one or more of the
-     * {@link RouteOptions} specified. Each returned {@link RouteInfo}
-     * should include all the requested connections that it supports.
-     *
-     * @param options The set of requests for routes
-     * @return The routes that this caller may connect to using one or more of
-     *         the route options.
-     */
-    public abstract List<RouteInfo> getMatchingRoutes(List<RouteRequest> options);
-
-    /**
-     * Handle a request to connect to a specific route with a specific request.
-     * The {@link RouteConnection} must be fully defined before being returned,
-     * though the actual connection to the route may be performed in the
-     * background.
-     *
-     * @param route The route to connect to
-     * @param request The connection request parameters
-     * @return A MediaRouteConnection representing the connection to the route
-     */
-    public abstract RouteConnection connect(RouteInfo route, RouteRequest request);
-
-    private IRouteProvider.Stub mBinder = new IRouteProvider.Stub() {
-
-        @Override
-        public void registerCallback(IRouteProviderCallback cb) throws RemoteException {
-            mCb = cb;
-        }
-
-        @Override
-        public void unregisterCallback(IRouteProviderCallback cb) throws RemoteException {
-            mCb = null;
-        }
-
-        @Override
-        public void updateDiscoveryRequests(List<RouteRequest> requests)
-                throws RemoteException {
-            RouteProviderService.this.updateDiscoveryRequests(requests);
-        }
-
-        @Override
-        public void getAvailableRoutes(List<RouteRequest> requests, ResultReceiver cb)
-                throws RemoteException {
-            List<RouteInfo> routes = RouteProviderService.this.getMatchingRoutes(requests);
-            ArrayList<RouteInfo> routesArray;
-            if (routes instanceof ArrayList) {
-                routesArray = (ArrayList<RouteInfo>) routes;
-            } else {
-                routesArray = new ArrayList<RouteInfo>(routes);
-            }
-            Bundle resultData = new Bundle();
-            resultData.putParcelableArrayList(KEY_ROUTES, routesArray);
-            cb.send(routes == null ? RESULT_FAILURE : RESULT_SUCCESS, resultData);
-        }
-
-        @Override
-        public void connect(RouteInfo route, RouteRequest request, ResultReceiver cb)
-                throws RemoteException {
-            RouteConnection connection = RouteProviderService.this.connect(route, request);
-            Bundle resultData = new Bundle();
-            if (connection != null) {
-                connection.publish();
-                resultData.putBinder(KEY_CONNECTION, connection.getBinder());
-            }
-
-            cb.send(connection == null ? RESULT_FAILURE : RESULT_SUCCESS, resultData);
-        }
-    };
-}
diff --git a/media/java/android/media/routeprovider/RouteRequest.aidl b/media/java/android/media/routeprovider/RouteRequest.aidl
deleted file mode 100644
index 7bc5722..0000000
--- a/media/java/android/media/routeprovider/RouteRequest.aidl
+++ /dev/null
@@ -1,18 +0,0 @@
-/* Copyright 2014, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-package android.media.routeprovider;
-
-parcelable RouteRequest;
diff --git a/media/java/android/media/routeprovider/RouteRequest.java b/media/java/android/media/routeprovider/RouteRequest.java
deleted file mode 100644
index 2ba75de..0000000
--- a/media/java/android/media/routeprovider/RouteRequest.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.media.routeprovider;
-
-import android.media.session.RouteOptions;
-import android.media.session.MediaSessionInfo;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.io.PrintWriter;
-
-/**
- * A request to connect or discover routes with certain capabilities.
- * <p>
- * Passed to a {@link RouteProviderService} when a request for discovery or to
- * connect to a route is made. This identifies the app making the request and
- * provides the full set of connection parameters they would like to use for a
- * connection. An app that can connect in multiple ways will be represented by
- * multiple requests.
- * @hide
- */
-public final class RouteRequest implements Parcelable {
-    private final MediaSessionInfo mSessionInfo;
-    private final RouteOptions mOptions;
-    private final boolean mActive;
-
-    /**
-     * @hide
-     */
-    public RouteRequest(MediaSessionInfo info, RouteOptions connRequest,
-            boolean active) {
-        mSessionInfo = info;
-        mOptions = connRequest;
-        mActive = active;
-    }
-
-    private RouteRequest(Parcel in) {
-        mSessionInfo = MediaSessionInfo.CREATOR.createFromParcel(in);
-        mOptions = RouteOptions.CREATOR.createFromParcel(in);
-        mActive = in.readInt() != 0;
-    }
-
-    /**
-     * Get information about the session making the request.
-     *
-     * @return Info on the session making the request
-     */
-    public MediaSessionInfo getSessionInfo() {
-        return mSessionInfo;
-    }
-
-    /**
-     * Get the connection options, which includes the interfaces and other
-     * connection params the session wants to use with a route.
-     *
-     * @return The connection options
-     */
-    public RouteOptions getConnectionOptions() {
-        return mOptions;
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder bob = new StringBuilder();
-        bob.append("RouteRequest {");
-        bob.append("active=").append(mActive);
-        bob.append(", info=").append(mSessionInfo.toString());
-        bob.append(", options=").append(mOptions.toString());
-        bob.append("}");
-        return bob.toString();
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        mSessionInfo.writeToParcel(dest, flags);
-        mOptions.writeToParcel(dest, flags);
-        dest.writeInt(mActive ? 1 : 0);
-    }
-
-    public static final Parcelable.Creator<RouteRequest> CREATOR
-            = new Parcelable.Creator<RouteRequest>() {
-        @Override
-        public RouteRequest createFromParcel(Parcel in) {
-            return new RouteRequest(in);
-        }
-
-        @Override
-        public RouteRequest[] newArray(int size) {
-            return new RouteRequest[size];
-        }
-    };
-}
diff --git a/media/java/android/media/routing/IMediaRouteClientCallback.aidl b/media/java/android/media/routing/IMediaRouteClientCallback.aidl
new file mode 100644
index 0000000..d90ea3b
--- /dev/null
+++ b/media/java/android/media/routing/IMediaRouteClientCallback.aidl
@@ -0,0 +1,41 @@
+/* Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.routing;
+
+import android.media.routing.MediaRouteSelector;
+import android.media.routing.ParcelableConnectionInfo;
+import android.media.routing.ParcelableDestinationInfo;
+import android.media.routing.ParcelableRouteInfo;
+import android.os.IBinder;
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+oneway interface IMediaRouteClientCallback {
+    void onDestinationFound(int seq, in ParcelableDestinationInfo destination,
+            in ParcelableRouteInfo[] routes);
+
+    void onDestinationLost(int seq, String id);
+
+    void onDiscoveryFailed(int seq, int error, in CharSequence message, in Bundle extras);
+
+    void onConnected(int seq, in ParcelableConnectionInfo connection);
+
+    void onDisconnected(int seq);
+
+    void onConnectionFailed(int seq, int error, in CharSequence message, in Bundle extras);
+}
diff --git a/media/java/android/media/routing/IMediaRouteService.aidl b/media/java/android/media/routing/IMediaRouteService.aidl
new file mode 100644
index 0000000..493ab6d
--- /dev/null
+++ b/media/java/android/media/routing/IMediaRouteService.aidl
@@ -0,0 +1,46 @@
+/* Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.routing;
+
+import android.media.routing.IMediaRouteClientCallback;
+import android.media.routing.MediaRouteSelector;
+import android.os.Bundle;
+
+/**
+ * Interface to an app's MediaRouteService.
+ * @hide
+ */
+oneway interface IMediaRouteService {
+    void registerClient(int clientUid, String clientPackageName,
+            in IMediaRouteClientCallback callback);
+
+    void unregisterClient(in IMediaRouteClientCallback callback);
+
+    void startDiscovery(in IMediaRouteClientCallback callback, int seq,
+            in List<MediaRouteSelector> selectors, int flags);
+
+    void stopDiscovery(in IMediaRouteClientCallback callback);
+
+    void connect(in IMediaRouteClientCallback callback, int seq,
+            String destinationId, String routeId, int flags, in Bundle extras);
+
+    void disconnect(in IMediaRouteClientCallback callback);
+
+    void pauseStream(in IMediaRouteClientCallback callback);
+
+    void resumeStream(in IMediaRouteClientCallback callback);
+}
+
diff --git a/telecomm/java/android/telecomm/CallServiceDescriptor.aidl b/media/java/android/media/routing/IMediaRouter.aidl
similarity index 73%
copy from telecomm/java/android/telecomm/CallServiceDescriptor.aidl
copy to media/java/android/media/routing/IMediaRouter.aidl
index f517c73..0abb258 100644
--- a/telecomm/java/android/telecomm/CallServiceDescriptor.aidl
+++ b/media/java/android/media/routing/IMediaRouter.aidl
@@ -1,11 +1,10 @@
-/*
- * Copyright 2014, The Android Open Source Project
+/* Copyright (C) 2014 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      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,
@@ -14,6 +13,10 @@
  * limitations under the License.
  */
 
-package android.telecomm;
+package android.media.routing;
 
-parcelable CallServiceDescriptor;
+/** @hide */
+interface IMediaRouter {
+
+}
+
diff --git a/telecomm/java/android/telecomm/CallServiceDescriptor.aidl b/media/java/android/media/routing/IMediaRouterDelegate.aidl
similarity index 72%
copy from telecomm/java/android/telecomm/CallServiceDescriptor.aidl
copy to media/java/android/media/routing/IMediaRouterDelegate.aidl
index f517c73..35f84c8 100644
--- a/telecomm/java/android/telecomm/CallServiceDescriptor.aidl
+++ b/media/java/android/media/routing/IMediaRouterDelegate.aidl
@@ -1,11 +1,10 @@
-/*
- * Copyright 2014, The Android Open Source Project
+/* Copyright (C) 2014 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      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,
@@ -14,6 +13,10 @@
  * limitations under the License.
  */
 
-package android.telecomm;
+package android.media.routing;
 
-parcelable CallServiceDescriptor;
+/** @hide */
+interface IMediaRouterDelegate {
+
+}
+
diff --git a/telecomm/java/android/telecomm/CallServiceDescriptor.aidl b/media/java/android/media/routing/IMediaRouterRoutingCallback.aidl
similarity index 72%
copy from telecomm/java/android/telecomm/CallServiceDescriptor.aidl
copy to media/java/android/media/routing/IMediaRouterRoutingCallback.aidl
index f517c73..173ae55 100644
--- a/telecomm/java/android/telecomm/CallServiceDescriptor.aidl
+++ b/media/java/android/media/routing/IMediaRouterRoutingCallback.aidl
@@ -1,11 +1,10 @@
-/*
- * Copyright 2014, The Android Open Source Project
+/* Copyright (C) 2014 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      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,
@@ -14,6 +13,10 @@
  * limitations under the License.
  */
 
-package android.telecomm;
+package android.media.routing;
 
-parcelable CallServiceDescriptor;
+/** @hide */
+interface IMediaRouterRoutingCallback {
+
+}
+
diff --git a/telecomm/java/android/telecomm/CallServiceDescriptor.aidl b/media/java/android/media/routing/IMediaRouterStateCallback.aidl
similarity index 72%
copy from telecomm/java/android/telecomm/CallServiceDescriptor.aidl
copy to media/java/android/media/routing/IMediaRouterStateCallback.aidl
index f517c73..0299904 100644
--- a/telecomm/java/android/telecomm/CallServiceDescriptor.aidl
+++ b/media/java/android/media/routing/IMediaRouterStateCallback.aidl
@@ -1,11 +1,10 @@
-/*
- * Copyright 2014, The Android Open Source Project
+/* Copyright (C) 2014 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      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,
@@ -14,6 +13,10 @@
  * limitations under the License.
  */
 
-package android.telecomm;
+package android.media.routing;
 
-parcelable CallServiceDescriptor;
+/** @hide */
+interface IMediaRouterStateCallback {
+
+}
+
diff --git a/media/java/android/media/session/RouteEvent.aidl b/media/java/android/media/routing/MediaRouteSelector.aidl
similarity index 90%
copy from media/java/android/media/session/RouteEvent.aidl
copy to media/java/android/media/routing/MediaRouteSelector.aidl
index 6966207..37bfa4a 100644
--- a/media/java/android/media/session/RouteEvent.aidl
+++ b/media/java/android/media/routing/MediaRouteSelector.aidl
@@ -13,6 +13,6 @@
 ** limitations under the License.
 */
 
-package android.media.session;
+package android.media.routing;
 
-parcelable RouteEvent;
+parcelable MediaRouteSelector;
diff --git a/media/java/android/media/routing/MediaRouteSelector.java b/media/java/android/media/routing/MediaRouteSelector.java
new file mode 100644
index 0000000..26a9b1c
--- /dev/null
+++ b/media/java/android/media/routing/MediaRouteSelector.java
@@ -0,0 +1,357 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.routing;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.media.routing.MediaRouter.RouteFeatures;
+import android.os.Bundle;
+import android.os.IInterface;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A media route selector consists of a set of constraints that are used to select
+ * the routes to which an application would like to connect.  The constraints consist
+ * of a set of required or optional features and protocols.  The constraints may also
+ * require the use of a specific media route service package or additional characteristics
+ * that are described by a bundle of extra parameters.
+ * <p>
+ * The application will typically create several different selectors that express
+ * various combinations of characteristics that it would like to use together when
+ * it connects to a destination media device.  For each destination that is discovered,
+ * media route services will publish some number of routes and include information
+ * about which selector each route matches.  The application will then choose among
+ * these routes to determine which best satisfies its desired purpose and connect to it.
+ * </p>
+ */
+public final class MediaRouteSelector implements Parcelable {
+    private final int mRequiredFeatures;
+    private final int mOptionalFeatures;
+    private final List<String> mRequiredProtocols;
+    private final List<String> mOptionalProtocols;
+    private final String mServicePackageName;
+    private final Bundle mExtras;
+
+    MediaRouteSelector(int requiredFeatures, int optionalFeatures,
+            List<String> requiredProtocols, List<String> optionalProtocols,
+            String servicePackageName, Bundle extras) {
+        mRequiredFeatures = requiredFeatures;
+        mOptionalFeatures = optionalFeatures;
+        mRequiredProtocols = requiredProtocols;
+        mOptionalProtocols = optionalProtocols;
+        mServicePackageName = servicePackageName;
+        mExtras = extras;
+    }
+
+    /**
+     * Gets the set of required route features.
+     *
+     * @return A set of required route feature flags.
+     */
+    public @RouteFeatures int getRequiredFeatures() {
+        return mRequiredFeatures;
+    }
+
+    /**
+     * Gets the set of optional route features.
+     *
+     * @return A set of optional route feature flags.
+     */
+    public @RouteFeatures int getOptionalFeatures() {
+        return mOptionalFeatures;
+    }
+
+    /**
+     * Gets the list of route protocols that a route must support in order to be selected.
+     * <p>
+     * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
+     * for more information.
+     * </p>
+     *
+     * @return The list of fully qualified route protocol names.
+     */
+    public @NonNull List<String> getRequiredProtocols() {
+        return mRequiredProtocols;
+    }
+
+    /**
+     * Gets the list of optional route protocols that a client may use if they are available.
+     * <p>
+     * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
+     * for more information.
+     * </p>
+     *
+     * @return The list of optional fully qualified route protocol names.
+     */
+    public @NonNull List<String> getOptionalProtocols() {
+        return mOptionalProtocols;
+    }
+
+    /**
+     * Returns true if the selector includes a required or optional request for
+     * the specified protocol using its fully qualified class name.
+     * <p>
+     * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
+     * for more information.
+     * </p>
+     *
+     * @param clazz The protocol class.
+     * @return True if the protocol was requested.
+     */
+    public boolean containsProtocol(@NonNull Class<?> clazz) {
+        return containsProtocol(clazz.getName());
+    }
+
+    /**
+     * Returns true if the selector includes a required or optional request for
+     * the specified protocol.
+     * <p>
+     * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
+     * for more information.
+     * </p>
+     *
+     * @param name The name of the protocol.
+     * @return True if the protocol was requested.
+     */
+    public boolean containsProtocol(@NonNull String name) {
+        return mRequiredProtocols.contains(name)
+                || mOptionalProtocols.contains(name);
+    }
+
+    /**
+     * Gets the package name of a specific media route service that this route selector
+     * requires.
+     *
+     * @return The required media route service package name, or null if none.
+     */
+    public @Nullable String getServicePackageName() {
+        return mServicePackageName;
+    }
+
+    /**
+     * Gets optional extras that may be used to select or configure routes for a
+     * particular purpose.  Some extras may be used by media route services to apply
+     * additional constraints or parameters for the routes to be discovered.
+     *
+     * @return The optional extras, or null if none.
+     */
+    public @Nullable Bundle getExtras() {
+        return mExtras;
+    }
+
+    @Override
+    public String toString() {
+        return "MediaRouteSelector{ "
+                + ", requiredFeatures=0x" + Integer.toHexString(mRequiredFeatures)
+                + ", optionalFeatures=0x" + Integer.toHexString(mOptionalFeatures)
+                + ", requiredProtocols=" + mRequiredProtocols
+                + ", optionalProtocols=" + mOptionalProtocols
+                + ", servicePackageName=" + mServicePackageName
+                + ", extras=" + mExtras + " }";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mRequiredFeatures);
+        dest.writeInt(mOptionalFeatures);
+        dest.writeStringList(mRequiredProtocols);
+        dest.writeStringList(mOptionalProtocols);
+        dest.writeString(mServicePackageName);
+        dest.writeBundle(mExtras);
+    }
+
+    public static final Parcelable.Creator<MediaRouteSelector> CREATOR =
+            new Parcelable.Creator<MediaRouteSelector>() {
+        @Override
+        public MediaRouteSelector createFromParcel(Parcel source) {
+            int requiredFeatures = source.readInt();
+            int optionalFeatures = source.readInt();
+            ArrayList<String> requiredProtocols = new ArrayList<String>();
+            ArrayList<String> optionalProtocols = new ArrayList<String>();
+            source.readStringList(requiredProtocols);
+            source.readStringList(optionalProtocols);
+            return new MediaRouteSelector(requiredFeatures, optionalFeatures,
+                    requiredProtocols, optionalProtocols,
+                    source.readString(), source.readBundle());
+        }
+
+        @Override
+        public MediaRouteSelector[] newArray(int size) {
+            return new MediaRouteSelector[size];
+        }
+    };
+
+    /**
+     * Builder for {@link MediaRouteSelector} objects.
+     */
+    public static final class Builder {
+        private int mRequiredFeatures;
+        private int mOptionalFeatures;
+        private final ArrayList<String> mRequiredProtocols = new ArrayList<String>();
+        private final ArrayList<String> mOptionalProtocols = new ArrayList<String>();
+        private String mServicePackageName;
+        private Bundle mExtras;
+
+        /**
+         * Creates an initially empty selector builder.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Sets the set of required route features.
+         *
+         * @param features A set of required route feature flags.
+         */
+        public @NonNull Builder setRequiredFeatures(@RouteFeatures int features) {
+            mRequiredFeatures = features;
+            return this;
+        }
+
+        /**
+         * Sets the set of optional route features.
+         *
+         * @param features A set of optional route feature flags.
+         */
+        public @NonNull Builder setOptionalFeatures(@RouteFeatures int features) {
+            mOptionalFeatures = features;
+            return this;
+        }
+
+        /**
+         * Adds a route protocol that a route must support in order to be selected
+         * using its fully qualified class name.
+         * <p>
+         * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
+         * for more information.
+         * </p>
+         *
+         * @param clazz The protocol class.
+         * @return this
+         */
+        public @NonNull Builder addRequiredProtocol(@NonNull Class<?> clazz) {
+            if (clazz == null) {
+                throw new IllegalArgumentException("clazz must not be null");
+            }
+            return addRequiredProtocol(clazz.getName());
+        }
+
+        /**
+         * Adds a route protocol that a route must support in order to be selected.
+         * <p>
+         * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
+         * for more information.
+         * </p>
+         *
+         * @param name The fully qualified name of the required protocol.
+         * @return this
+         */
+        public @NonNull Builder addRequiredProtocol(@NonNull String name) {
+            if (TextUtils.isEmpty(name)) {
+                throw new IllegalArgumentException("name must not be null or empty");
+            }
+            mRequiredProtocols.add(name);
+            return this;
+        }
+
+        /**
+         * Adds an optional route protocol that a client may use if available
+         * using its fully qualified class name.
+         * <p>
+         * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
+         * for more information.
+         * </p>
+         *
+         * @param clazz The protocol class.
+         * @return this
+         */
+        public @NonNull Builder addOptionalProtocol(@NonNull Class<?> clazz) {
+            if (clazz == null) {
+                throw new IllegalArgumentException("clazz must not be null");
+            }
+            return addOptionalProtocol(clazz.getName());
+        }
+
+        /**
+         * Adds an optional route protocol that a client may use if available.
+         * <p>
+         * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
+         * for more information.
+         * </p>
+         *
+         * @param name The fully qualified name of the optional protocol.
+         * @return this
+         */
+        public @NonNull Builder addOptionalProtocol(@NonNull String name) {
+            if (TextUtils.isEmpty(name)) {
+                throw new IllegalArgumentException("name must not be null or empty");
+            }
+            mOptionalProtocols.add(name);
+            return this;
+        }
+
+        /**
+         * Sets the package name of the media route service to which this selector
+         * appertains.
+         * <p>
+         * If a package name is specified here then this selector will only be
+         * passed to media route services from that package.  This has the effect
+         * of restricting the set of matching routes to just those that are offered
+         * by that package.
+         * </p>
+         *
+         * @param packageName The required service package name, or null if none.
+         * @return this
+         */
+        public @NonNull Builder setServicePackageName(@Nullable String packageName) {
+            mServicePackageName = packageName;
+            return this;
+        }
+
+        /**
+         * Sets optional extras that may be used to select or configure routes for a
+         * particular purpose.  Some extras may be used by route services to specify
+         * additional constraints or parameters for the routes to be discovered.
+         *
+         * @param extras The optional extras, or null if none.
+         * @return this
+         */
+        public @NonNull Builder setExtras(@Nullable Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        /**
+         * Builds the {@link MediaRouteSelector} object.
+         *
+         * @return The new media route selector instance.
+         */
+        public @NonNull MediaRouteSelector build() {
+            return new MediaRouteSelector(mRequiredFeatures, mOptionalFeatures,
+                    mRequiredProtocols, mOptionalProtocols, mServicePackageName, mExtras);
+        }
+    }
+}
diff --git a/media/java/android/media/routing/MediaRouteService.java b/media/java/android/media/routing/MediaRouteService.java
new file mode 100644
index 0000000..4d5a8a9
--- /dev/null
+++ b/media/java/android/media/routing/MediaRouteService.java
@@ -0,0 +1,1023 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.routing;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.app.Service;
+import android.content.Intent;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.media.routing.MediaRouter.ConnectionError;
+import android.media.routing.MediaRouter.ConnectionInfo;
+import android.media.routing.MediaRouter.ConnectionRequest;
+import android.media.routing.MediaRouter.DestinationInfo;
+import android.media.routing.MediaRouter.DiscoveryError;
+import android.media.routing.MediaRouter.DiscoveryRequest;
+import android.media.routing.MediaRouter.RouteInfo;
+import android.media.routing.MediaRouter.ServiceMetadata;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Media route services implement strategies for discovering
+ * and establishing connections to media devices and their routes.  These services
+ * are also known as media route providers.
+ * <p>
+ * Each media route service subclass is responsible for enabling applications
+ * and the system to interact with media devices of some kind.
+ * For example, one particular media route service implementation might
+ * offer support for discovering nearby wireless display devices and streaming
+ * video contents to them; another media route service implementation might
+ * offer support for discovering nearby speakers and streaming media appliances
+ * and sending commands to play content on request.
+ * </p><p>
+ * Subclasses must override the {@link #onCreateClientSession} method to return
+ * a {@link ClientSession} object that implements the {@link ClientSession#onStartDiscovery},
+ * {@link ClientSession#onStopDiscovery}, and {@link ClientSession#onConnect} methods
+ * to allow clients to discover and connect to media devices.
+ * </p><p>
+ * This object is not thread-safe.  All callbacks are invoked on the main looper.
+ * </p>
+ *
+ * <h3>Clients</h3>
+ * <p>
+ * The clients of this API are media applications that would like to discover
+ * and connect to media devices.  The client may also be the system, such as
+ * when the user initiates display mirroring via the Cast Screen function.
+ * </p><p>
+ * There may be multiple client sessions active at the same time.  Each client
+ * session can request discovery and connect to routes independently of any
+ * other client.  It is the responsibility of the media route service to maintain
+ * separate state for each client session and to ensure that clients cannot interfere
+ * with one another in harmful ways.
+ * </p><p>
+ * Notwithstanding the requirement to support any number of concurrent client
+ * sessions, the media route service may impose constraints on how many clients
+ * can connect to the same media device in a particular mode at the same time.
+ * In some cases, media devices may support connections from an arbitrary number
+ * of clients simultaneously but often it may be necessary to ensure that only
+ * one client is in control.  When this happens, the media route service should
+ * report a connection error unless the connection request specifies that the
+ * client should take control of the media device (and forcibly disconnect other
+ * clients that may be using it).
+ * </p>
+ *
+ * <h3>Destinations</h3>
+ * <p>
+ * The media devices to which an application may send media content are referred
+ * to in the API as destinations.  Each destination therefore represents a single
+ * independent device such as a speaker or TV set.  Destinations are given meaningful
+ * names and descriptions to help the user associate them with devices in their
+ * environment.
+ * </p><p>
+ * Destinations may be local or remote and may be accessed through various means,
+ * often wirelessly.  The user may install media route services to enable
+ * media applications to connect to a variety of destinations with different
+ * capabilities.
+ * </p>
+ *
+ * <h3>Routes</h3>
+ * <p>
+ * Routes represent possible usages or means of reaching and interacting with
+ * a destination.  Since destinations may support many different features, they may
+ * each offer multiple routes for applications to choose from based on their needs.
+ * For example, one route might express the ability to stream locally rendered audio
+ * and video to the device; another route might express the ability to send a URL for
+ * the destination to download from the network and play all by itself.
+ * </p><p>
+ * Routes are discovered according to the set of capabilities that
+ * an application or the system is seeking to use at a particular time.  For example,
+ * if an application wants to stream music to a destination then it will ask the
+ * {@link MediaRouter} to find routes to destinations can stream music and ignore
+ * all other destinations that cannot.
+ * </p><p>
+ * In general, the application will inspect the set of routes that have been
+ * offered then connect to the most appropriate route for its desired purpose.
+ * </p>
+ *
+ * <h3>Discovery</h3>
+ * <p>
+ * Discovery is the process of finding destinations based on a description of the
+ * kinds of routes that an application or the system would like to use.
+ * </p><p>
+ * Discovery begins when {@link ClientSession#onStartDiscovery} is called and ends when
+ * {@link ClientSession#onStopDiscovery} is called.  There may be multiple simultaneous
+ * discovery requests in progress at the same time from different clients.  It is up to
+ * the media route service to perform these requests in parallel or multiplex them
+ * as required.
+ * </p><p>
+ * Media route services are <em>strongly encouraged</em> to use the information
+ * in the discovery request to optimize discovery and avoid redundant work.
+ * In the case where no media device supported by the media route service
+ * could possibly offer the requested capabilities, the
+ * {@link ClientSession#onStartDiscovery} method should return <code>false</code> to
+ * let the system know that it can unbind from the media route service and
+ * release its resources.
+ * </p>
+ *
+ * <h3>Settings</h3>
+ * <p>
+ * Many kinds of devices can be discovered on demand simply by scanning the local network
+ * or using wireless protocols such as Bluetooth to find them.  However, in some cases
+ * it may be necessary for the user to manually configure destinations before they
+ * can be used (or to adjust settings later).  Actual user configuration of destinations
+ * is beyond the scope of this API but media route services may specify an activity
+ * in their manifest that the user can launch to perform these tasks.
+ * </p><p>
+ * Note that media route services that are installed from the store must be enabled
+ * by the user before they become available for applications to use.
+ * The {@link android.provider.Settings#ACTION_CAST_SETTINGS Settings.ACTION_CAST_SETTINGS}
+ * settings activity provides the ability for the user to configure media route services.
+ * </p>
+ *
+ * <h3>Manifest Declaration</h3>
+ * <p>
+ * Media route services must be declared in the manifest along with meta-data
+ * about the kinds of routes that they are capable of discovering.  The system
+ * uses this information to optimize the set of services to which it binds in
+ * order to satisfy a particular discovery request.
+ * </p><p>
+ * To extend this class, you must declare the service in your manifest file with
+ * the {@link android.Manifest.permission#BIND_MEDIA_ROUTE_SERVICE} permission
+ * and include an intent filter with the {@link #SERVICE_INTERFACE} action.  You must
+ * also add meta-data to describe the kinds of routes that your service is capable
+ * of discovering.
+ * </p><p>
+ * For example:
+ * </p><pre>
+ * &lt;service android:name=".MediaRouteProvider"
+ *          android:label="&#64;string/service_name"
+ *          android:permission="android.permission.BIND_MEDIA_ROUTE_SERVICE">
+ *     &lt;intent-filter>
+ *         &lt;action android:name="android.media.routing.MediaRouteService" />
+ *     &lt;/intent-filter>
+ *
+ *     TODO: INSERT METADATA DECLARATIONS HERE
+ *
+ * &lt;/service>
+ * </pre>
+ */
+public abstract class MediaRouteService extends Service {
+    private static final String TAG = "MediaRouteService";
+
+    private static final boolean DEBUG = true;
+
+    private final Handler mHandler;
+    private final BinderService mService;
+    private final ArrayMap<IBinder, ClientRecord> mClientRecords =
+            new ArrayMap<IBinder, ClientRecord>();
+
+    private ServiceMetadata mMetadata;
+
+    /**
+     * The {@link Intent} that must be declared as handled by the service.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE =
+            "android.media.routing.MediaRouteService";
+
+    /**
+     * Creates a media route service.
+     */
+    public MediaRouteService() {
+        mHandler = new Handler(true);
+        mService = new BinderService();
+    }
+
+    @Override
+    public @Nullable IBinder onBind(Intent intent) {
+        if (SERVICE_INTERFACE.equals(intent.getAction())) {
+            return mService;
+        }
+        return null;
+    }
+
+    /**
+     * Creates a new client session on behalf of a client.
+     * <p>
+     * The implementation should return a {@link ClientSession} for the client
+     * to use.  The media route service must take care to manage the state of
+     * each client session independently from any others that might also be
+     * in use at the same time.
+     * </p>
+     *
+     * @param client Information about the client.
+     * @return The client session object, or null if the client is not allowed
+     * to interact with this media route service.
+     */
+    public abstract @Nullable ClientSession onCreateClientSession(@NonNull ClientInfo client);
+
+    /**
+     * Gets metadata about this service.
+     * <p>
+     * Use this method to obtain a {@link ServiceMetadata} object to provide when creating
+     * a {@link android.media.routing.MediaRouter.DestinationInfo.Builder}.
+     * </p>
+     *
+     * @return Metadata about this service.
+     */
+    public @NonNull ServiceMetadata getServiceMetadata() {
+        if (mMetadata == null) {
+            try {
+                mMetadata = new ServiceMetadata(this);
+            } catch (NameNotFoundException ex) {
+                Log.wtf(TAG, "Could not retrieve own service metadata!");
+            }
+        }
+        return mMetadata;
+    }
+
+    /**
+     * Enables a single client to access the functionality of the media route service.
+     */
+    public static abstract class ClientSession {
+        /**
+         * Starts discovery.
+         * <p>
+         * If the media route service is capable of discovering routes that satisfy
+         * the request then this method should start discovery and return true.
+         * Otherwise, this method should return false.  If false is returned,
+         * then the framework will not call {@link #onStopDiscovery} since discovery
+         * was never actually started.
+         * </p><p>
+         * There may already be other discovery requests in progress at the same time
+         * for other clients; the media route service must keep track of them all.
+         * </p>
+         *
+         * @param req The discovery request to start.
+         * @param callback A callback to receive discovery events related to this
+         * particular request.  The events that the service sends to this callback
+         * will be sent to the client that initiated the discovery request.
+         * @return True if discovery has started.  False if the media route service
+         * is unable to discover routes that satisfy the request.
+         */
+        public abstract boolean onStartDiscovery(@NonNull DiscoveryRequest req,
+                @NonNull DiscoveryCallback callback);
+
+        /**
+         * Stops discovery.
+         * <p>
+         * If {@link #onStartDiscovery} returned true, then this method will eventually
+         * be called when the framework no longer requires this discovery request
+         * to be performed.
+         * </p><p>
+         * There may still be other discovery requests in progress for other clients;
+         * they must keep working until they have each been stopped by their client.
+         * </p>
+         */
+        public abstract void onStopDiscovery();
+
+        /**
+         * Starts connecting to a route.
+         *
+         * @param req The connection request.
+         * @param callback A callback to receive events connection events related
+         * to this particular request.  The events that the service sends to this callback
+         * will be sent to the client that initiated the discovery request.
+         * @return True if the connection is in progress, or false if the client
+         * unable to connect to the requested route.
+         */
+        public abstract boolean onConnect(@NonNull ConnectionRequest req,
+                @NonNull ConnectionCallback callback);
+
+        /**
+         * Called when the client requests to disconnect from the route
+         * or abort a connection attempt in progress.
+         */
+        public abstract void onDisconnect();
+
+        /**
+         * Called when the client requests to pause streaming of content to
+         * live audio/video routes such as when it goes into the background.
+         * <p>
+         * The default implementation does nothing.
+         * </p>
+         */
+        public void onPauseStream() { }
+
+        /**
+         * Called when the application requests to resume streaming of content to
+         * live audio/video routes such as when it returns to the foreground.
+         * <p>
+         * The default implementation does nothing.
+         * </p>
+         */
+        public void onResumeStream() { }
+
+        /**
+         * Called when the client is releasing the session.
+         * <p>
+         * The framework automatically takes care of stopping discovery and
+         * terminating the connection politely before calling this method to release
+         * the session.
+         * </p><p>
+         * The default implementation does nothing.
+         * </p>
+         */
+        public void onRelease() { }
+    }
+
+    /**
+     * Provides events in response to a discovery request.
+     */
+    public final class DiscoveryCallback {
+        private final ClientRecord mRecord;
+
+        DiscoveryCallback(ClientRecord record) {
+            mRecord = record;
+        }
+
+        /**
+         * Called by the service when a destination is found that
+         * offers one or more routes that satisfy the discovery request.
+         * <p>
+         * This method should be called whenever the list of available routes
+         * at a destination changes or whenever the properties of the destination
+         * itself change.
+         * </p>
+         *
+         * @param destination The destination that was found.
+         * @param routes The list of that destination's routes that satisfy the
+         * discovery request.
+         */
+        public void onDestinationFound(final @NonNull DestinationInfo destination,
+                final @NonNull List<RouteInfo> routes) {
+            if (destination == null) {
+                throw new IllegalArgumentException("destination must not be null");
+            }
+            if (routes == null) {
+                throw new IllegalArgumentException("routes must not be null");
+            }
+            for (int i = 0; i < routes.size(); i++) {
+                if (routes.get(i).getDestination() != destination) {
+                    throw new IllegalArgumentException("routes must refer to the "
+                            + "destination");
+                }
+            }
+
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mRecord.dispatchDestinationFound(DiscoveryCallback.this,
+                            destination, routes);
+                }
+            });
+        }
+
+        /**
+         * Called by the service when a destination is no longer
+         * reachable or is no longer offering any routes that satisfy
+         * the discovery request.
+         *
+         * @param destination The destination that went away.
+         */
+        public void onDestinationLost(final @NonNull DestinationInfo destination) {
+            if (destination == null) {
+                throw new IllegalArgumentException("destination must not be null");
+            }
+
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mRecord.dispatchDestinationLost(DiscoveryCallback.this, destination);
+                }
+            });
+        }
+
+        /**
+         * Called by the service when a discovery has failed in a non-recoverable manner.
+         *
+         * @param error The error code: one of
+         * {@link MediaRouter#DISCOVERY_ERROR_UNKNOWN},
+         * {@link MediaRouter#DISCOVERY_ERROR_ABORTED},
+         * or {@link MediaRouter#DISCOVERY_ERROR_NO_CONNECTIVITY}.
+         * @param message The localized error message, or null if none.  This message
+         * may be shown to the user.
+         * @param extras Additional information about the error which a client
+         * may use, or null if none.
+         */
+        public void onDiscoveryFailed(final @DiscoveryError int error,
+                final @Nullable CharSequence message, final @Nullable Bundle extras) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mRecord.dispatchDiscoveryFailed(DiscoveryCallback.this,
+                            error, message, extras);
+                }
+            });
+        }
+    }
+
+    /**
+     * Provides events in response to a connection request.
+     */
+    public final class ConnectionCallback {
+        private final ClientRecord mRecord;
+
+        ConnectionCallback(ClientRecord record) {
+            mRecord = record;
+        }
+
+        /**
+         * Called by the service when the connection succeeds.
+         *
+         * @param connection Immutable information about the connection.
+         */
+        public void onConnected(final @NonNull ConnectionInfo connection) {
+            if (connection == null) {
+                throw new IllegalArgumentException("connection must not be null");
+            }
+
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mRecord.dispatchConnected(ConnectionCallback.this, connection);
+                }
+            });
+        }
+
+        /**
+         * Called by the service when the connection is terminated normally.
+         * <p>
+         * Abnormal termination is reported via {@link #onConnectionFailed}.
+         * </p>
+         */
+        public void onDisconnected() {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mRecord.dispatchDisconnected(ConnectionCallback.this);
+                }
+            });
+        }
+
+        /**
+         * Called by the service when a connection attempt or connection in
+         * progress has failed in a non-recoverable manner.
+         *
+         * @param error The error code: one of
+         * {@link MediaRouter#CONNECTION_ERROR_ABORTED},
+         * {@link MediaRouter#CONNECTION_ERROR_UNAUTHORIZED},
+         * {@link MediaRouter#CONNECTION_ERROR_UNREACHABLE},
+         * {@link MediaRouter#CONNECTION_ERROR_BUSY},
+         * {@link MediaRouter#CONNECTION_ERROR_TIMEOUT},
+         * {@link MediaRouter#CONNECTION_ERROR_BROKEN},
+         * or {@link MediaRouter#CONNECTION_ERROR_BARGED}.
+         * @param message The localized error message, or null if none.  This message
+         * may be shown to the user.
+         * @param extras Additional information about the error which a client
+         * may use, or null if none.
+         */
+        public void onConnectionFailed(final @ConnectionError int error,
+                final @Nullable CharSequence message, final @Nullable Bundle extras) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mRecord.dispatchConnectionFailed(ConnectionCallback.this,
+                            error, message, extras);
+                }
+            });
+        }
+    }
+
+    /**
+     * Identifies a client of the media route service.
+     */
+    public static final class ClientInfo {
+        private final int mUid;
+        private final String mPackageName;
+
+        ClientInfo(int uid, String packageName) {
+            mUid = uid;
+            mPackageName = packageName;
+        }
+
+        /**
+         * Gets the UID of the client application.
+         */
+        public int getUid() {
+            return mUid;
+        }
+
+        /**
+         * Gets the package name of the client application.
+         */
+        public @NonNull String getPackageName() {
+            return mPackageName;
+        }
+
+        @Override
+        public @NonNull String toString() {
+            return "ClientInfo{ uid=" + mUid + ", package=" + mPackageName + " }";
+        }
+    }
+
+    private final class BinderService extends IMediaRouteService.Stub {
+        @Override
+        public void registerClient(final int clientUid, final String clientPackageName,
+                final IMediaRouteClientCallback callback) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    ClientInfo client = new ClientInfo(clientUid, clientPackageName);
+                    if (DEBUG) {
+                        Log.d(TAG, "registerClient: client=" + client);
+                    }
+
+                    ClientSession session = onCreateClientSession(client);
+                    if (session == null) {
+                        // request refused by service
+                        Log.w(TAG, "Media route service refused to create session for client: "
+                                + "client=" + client);
+                        return;
+                    }
+
+                    ClientRecord record = new ClientRecord(callback, client, session);
+                    try {
+                        callback.asBinder().linkToDeath(record, 0);
+                    } catch (RemoteException ex) {
+                        // client died prematurely
+                        Log.w(TAG, "Client died prematurely while creating session: "
+                                + "client=" + client);
+                        record.release();
+                        return;
+                    }
+
+                    mClientRecords.put(callback.asBinder(), record);
+                }
+            });
+        }
+
+        @Override
+        public void unregisterClient(IMediaRouteClientCallback callback) {
+            unregisterClient(callback, false);
+        }
+
+        void unregisterClient(final IMediaRouteClientCallback callback,
+                final boolean died) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    ClientRecord record = mClientRecords.remove(callback.asBinder());
+                    if (record == null) {
+                        return; // spurious
+                    }
+
+                    if (DEBUG) {
+                        Log.d(TAG, "unregisterClient: client=" + record.getClientInfo()
+                                + ", died=" + died);
+                    }
+
+                    record.release();
+                    callback.asBinder().unlinkToDeath(record, 0);
+                }
+            });
+        }
+
+        @Override
+        public void startDiscovery(final IMediaRouteClientCallback callback,
+                final int seq, final List<MediaRouteSelector> selectors,
+                final int flags) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    ClientRecord record = mClientRecords.get(callback.asBinder());
+                    if (record == null) {
+                        return; // spurious
+                    }
+
+                    if (DEBUG) {
+                        Log.d(TAG, "startDiscovery: client=" + record.getClientInfo()
+                                + ", seq=" + seq + ", selectors=" + selectors
+                                + ", flags=0x" + Integer.toHexString(flags));
+                    }
+                    record.startDiscovery(seq, selectors, flags);
+                }
+            });
+        }
+
+        @Override
+        public void stopDiscovery(final IMediaRouteClientCallback callback) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    ClientRecord record = mClientRecords.get(callback.asBinder());
+                    if (record == null) {
+                        return; // spurious
+                    }
+
+                    if (DEBUG) {
+                        Log.d(TAG, "stopDiscovery: client=" + record.getClientInfo());
+                    }
+                    record.stopDiscovery();
+                }
+            });
+        }
+
+        @Override
+        public void connect(final IMediaRouteClientCallback callback,
+                final int seq, final String destinationId, final String routeId,
+                final int flags, final Bundle extras) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    ClientRecord record = mClientRecords.get(callback.asBinder());
+                    if (record == null) {
+                        return; // spurious
+                    }
+
+                    if (DEBUG) {
+                        Log.d(TAG, "connect: client=" + record.getClientInfo()
+                                + ", seq=" + seq + ", destinationId=" + destinationId
+                                + ", routeId=" + routeId
+                                + ", flags=0x" + Integer.toHexString(flags)
+                                + ", extras=" + extras);
+                    }
+                    record.connect(seq, destinationId, routeId, flags, extras);
+                }
+            });
+        }
+
+        @Override
+        public void disconnect(final IMediaRouteClientCallback callback) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    ClientRecord record = mClientRecords.get(callback.asBinder());
+                    if (record == null) {
+                        return; // spurious
+                    }
+
+                    if (DEBUG) {
+                        Log.d(TAG, "disconnect: client=" + record.getClientInfo());
+                    }
+                    record.disconnect();
+                }
+            });
+        }
+
+        @Override
+        public void pauseStream(final IMediaRouteClientCallback callback) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    ClientRecord record = mClientRecords.get(callback.asBinder());
+                    if (record == null) {
+                        return; // spurious
+                    }
+
+                    if (DEBUG) {
+                        Log.d(TAG, "pauseStream: client=" + record.getClientInfo());
+                    }
+                    record.pauseStream();
+                }
+            });
+        }
+
+        @Override
+        public void resumeStream(final IMediaRouteClientCallback callback) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    ClientRecord record = mClientRecords.get(callback.asBinder());
+                    if (record == null) {
+                        return; // spurious
+                    }
+
+                    if (DEBUG) {
+                        Log.d(TAG, "resumeStream: client=" + record.getClientInfo());
+                    }
+                    record.resumeStream();
+                }
+            });
+        }
+    }
+
+    // Must be accessed on handler
+    private final class ClientRecord implements IBinder.DeathRecipient {
+        private final IMediaRouteClientCallback mClientCallback;
+        private final ClientInfo mClient;
+        private final ClientSession mSession;
+
+        private int mDiscoverySeq;
+        private DiscoveryRequest mDiscoveryRequest;
+        private DiscoveryCallback mDiscoveryCallback;
+        private final ArrayMap<String, DestinationRecord> mDestinations =
+                new ArrayMap<String, DestinationRecord>();
+
+        private int mConnectionSeq;
+        private ConnectionRequest mConnectionRequest;
+        private ConnectionCallback mConnectionCallback;
+        private ConnectionInfo mConnection;
+        private boolean mConnectionPaused;
+
+        public ClientRecord(IMediaRouteClientCallback callback,
+                ClientInfo client, ClientSession session) {
+            mClientCallback = callback;
+            mClient = client;
+            mSession = session;
+        }
+
+        // Invoked on binder thread unlike all other methods in this class.
+        @Override
+        public void binderDied() {
+            mService.unregisterClient(mClientCallback, true);
+        }
+
+        public ClientInfo getClientInfo() {
+            return mClient;
+        }
+
+        public void release() {
+            stopDiscovery();
+            disconnect();
+        }
+
+        public void startDiscovery(int seq, List<MediaRouteSelector> selectors,
+                int flags) {
+            stopDiscovery();
+
+            mDiscoverySeq = seq;
+            mDiscoveryRequest = new DiscoveryRequest(selectors);
+            mDiscoveryRequest.setFlags(flags);
+            mDiscoveryCallback = new DiscoveryCallback(this);
+            boolean started = mSession.onStartDiscovery(mDiscoveryRequest, mDiscoveryCallback);
+            if (!started) {
+                dispatchDiscoveryFailed(mDiscoveryCallback,
+                        MediaRouter.DISCOVERY_ERROR_ABORTED, null, null);
+                clearDiscovery();
+            }
+        }
+
+        public void stopDiscovery() {
+            if (mDiscoveryRequest != null) {
+                mSession.onStopDiscovery();
+                clearDiscovery();
+            }
+        }
+
+        private void clearDiscovery() {
+            mDestinations.clear();
+            mDiscoveryRequest = null;
+            mDiscoveryCallback = null;
+        }
+
+        public void connect(int seq, String destinationId, String routeId,
+                int flags, Bundle extras) {
+            disconnect();
+
+            mConnectionSeq = seq;
+            mConnectionCallback = new ConnectionCallback(this);
+
+            DestinationRecord destinationRecord = mDestinations.get(destinationId);
+            if (destinationRecord == null) {
+                Log.w(TAG, "Aborting connection to route since no matching destination "
+                        + "was found in the list of known destinations: "
+                        + "destinationId=" + destinationId);
+                dispatchConnectionFailed(mConnectionCallback,
+                        MediaRouter.CONNECTION_ERROR_ABORTED, null, null);
+                clearConnection();
+                return;
+            }
+
+            RouteInfo route = destinationRecord.getRoute(routeId);
+            if (route == null) {
+                Log.w(TAG, "Aborting connection to route since no matching route "
+                        + "was found in the list of known routes: "
+                        + "destination=" + destinationRecord.destination
+                        + ", routeId=" + routeId);
+                dispatchConnectionFailed(mConnectionCallback,
+                        MediaRouter.CONNECTION_ERROR_ABORTED, null, null);
+                clearConnection();
+                return;
+            }
+
+            mConnectionRequest = new ConnectionRequest(route);
+            mConnectionRequest.setFlags(flags);
+            mConnectionRequest.setExtras(extras);
+            boolean started = mSession.onConnect(mConnectionRequest, mConnectionCallback);
+            if (!started) {
+                dispatchConnectionFailed(mConnectionCallback,
+                        MediaRouter.CONNECTION_ERROR_ABORTED, null, null);
+                clearConnection();
+            }
+        }
+
+        public void disconnect() {
+            if (mConnectionRequest != null) {
+                mSession.onDisconnect();
+                clearConnection();
+            }
+        }
+
+        private void clearConnection() {
+            mConnectionRequest = null;
+            mConnectionCallback = null;
+            if (mConnection != null) {
+                mConnection.close();
+                mConnection = null;
+            }
+            mConnectionPaused = false;
+        }
+
+        public void pauseStream() {
+            if (mConnectionRequest != null && !mConnectionPaused) {
+                mConnectionPaused = true;
+                mSession.onPauseStream();
+            }
+        }
+
+        public void resumeStream() {
+            if (mConnectionRequest != null && mConnectionPaused) {
+                mConnectionPaused = false;
+                mSession.onResumeStream();
+            }
+        }
+
+        public void dispatchDestinationFound(DiscoveryCallback callback,
+                DestinationInfo destination, List<RouteInfo> routes) {
+            if (callback == mDiscoveryCallback) {
+                if (DEBUG) {
+                    Log.d(TAG, "destinationFound: destination=" + destination
+                            + ", routes=" + routes);
+                }
+                mDestinations.put(destination.getId(),
+                        new DestinationRecord(destination, routes));
+
+                ParcelableDestinationInfo pdi = new ParcelableDestinationInfo();
+                pdi.id = destination.getId();
+                pdi.name = destination.getName();
+                pdi.description = destination.getDescription();
+                pdi.iconResourceId = destination.getIconResourceId();
+                pdi.extras = destination.getExtras();
+                ArrayList<ParcelableRouteInfo> pris = new ArrayList<ParcelableRouteInfo>();
+                for (RouteInfo route : routes) {
+                    int selectorIndex = mDiscoveryRequest.getSelectors().indexOf(
+                            route.getSelector());
+                    if (selectorIndex < 0) {
+                        Log.w(TAG, "Ignoring route because the selector does not match "
+                                + "any of those that were originally supplied by the "
+                                + "client's discovery request: destination=" + destination
+                                + ", route=" + route);
+                        continue;
+                    }
+
+                    ParcelableRouteInfo pri = new ParcelableRouteInfo();
+                    pri.id = route.getId();
+                    pri.selectorIndex = selectorIndex;
+                    pri.features = route.getFeatures();
+                    pri.protocols = route.getProtocols().toArray(
+                            new String[route.getProtocols().size()]);
+                    pri.extras = route.getExtras();
+                    pris.add(pri);
+                }
+                try {
+                    mClientCallback.onDestinationFound(mDiscoverySeq, pdi,
+                            pris.toArray(new ParcelableRouteInfo[pris.size()]));
+                } catch (RemoteException ex) {
+                    // binder death handled elsewhere
+                }
+            }
+        }
+
+        public void dispatchDestinationLost(DiscoveryCallback callback,
+                DestinationInfo destination) {
+            if (callback == mDiscoveryCallback) {
+                if (DEBUG) {
+                    Log.d(TAG, "destinationLost: destination=" + destination);
+                }
+
+                if (mDestinations.get(destination.getId()).destination == destination) {
+                    mDestinations.remove(destination.getId());
+                    try {
+                        mClientCallback.onDestinationLost(mDiscoverySeq, destination.getId());
+                    } catch (RemoteException ex) {
+                        // binder death handled elsewhere
+                    }
+                }
+            }
+        }
+
+        public void dispatchDiscoveryFailed(DiscoveryCallback callback,
+                int error, CharSequence message, Bundle extras) {
+            if (callback == mDiscoveryCallback) {
+                if (DEBUG) {
+                    Log.d(TAG, "discoveryFailed: error=" + error + ", message=" + message
+                            + ", extras=" + extras);
+                }
+
+                try {
+                    mClientCallback.onDiscoveryFailed(mDiscoverySeq, error, message, extras);
+                } catch (RemoteException ex) {
+                    // binder death handled elsewhere
+                }
+            }
+        }
+
+        public void dispatchConnected(ConnectionCallback callback, ConnectionInfo connection) {
+            if (callback == mConnectionCallback) {
+                if (DEBUG) {
+                    Log.d(TAG, "connected: connection=" + connection);
+                }
+                if (mConnection == null) {
+                    mConnection = connection;
+
+                    ParcelableConnectionInfo pci = new ParcelableConnectionInfo();
+                    pci.audioAttributes = connection.getAudioAttributes();
+                    pci.presentationDisplayId = connection.getPresentationDisplay() != null ?
+                            connection.getPresentationDisplay().getDisplayId() : -1;
+                    pci.protocolBinders = new IBinder[connection.getProtocols().size()];
+                    for (int i = 0; i < pci.protocolBinders.length; i++) {
+                        pci.protocolBinders[i] = connection.getProtocolBinder(i);
+                    }
+                    pci.extras = connection.getExtras();
+                    try {
+                        mClientCallback.onConnected(mConnectionSeq, pci);
+                    } catch (RemoteException ex) {
+                        // binder death handled elsewhere
+                    }
+                } else {
+                    Log.w(TAG, "Media route service called onConnected() while already "
+                            + "connected.");
+                }
+            }
+        }
+
+        public void dispatchDisconnected(ConnectionCallback callback) {
+            if (callback == mConnectionCallback) {
+                if (DEBUG) {
+                    Log.d(TAG, "disconnected");
+                }
+
+                if (mConnection != null) {
+                    mConnection.close();
+                    mConnection = null;
+
+                    try {
+                        mClientCallback.onDisconnected(mConnectionSeq);
+                    } catch (RemoteException ex) {
+                        // binder death handled elsewhere
+                    }
+                }
+            }
+        }
+
+        public void dispatchConnectionFailed(ConnectionCallback callback,
+                int error, CharSequence message, Bundle extras) {
+            if (callback == mConnectionCallback) {
+                if (DEBUG) {
+                    Log.d(TAG, "connectionFailed: error=" + error + ", message=" + message
+                            + ", extras=" + extras);
+                }
+
+                try {
+                    mClientCallback.onConnectionFailed(mConnectionSeq, error, message, extras);
+                } catch (RemoteException ex) {
+                    // binder death handled elsewhere
+                }
+            }
+        }
+    }
+
+    private static final class DestinationRecord {
+        public final DestinationInfo destination;
+        public final List<RouteInfo> routes;
+
+        public DestinationRecord(DestinationInfo destination, List<RouteInfo> routes) {
+            this.destination = destination;
+            this.routes = routes;
+        }
+
+        public RouteInfo getRoute(String routeId) {
+            final int count = routes.size();
+            for (int i = 0; i < count; i++) {
+                RouteInfo route = routes.get(i);
+                if (route.getId().equals(routeId)) {
+                    return route;
+                }
+            }
+            return null;
+        }
+    }
+}
diff --git a/media/java/android/media/routing/MediaRouter.java b/media/java/android/media/routing/MediaRouter.java
new file mode 100644
index 0000000..4f6d324
--- /dev/null
+++ b/media/java/android/media/routing/MediaRouter.java
@@ -0,0 +1,1886 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.routing;
+
+import android.annotation.DrawableRes;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Presentation;
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ServiceInfo;
+import android.graphics.drawable.Drawable;
+import android.hardware.display.DisplayManager;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.media.AudioTrack;
+import android.media.VolumeProvider;
+import android.media.session.MediaController;
+import android.media.session.MediaSession;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.view.Display;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Media router allows applications to discover, connect to, control,
+ * and send content to nearby media devices known as destinations.
+ * <p>
+ * There are generally two participants involved in media routing: an
+ * application that wants to send media content to a destination and a
+ * {@link MediaRouteService media route service} that provides the
+ * service of transporting that content where it needs to go on behalf of the
+ * application.
+ * </p><p>
+ * To send media content to a destination, the application must ask the system
+ * to discover available routes to destinations that provide certain capabilities,
+ * establish a connection to a route, then send messages through the connection to
+ * control the routing of audio and video streams, launch remote applications,
+ * and invoke other functions of the destination.
+ * </p><p>
+ * Media router objects are thread-safe.
+ * </p>
+ *
+ * <h3>Destinations</h3>
+ * <p>
+ * The media devices to which an application may send media content are referred
+ * to in the API as destinations.  Each destination therefore represents a single
+ * independent device such as a speaker or TV set.  Destinations are given meaningful
+ * names and descriptions to help the user associate them with devices in their
+ * environment.
+ * </p><p>
+ * Destinations may be local or remote and may be accessed through various means,
+ * often wirelessly.  The user may install media route services to enable
+ * media applications to connect to a variety of destinations with different
+ * capabilities.
+ * </p>
+ *
+ * <h3>Routes</h3>
+ * <p>
+ * Routes represent possible usages or means of reaching and interacting with
+ * a destination.  Since destinations may support many different features, they may
+ * each offer multiple routes for applications to choose from based on their needs.
+ * For example, one route might express the ability to stream locally rendered audio
+ * and video to the device; another route might express the ability to send a URL for
+ * the destination to download from the network and play all by itself.
+ * </p><p>
+ * Routes are discovered according to the set of capabilities that
+ * an application or the system is seeking to use at a particular time.  For example,
+ * if an application wants to stream music to a destination then it will ask the
+ * {@link MediaRouter} to find routes to destinations can stream music and ignore
+ * all other destinations that cannot.
+ * </p><p>
+ * In general, the application will inspect the set of routes that have been
+ * offered then connect to the most appropriate route for its desired purpose.
+ * </p>
+ *
+ * <h3>Route Selection</h3>
+ * <p>
+ * When the user open the media route chooser activity, the system will display
+ * a list of nearby media destinations which have been discovered.  After the
+ * choice is made the application may connect to one of the routes offered by
+ * this destination and begin communicating with the destination.
+ * </p><p>
+ * Destinations are located through a process called discovery.  During discovery,
+ * the system will start installed {@link MediaRouteService media route services}
+ * to scan the network for nearby devices that offer the kinds of capabilities that the
+ * application is seeking to use.  The application specifies the capabilities it requires by
+ * adding {@link MediaRouteSelector media route selectors} to the media router
+ * using the {@link #addSelector} method.  Only destinations that provide routes
+ * which satisfy at least one of these media route selectors will be discovered.
+ * </p><p>
+ * Once the user has selected a destination, the application will be given a chance
+ * to choose one of the routes to which it would like to connect.  The application
+ * may switch to a different route from the same destination at a later time but
+ * in order to connect to a new destination, the application must once again launch
+ * the media route chooser activity to ask the user to choose a destination.
+ * </p>
+ *
+ * <h3>Route Protocols</h3>
+ * <p>
+ * Route protocols express capabilities offered by routes.  Each media route selector
+ * must specify at least one required protocol by which the routes will be selected.
+ * </p><p>
+ * The framework provides several predefined <code>MediaRouteProtocols</code> which are
+ * defined in the <code>android-support-media-protocols.jar</code> support library.
+ * Applications must statically link this library to make use of these protocols.
+ * </p><p>
+ * The static library approach is used to enable ongoing extension and refinement
+ * of protocols in the SDK and interoperability with the media router implementation
+ * for older platform versions which is offered by the framework support library.
+ * </p><p>
+ * Media route services may also define custom media route protocols of their own
+ * to enable applications to access specialized capabilities of certain destinations
+ * assuming they have linked in the required protocol code.
+ * </p><p>
+ * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code> for more information.
+ * </p>
+ *
+ * <h3>Connections</h3>
+ * <p>
+ * After connecting to a media route, the application can send commands to
+ * the route using any of the protocols that it requested.  If the route supports live
+ * audio or video streaming then the application can create an {@link AudioTrack} or
+ * {@link Presentation} to route locally generated content to the destination.
+ * </p>
+ *
+ * <h3>Delegation</h3>
+ * <p>
+ * The creator of the media router is responsible for establishing the policy for
+ * discovering and connecting to destinations.  UI components may observe the state
+ * of the media router by {@link #createDelegate creating} a {@link Delegate}.
+ * </p><p>
+ * The media router should also be attached to the {@link MediaSession media session}
+ * that is handling media playback lifecycle.  This will allow
+ * authorized {@link MediaController media controllers}, possibly running in other
+ * processes, to provide UI to examine and change the media destination by
+ * {@link MediaController#createMediaRouterDelegate creating} a {@link Delegate}
+ * for the media router associated with the session.
+ * </p>
+ */
+public final class MediaRouter {
+    private final DisplayManager mDisplayManager;
+
+    private final Object mLock = new Object();
+
+    private RoutingCallback mRoutingCallback;
+    private Handler mRoutingCallbackHandler;
+
+    private boolean mReleased;
+    private int mDiscoveryState;
+    private int mConnectionState;
+    private final ArrayList<MediaRouteSelector> mSelectors =
+            new ArrayList<MediaRouteSelector>();
+    private final ArrayMap<DestinationInfo, List<RouteInfo>> mDiscoveredDestinations =
+            new ArrayMap<DestinationInfo, List<RouteInfo>>();
+    private RouteInfo mSelectedRoute;
+    private ConnectionInfo mConnection;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = { DISCOVERY_STATE_STOPPED, DISCOVERY_STATE_STARTED })
+    public @interface DiscoveryState { }
+
+    /**
+     * Discovery state: Discovery is not currently in progress.
+     */
+    public static final int DISCOVERY_STATE_STOPPED = 0;
+
+    /**
+     * Discovery state: Discovery is being performed.
+     */
+    public static final int DISCOVERY_STATE_STARTED = 1;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, value = { DISCOVERY_FLAG_BACKGROUND })
+    public @interface DiscoveryFlags { }
+
+    /**
+     * Discovery flag: Indicates that the client has requested passive discovery in
+     * the background.  The media route service should try to use less power and rely
+     * more on its internal caches to minimize its impact.
+     */
+    public static final int DISCOVERY_FLAG_BACKGROUND = 1 << 0;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = { DISCOVERY_ERROR_UNKNOWN, DISCOVERY_ERROR_ABORTED,
+            DISCOVERY_ERROR_NO_CONNECTIVITY })
+    public @interface DiscoveryError { }
+
+    /**
+     * Discovery error: Unknown error; refer to the error message for details.
+     */
+    public static final int DISCOVERY_ERROR_UNKNOWN = 0;
+
+    /**
+     * Discovery error: The media router or media route service has decided not to
+     * handle the discovery request for some reason.
+     */
+    public static final int DISCOVERY_ERROR_ABORTED = 1;
+
+    /**
+     * Discovery error: The media route service is unable to perform discovery
+     * due to a lack of connectivity such as because the radio is disabled.
+     */
+    public static final int DISCOVERY_ERROR_NO_CONNECTIVITY = 2;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = { CONNECTION_STATE_DISCONNECTED, CONNECTION_STATE_CONNECTING,
+            CONNECTION_STATE_CONNECTED })
+    public @interface ConnectionState { }
+
+    /**
+     * Connection state: No destination has been selected.  Media content should
+     * be sent to the default output.
+     */
+    public static final int CONNECTION_STATE_DISCONNECTED = 0;
+
+    /**
+     * Connection state: The application is in the process of connecting to
+     * a route offered by the selected destination.
+     */
+    public static final int CONNECTION_STATE_CONNECTING = 1;
+
+    /**
+     * Connection state: The application has connected to a route offered by
+     * the selected destination.
+     */
+    public static final int CONNECTION_STATE_CONNECTED = 2;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, value = { CONNECTION_FLAG_BARGE })
+    public @interface ConnectionFlags { }
+
+    /**
+     * Connection flag: Indicates that the client has requested to barge in and evict
+     * other clients that might have already connected to the destination and that
+     * would otherwise prevent this client from connecting.  When this flag is not
+     * set, the media route service should be polite and report
+     * {@link MediaRouter#CONNECTION_ERROR_BUSY} in case the destination is
+     * already occupied and cannot accept additional connections.
+     */
+    public static final int CONNECTION_FLAG_BARGE = 1 << 0;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = { CONNECTION_ERROR_UNKNOWN, CONNECTION_ERROR_ABORTED,
+            CONNECTION_ERROR_UNAUTHORIZED, CONNECTION_ERROR_UNAUTHORIZED,
+            CONNECTION_ERROR_BUSY, CONNECTION_ERROR_TIMEOUT, CONNECTION_ERROR_BROKEN })
+    public @interface ConnectionError { }
+
+    /**
+     * Connection error: Unknown error; refer to the error message for details.
+     */
+    public static final int CONNECTION_ERROR_UNKNOWN = 0;
+
+    /**
+     * Connection error: The media router or media route service has decided not to
+     * handle the connection request for some reason.
+     */
+    public static final int CONNECTION_ERROR_ABORTED = 1;
+
+    /**
+     * Connection error: The device has refused the connection from this client.
+     * This error should be avoided because the media route service should attempt
+     * to filter out devices that the client cannot access as it performs discovery
+     * on behalf of that client.
+     */
+    public static final int CONNECTION_ERROR_UNAUTHORIZED = 2;
+
+    /**
+     * Connection error: The device is unreachable over the network.
+     */
+    public static final int CONNECTION_ERROR_UNREACHABLE = 3;
+
+    /**
+     * Connection error: The device is already busy serving another client and
+     * the connection request did not ask to barge in.
+     */
+    public static final int CONNECTION_ERROR_BUSY = 4;
+
+    /**
+     * Connection error: A timeout occurred during connection.
+     */
+    public static final int CONNECTION_ERROR_TIMEOUT = 5;
+
+    /**
+     * Connection error: The connection to the device was severed unexpectedly.
+     */
+    public static final int CONNECTION_ERROR_BROKEN = 6;
+
+    /**
+     * Connection error: The connection was terminated because a different client barged
+     * in and took control of the destination.
+     */
+    public static final int CONNECTION_ERROR_BARGED = 7;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = { DISCONNECTION_REASON_APPLICATION_REQUEST,
+            DISCONNECTION_REASON_USER_REQUEST, DISCONNECTION_REASON_ERROR })
+    public @interface DisconnectionReason { }
+
+    /**
+     * Disconnection reason: The application requested disconnection itself.
+     */
+    public static final int DISCONNECTION_REASON_APPLICATION_REQUEST = 0;
+
+    /**
+     * Disconnection reason: The user requested disconnection.
+     */
+    public static final int DISCONNECTION_REASON_USER_REQUEST = 1;
+
+    /**
+     * Disconnection reason: An error occurred.
+     */
+    public static final int DISCONNECTION_REASON_ERROR = 2;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, value = { ROUTE_FEATURE_LIVE_AUDIO, ROUTE_FEATURE_LIVE_VIDEO })
+    public @interface RouteFeatures { }
+
+    /**
+     * Route feature: Live audio.
+     * <p>
+     * A route that supports live audio streams audio rendered by the application
+     * to the destination.
+     * </p><p>
+     * To take advantage of live audio routing, the application must render its
+     * media using the audio attributes specified by {@link #getPreferredAudioAttributes}.
+     * </p>
+     *
+     * @see #getPreferredAudioAttributes
+     * @see android.media.AudioAttributes
+     */
+    public static final int ROUTE_FEATURE_LIVE_AUDIO = 1 << 0;
+
+    /**
+     * Route feature: Live video.
+     * <p>
+     * A route that supports live video streams video rendered by the application
+     * to the destination.
+     * </p><p>
+     * To take advantage of live video routing, the application must render its
+     * media to a {@link android.app.Presentation presentation window} on the
+     * display specified by {@link #getPreferredPresentationDisplay}.
+     * </p>
+     *
+     * @see #getPreferredPresentationDisplay
+     * @see android.app.Presentation
+     */
+    public static final int ROUTE_FEATURE_LIVE_VIDEO = 1 << 1;
+
+    /**
+     * Creates a media router.
+     *
+     * @param context The context with which the router is associated.
+     */
+    public MediaRouter(@NonNull Context context) {
+        if (context == null) {
+            throw new IllegalArgumentException("context must not be null");
+        }
+
+        mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
+    }
+
+    /** @hide */
+    public IMediaRouter getBinder() {
+        // todo
+        return null;
+    }
+
+    /**
+     * Disconnects from the selected destination and releases the media router.
+     * <p>
+     * This method should be called by the application when it no longer requires
+     * the media router to ensure that all bound resources may be cleaned up.
+     * </p>
+     */
+    public void release() {
+        synchronized (mLock) {
+            mReleased = true;
+            // todo
+        }
+    }
+
+    /**
+     * Returns true if the media router has been released.
+     */
+    public boolean isReleased() {
+        synchronized (mLock) {
+            return mReleased;
+        }
+    }
+
+    /**
+     * Gets the current route discovery state.
+     *
+     * @return The current discovery state: one of {@link #DISCOVERY_STATE_STOPPED},
+     * {@link #DISCOVERY_STATE_STARTED}.
+     */
+    public @DiscoveryState int getDiscoveryState() {
+        synchronized (mLock) {
+            return mDiscoveryState;
+        }
+    }
+
+    /**
+     * Gets the current route connection state.
+     *
+     * @return The current state: one of {@link #CONNECTION_STATE_DISCONNECTED},
+     * {@link #CONNECTION_STATE_CONNECTING} or {@link #CONNECTION_STATE_CONNECTED}.
+     */
+    public @ConnectionState int getConnectionState() {
+        synchronized (mLock) {
+            return mConnectionState;
+        }
+    }
+
+    /**
+     * Creates a media router delegate through which the destination of the media
+     * router may be controlled.
+     * <p>
+     * This is the point of entry for UI code that initiates discovery and
+     * connection to routes.
+     * </p>
+     */
+    public @NonNull Delegate createDelegate() {
+        return null; // todo
+    }
+
+    /**
+     * Sets a callback to participate in route discovery, filtering, and connection
+     * establishment.
+     *
+     * @param callback The callback to set, or null if none.
+     * @param handler The handler to receive callbacks, or null to use the current thread.
+     */
+    public void setRoutingCallback(@Nullable RoutingCallback callback,
+            @Nullable Handler handler) {
+        synchronized (mLock) {
+            if (callback == null) {
+                mRoutingCallback = null;
+                mRoutingCallbackHandler = null;
+            } else {
+                mRoutingCallback = callback;
+                mRoutingCallbackHandler = handler != null ? handler : new Handler();
+            }
+        }
+    }
+
+    /**
+     * Adds a media route selector to use to find destinations that have
+     * routes with the specified capabilities during route discovery.
+     */
+    public void addSelector(@NonNull MediaRouteSelector selector) {
+        if (selector == null) {
+            throw new IllegalArgumentException("selector must not be null");
+        }
+
+        synchronized (mLock) {
+            if (!mSelectors.contains(selector)) {
+                mSelectors.add(selector);
+                // todo
+            }
+        }
+    }
+
+    /**
+     * Removes a media route selector.
+     */
+    public void removeSelector(@NonNull MediaRouteSelector selector) {
+        if (selector == null) {
+            throw new IllegalArgumentException("selector must not be null");
+        }
+
+        synchronized (mLock) {
+            if (mSelectors.remove(selector)) {
+                // todo
+            }
+        }
+    }
+
+    /**
+     * Removes all media route selectors.
+     * <p>
+     * Note that at least one selector must be added in order to perform discovery.
+     * </p>
+     */
+    public void clearSelectors() {
+        synchronized (mLock) {
+            if (!mSelectors.isEmpty()) {
+                mSelectors.clear();
+                // todo
+            }
+        }
+    }
+
+    /**
+     * Gets a list of all media route selectors to consider during discovery.
+     */
+    public @NonNull List<MediaRouteSelector> getSelectors() {
+        synchronized (mLock) {
+            return new ArrayList<MediaRouteSelector>(mSelectors);
+        }
+    }
+
+    /**
+     * Gets the connection to the currently selected route.
+     *
+     * @return The connection to the currently selected route, or null if not connected.
+     */
+    public @NonNull ConnectionInfo getConnection() {
+        synchronized (mLock) {
+            return mConnection;
+        }
+    }
+
+    /**
+     * Gets the list of discovered destinations.
+     * <p>
+     * This list is only valid while discovery is running and is null otherwise.
+     * </p>
+     *
+     * @return The list of discovered destinations, or null if discovery is not running.
+     */
+    public @NonNull List<DestinationInfo> getDiscoveredDestinations() {
+        synchronized (mLock) {
+            if (mDiscoveryState == DISCOVERY_STATE_STARTED) {
+                return new ArrayList<DestinationInfo>(mDiscoveredDestinations.keySet());
+            }
+            return null;
+        }
+    }
+
+    /**
+     * Gets the list of discovered routes for a particular destination.
+     * <p>
+     * This list is only valid while discovery is running and is null otherwise.
+     * </p>
+     *
+     * @param destination The destination for which to get the list of discovered routes.
+     * @return The list of discovered routes for the destination, or null if discovery
+     * is not running.
+     */
+    public @NonNull List<RouteInfo> getDiscoveredRoutes(@NonNull DestinationInfo destination) {
+        if (destination == null) {
+            throw new IllegalArgumentException("destination must not be null");
+        }
+        synchronized (mLock) {
+            if (mDiscoveryState == DISCOVERY_STATE_STARTED) {
+                List<RouteInfo> routes = mDiscoveredDestinations.get(destination);
+                if (routes != null) {
+                    return new ArrayList<RouteInfo>(routes);
+                }
+            }
+            return null;
+        }
+    }
+
+    /**
+     * Gets the destination that has been selected.
+     *
+     * @return The selected destination, or null if disconnected.
+     */
+    public @Nullable DestinationInfo getSelectedDestination() {
+        synchronized (mLock) {
+            return mSelectedRoute != null ? mSelectedRoute.getDestination() : null;
+        }
+    }
+
+    /**
+     * Gets the route that has been selected.
+     *
+     * @return The selected destination, or null if disconnected.
+     */
+    public @Nullable RouteInfo getSelectedRoute() {
+        synchronized (mLock) {
+            return mSelectedRoute;
+        }
+    }
+
+    /**
+     * Gets the preferred audio attributes that should be used to stream live audio content
+     * based on the connected route.
+     * <p>
+     * Use an {@link AudioTrack} to send audio content to the destination with these
+     * audio attributes.
+     * </p><p>
+     * The preferred audio attributes may change when a connection is established but it
+     * will remain constant until disconnected.
+     * </p>
+     *
+     * @return The preferred audio attributes to use.  When connected, returns the
+     * route's audio attributes or null if it does not support live audio streaming.
+     * Otherwise returns audio attributes associated with {@link AudioAttributes#USAGE_MEDIA}.
+     */
+    public @Nullable AudioAttributes getPreferredAudioAttributes() {
+        synchronized (mLock) {
+            if (mConnection != null) {
+                return mConnection.getAudioAttributes();
+            }
+            return new AudioAttributes.Builder()
+                    .setLegacyStreamType(AudioManager.STREAM_MUSIC)
+                    .build();
+        }
+    }
+
+    /**
+     * Gets the preferred presentation display that should be used to stream live video content
+     * based on the connected route.
+     * <p>
+     * Use a {@link Presentation} to send video content to the destination with this display.
+     * </p><p>
+     * The preferred presentation display may change when a connection is established but it
+     * will remain constant until disconnected.
+     * </p>
+     *
+     * @return The preferred presentation display to use.  When connected, returns
+     * the route's presentation display or null if it does not support live video
+     * streaming.  Otherwise returns the first available
+     * {@link DisplayManager#DISPLAY_CATEGORY_PRESENTATION presentation display},
+     * such as a mirrored wireless or HDMI display or null if none.
+     */
+    public @Nullable Display getPreferredPresentationDisplay() {
+        synchronized (mLock) {
+            if (mConnection != null) {
+                return mConnection.getPresentationDisplay();
+            }
+            Display[] displays = mDisplayManager.getDisplays(
+                    DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
+            return displays.length != 0 ? displays[0] : null;
+        }
+    }
+
+    /**
+     * Gets the preferred volume provider that should be used to control the volume
+     * of content rendered on the currently selected route.
+     * <p>
+     * The preferred volume provider may change when a connection is established but it
+     * will remain the same until disconnected.
+     * </p>
+     *
+     * @return The preferred volume provider to use, or null if the currently
+     * selected route does not support remote volume adjustment or if the connection
+     * is not yet established.  If no route is selected, returns null to indicate
+     * that system volume control should be used.
+     */
+    public @Nullable VolumeProvider getPreferredVolumeProvider() {
+        synchronized (mLock) {
+            if (mConnection != null) {
+                return mConnection.getVolumeProvider();
+            }
+            return null;
+        }
+    }
+
+    /**
+     * Requests to pause streaming of live audio or video routes.
+     * Should be called when the application is going into the background and is
+     * no longer rendering content locally.
+     * <p>
+     * This method does nothing unless a connection has been established.
+     * </p>
+     */
+    public void pauseStream() {
+        // todo
+    }
+
+    /**
+     * Requests to resume streaming of live audio or video routes.
+     * May be called when the application is returning to the foreground and is
+     * about to resume rendering content locally.
+     * <p>
+     * This method does nothing unless a connection has been established.
+     * </p>
+     */
+    public void resumeStream() {
+        // todo
+    }
+
+    /**
+     * This class is used by UI components to let the user discover and
+     * select a destination to which the media router should connect.
+     * <p>
+     * This API has somewhat more limited functionality than the {@link MediaRouter}
+     * itself because it is designed to allow applications to control
+     * the destination of media router instances that belong to other processes.
+     * </p><p>
+     * To control the destination of your own media router, call
+     * {@link #createDelegate} to obtain a local delegate object.
+     * </p><p>
+     * To control the destination of a media router that belongs to another process,
+     * first obtain a {@link MediaController} that is associated with the media playback
+     * that is occurring in that process, then call
+     * {@link MediaController#createMediaRouterDelegate} to obtain an instance of
+     * its destination controls.  Note that special permissions may be required to
+     * obtain the {@link MediaController} instance in the first place.
+     * </p>
+     */
+    public static final class Delegate {
+        /**
+         * Returns true if the media router has been released.
+         */
+        public boolean isReleased() {
+            // todo
+            return false;
+        }
+
+        /**
+         * Gets the current route discovery state.
+         *
+         * @return The current discovery state: one of {@link #DISCOVERY_STATE_STOPPED},
+         * {@link #DISCOVERY_STATE_STARTED}.
+         */
+        public @DiscoveryState int getDiscoveryState() {
+            // todo
+            return -1;
+        }
+
+        /**
+         * Gets the current route connection state.
+         *
+         * @return The current state: one of {@link #CONNECTION_STATE_DISCONNECTED},
+         * {@link #CONNECTION_STATE_CONNECTING} or {@link #CONNECTION_STATE_CONNECTED}.
+         */
+        public @ConnectionState int getConnectionState() {
+            // todo
+            return -1;
+        }
+
+        /**
+         * Gets the currently selected destination.
+         *
+         * @return The destination information, or null if none.
+         */
+        public @Nullable DestinationInfo getSelectedDestination() {
+            return null;
+        }
+
+        /**
+         * Gets the list of discovered destinations.
+         * <p>
+         * This list is only valid while discovery is running and is null otherwise.
+         * </p>
+         *
+         * @return The list of discovered destinations, or null if discovery is not running.
+         */
+        public @NonNull List<DestinationInfo> getDiscoveredDestinations() {
+            return null;
+        }
+
+        /**
+         * Adds a callback to receive state changes.
+         *
+         * @param callback The callback to set, or null if none.
+         * @param handler The handler to receive callbacks, or null to use the current thread.
+         */
+        public void addStateCallback(@Nullable StateCallback callback,
+                @Nullable Handler handler) {
+            if (callback == null) {
+                throw new IllegalArgumentException("callback must not be null");
+            }
+            if (handler == null) {
+                handler = new Handler();
+            }
+            // todo
+        }
+
+        /**
+         * Removes a callback for state changes.
+         *
+         * @param callback The callback to set, or null if none.
+         */
+        public void removeStateCallback(@Nullable StateCallback callback) {
+            // todo
+        }
+
+        /**
+         * Starts performing discovery.
+         * <p>
+         * Performing discovery is expensive.  Make sure to call {@link #stopDiscovery}
+         * as soon as possible once a new destination has been selected to allow the system
+         * to stop services associated with discovery.
+         * </p>
+         *
+         * @param flags The discovery flags, such as {@link MediaRouter#DISCOVERY_FLAG_BACKGROUND}.
+         */
+        public void startDiscovery(@DiscoveryFlags int flags) {
+            // todo
+        }
+
+        /**
+         * Stops performing discovery.
+         */
+        public void stopDiscovery() {
+            // todo
+        }
+
+        /**
+         * Connects to a destination during route discovery.
+         * <p>
+         * This method may only be called while route discovery is active and the
+         * destination appears in the
+         * {@link #getDiscoveredDestinations list of discovered destinations}.
+         * If the media router is already connected to a route then it will first disconnect
+         * from the current route then connect to the new route.
+         * </p>
+         *
+         * @param destination The destination to which the media router should connect.
+         * @param flags The connection flags, such as {@link MediaRouter#CONNECTION_FLAG_BARGE}.
+         */
+        public void connect(@NonNull DestinationInfo destination, @DiscoveryFlags int flags) {
+            // todo
+        }
+
+        /**
+         * Disconnects from the currently selected destination.
+         * <p>
+         * Does nothing if not currently connected.
+         * </p>
+         *
+         * @param reason The reason for the disconnection: one of
+         * {@link #DISCONNECTION_REASON_APPLICATION_REQUEST},
+         * {@link #DISCONNECTION_REASON_USER_REQUEST}, or {@link #DISCONNECTION_REASON_ERROR}.
+         */
+        public void disconnect(@DisconnectionReason int reason) {
+            // todo
+        }
+    }
+
+    /**
+     * Describes immutable properties of a connection to a route.
+     */
+    public static final class ConnectionInfo {
+        private final RouteInfo mRoute;
+        private final AudioAttributes mAudioAttributes;
+        private final Display mPresentationDisplay;
+        private final VolumeProvider mVolumeProvider;
+        private final IBinder[] mProtocolBinders;
+        private final Object[] mProtocolInstances;
+        private final Bundle mExtras;
+        private final ArrayList<Closeable> mCloseables;
+
+        private static final Class<?>[] MEDIA_ROUTE_PROTOCOL_CTOR_PARAMETERS =
+                new Class<?>[] { IBinder.class };
+
+        ConnectionInfo(RouteInfo route,
+                AudioAttributes audioAttributes, Display display,
+                VolumeProvider volumeProvider, IBinder[] protocolBinders,
+                Bundle extras, ArrayList<Closeable> closeables) {
+            mRoute = route;
+            mAudioAttributes = audioAttributes;
+            mPresentationDisplay = display;
+            mVolumeProvider = volumeProvider;
+            mProtocolBinders = protocolBinders;
+            mProtocolInstances = new Object[mProtocolBinders.length];
+            mExtras = extras;
+            mCloseables = closeables;
+        }
+
+        /**
+         * Gets the route that is connected.
+         */
+        public @NonNull RouteInfo getRoute() {
+            return mRoute;
+        }
+
+        /**
+         * Gets the audio attributes which the client should use to stream audio
+         * to the destination, or null if the route does not support live audio streaming.
+         */
+        public @Nullable AudioAttributes getAudioAttributes() {
+            return mAudioAttributes;
+        }
+
+        /**
+         * Gets the display which the client should use to stream video to the
+         * destination using a {@link Presentation}, or null if the route does not
+         * support live video streaming.
+         */
+        public @Nullable Display getPresentationDisplay() {
+            return mPresentationDisplay;
+        }
+
+        /**
+         * Gets the route's volume provider, or null if none.
+         */
+        public @Nullable VolumeProvider getVolumeProvider() {
+            return mVolumeProvider;
+        }
+
+        /**
+         * Gets the set of supported route features.
+         */
+        public @RouteFeatures int getFeatures() {
+            return mRoute.getFeatures();
+        }
+
+        /**
+         * Gets the list of supported route protocols.
+         * <p>
+         * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
+         * for more information.
+         * </p>
+         */
+        public @NonNull List<String> getProtocols() {
+            return mRoute.getProtocols();
+        }
+
+        /**
+         * Gets an instance of a route protocol object that wraps the protocol binder
+         * and provides easy access to the protocol's functionality.
+         * <p>
+         * This is a convenience method which invokes {@link #getProtocolBinder(String)}
+         * using the name of the provided class then passes the resulting {@link IBinder}
+         * to a single-argument constructor of that class.
+         * </p><p>
+         * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
+         * for more information.
+         * </p>
+         */
+        @SuppressWarnings("unchecked")
+        public @Nullable <T> T getProtocolObject(Class<T> clazz) {
+            int index = getProtocols().indexOf(clazz.getName());
+            if (index < 0) {
+                return null;
+            }
+            if (mProtocolInstances[index] == null && mProtocolBinders[index] != null) {
+                final Constructor<T> ctor;
+                try {
+                    ctor = clazz.getConstructor(MEDIA_ROUTE_PROTOCOL_CTOR_PARAMETERS);
+                } catch (NoSuchMethodException ex) {
+                    throw new RuntimeException("Could not find public constructor "
+                            + "with IBinder argument in protocol class: " + clazz.getName(), ex);
+                }
+                try {
+                    mProtocolInstances[index] = ctor.newInstance(mProtocolBinders[index]);
+                } catch (InstantiationException | IllegalAccessException
+                        | InvocationTargetException ex) {
+                    throw new RuntimeException("Could create instance of protocol class: "
+                            + clazz.getName(), ex);
+                }
+            }
+            return (T)mProtocolInstances[index];
+        }
+
+        /**
+         * Gets the {@link IBinder} that provides access to the specified route protocol
+         * or null if the protocol is not supported.
+         * <p>
+         * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
+         * for more information.
+         * </p>
+         */
+        public @Nullable IBinder getProtocolBinder(@NonNull String name) {
+            int index = getProtocols().indexOf(name);
+            return index >= 0 ? mProtocolBinders[index] : null;
+        }
+
+        /**
+         * Gets the {@link IBinder} that provides access to the specified route protocol
+         * at the given index in the protocol list or null if the protocol is not supported.
+         * <p>
+         * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
+         * for more information.
+         * </p>
+         */
+        public @Nullable IBinder getProtocolBinder(int index) {
+            return mProtocolBinders[index];
+        }
+
+        /**
+         * Gets optional extra media route service or protocol specific information about
+         * the connection.  Use the service or protocol name as the prefix for
+         * any extras to avoid namespace collisions.
+         */
+        public @Nullable Bundle getExtras() {
+            return mExtras;
+        }
+
+        /**
+         * Closes all closeables associated with the connection when the connection
+         * is being torn down.
+         */
+        void close() {
+            final int count = mCloseables.size();
+            for (int i = 0; i < count; i++) {
+                try {
+                    mCloseables.get(i).close();
+                } catch (IOException ex) {
+                }
+            }
+        }
+
+        @Override
+        public @NonNull String toString() {
+            return "ConnectionInfo{ route=" + mRoute
+                    + ", audioAttributes=" + mAudioAttributes
+                    + ", presentationDisplay=" + mPresentationDisplay
+                    + ", volumeProvider=" + mVolumeProvider
+                    + ", protocolBinders=" + mProtocolBinders + " }";
+        }
+
+        /**
+         * Builds {@link ConnectionInfo} objects.
+         */
+        public static final class Builder {
+            private final RouteInfo mRoute;
+            private AudioAttributes mAudioAttributes;
+            private Display mPresentationDisplay;
+            private VolumeProvider mVolumeProvider;
+            private final IBinder[] mProtocols;
+            private Bundle mExtras;
+            private final ArrayList<Closeable> mCloseables = new ArrayList<Closeable>();
+
+            /**
+             * Creates a builder for connection information.
+             *
+             * @param route The route that is connected.
+             */
+            public Builder(@NonNull RouteInfo route) {
+                if (route == null) {
+                    throw new IllegalArgumentException("route");
+                }
+                mRoute = route;
+                mProtocols = new IBinder[route.getProtocols().size()];
+            }
+
+            /**
+             * Sets the audio attributes which the client should use to stream audio
+             * to the destination, or null if the route does not support live audio streaming.
+             */
+            public @NonNull Builder setAudioAttributes(
+                    @Nullable AudioAttributes audioAttributes) {
+                mAudioAttributes = audioAttributes;
+                return this;
+            }
+
+            /**
+             * Sets the display which the client should use to stream video to the
+             * destination using a {@link Presentation}, or null if the route does not
+             * support live video streaming.
+             */
+            public @NonNull Builder setPresentationDisplay(@Nullable Display display) {
+                mPresentationDisplay = display;
+                return this;
+            }
+
+            /**
+             * Sets the route's volume provider, or null if none.
+             */
+            public @NonNull Builder setVolumeProvider(@Nullable VolumeProvider provider) {
+                mVolumeProvider = provider;
+                return this;
+            }
+
+            /**
+             * Sets the binder stub of a supported route protocol using
+             * the protocol's fully qualified class name.  The protocol must be one
+             * of those that was indicated as being supported by the route.
+             * <p>
+             * If the stub implements {@link Closeable} then it will automatically
+             * be closed when the client disconnects from the route.
+             * </p><p>
+             * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
+             * for more information.
+             * </p>
+             */
+            public @NonNull Builder setProtocolStub(@NonNull Class<?> clazz,
+                    @NonNull IInterface stub) {
+                if (clazz == null) {
+                    throw new IllegalArgumentException("clazz must not be null");
+                }
+                if (stub == null) {
+                    throw new IllegalArgumentException("stub must not be null");
+                }
+                if (stub instanceof Closeable) {
+                    mCloseables.add((Closeable)stub);
+                }
+                return setProtocolBinder(clazz.getName(), stub.asBinder());
+            }
+
+            /**
+             * Sets the binder interface of a supported route protocol by name.
+             * The protocol must be one of those that was indicated as being supported
+             * by the route.
+             * <p>
+             * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
+             * for more information.
+             * </p>
+             */
+            public @NonNull Builder setProtocolBinder(@NonNull String name,
+                    @NonNull IBinder binder) {
+                if (TextUtils.isEmpty(name)) {
+                    throw new IllegalArgumentException("name must not be null or empty");
+                }
+                if (binder == null) {
+                    throw new IllegalArgumentException("binder must not be null");
+                }
+                int index = mRoute.getProtocols().indexOf(name);
+                if (index < 0) {
+                    throw new IllegalArgumentException("name must specify a protocol that "
+                            + "the route actually declared that it supports: "
+                            + "name=" + name + ", protocols=" + mRoute.getProtocols());
+                }
+                mProtocols[index] = binder;
+                return this;
+            }
+
+            /**
+             * Sets optional extra media route service or protocol specific information about
+             * the connection.  Use the service or protocol name as the prefix for
+             * any extras to avoid namespace collisions.
+             */
+            public @NonNull Builder setExtras(@Nullable Bundle extras) {
+                mExtras = extras;
+                return this;
+            }
+
+            /**
+             * Builds the {@link ConnectionInfo} object.
+             */
+            public @NonNull ConnectionInfo build() {
+                return new ConnectionInfo(mRoute,
+                        mAudioAttributes, mPresentationDisplay,
+                        mVolumeProvider, mProtocols, mExtras, mCloseables);
+            }
+        }
+    }
+
+    /**
+     * Describes one particular way of routing media content to a destination
+     * according to the capabilities specified by a media route selector on behalf
+     * of an application.
+     */
+    public static final class RouteInfo {
+        private final String mId;
+        private final DestinationInfo mDestination;
+        private final MediaRouteSelector mSelector;
+        private final int mFeatures;
+        private final ArrayList<String> mProtocols;
+        private final Bundle mExtras;
+
+        RouteInfo(String id, DestinationInfo destination, MediaRouteSelector selector,
+                int features, ArrayList<String> protocols, Bundle extras) {
+            mId = id;
+            mDestination = destination;
+            mSelector = selector;
+            mFeatures = features;
+            mProtocols = protocols;
+            mExtras = extras;
+        }
+
+        /**
+         * Gets the route's stable identifier.
+         * <p>
+         * The id is intended to uniquely identify the route among all routes that
+         * are offered by a particular destination in such a way that the client can
+         * refer to it at a later time.
+         * </p>
+         */
+        public @NonNull String getId() {
+            return mId;
+        }
+
+        /**
+         * Gets the destination that is offering this route.
+         */
+        public @NonNull DestinationInfo getDestination() {
+            return mDestination;
+        }
+
+        /**
+         * Gets the media route selector provided by the client for which this
+         * route was created.
+         * <p>
+         * It is implied that this route supports all of the required capabilities
+         * that were expressed in the selector.
+         * </p>
+         */
+        public @NonNull MediaRouteSelector getSelector() {
+            return mSelector;
+        }
+
+        /**
+         * Gets the set of supported route features.
+         */
+        public @RouteFeatures int getFeatures() {
+            return mFeatures;
+        }
+
+        /**
+         * Gets the list of supported route protocols.
+         * <p>
+         * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
+         * for more information.
+         * </p>
+         */
+        public @NonNull List<String> getProtocols() {
+            return mProtocols;
+        }
+
+        /**
+         * Gets optional extra information about the route, or null if none.
+         */
+        public @Nullable Bundle getExtras() {
+            return mExtras;
+        }
+
+        @Override
+        public @NonNull String toString() {
+            return "RouteInfo{ id=" + mId + ", destination=" + mDestination
+                    + ", features=0x" + Integer.toHexString(mFeatures)
+                    + ", selector=" + mSelector + ", protocols=" + mProtocols
+                    + ", extras=" + mExtras + " }";
+        }
+
+        /**
+         * Builds {@link RouteInfo} objects.
+         */
+        public static final class Builder {
+            private final DestinationInfo mDestination;
+            private final String mId;
+            private final MediaRouteSelector mSelector;
+            private int mFeatures;
+            private final ArrayList<String> mProtocols = new ArrayList<String>();
+            private Bundle mExtras;
+
+            /**
+             * Creates a builder for route information.
+             *
+             * @param id The route's stable identifier.
+             * @param destination The destination of this route.
+             * @param selector The media route selector provided by the client for which
+             * this route was created.  This must be one of the selectors that was
+             * included in the discovery request.
+             */
+            public Builder(@NonNull String id, @NonNull DestinationInfo destination,
+                    @NonNull MediaRouteSelector selector) {
+                if (TextUtils.isEmpty(id)) {
+                    throw new IllegalArgumentException("id must not be null or empty");
+                }
+                if (destination == null) {
+                    throw new IllegalArgumentException("destination must not be null");
+                }
+                if (selector == null) {
+                    throw new IllegalArgumentException("selector must not be null");
+                }
+                mDestination = destination;
+                mId = id;
+                mSelector = selector;
+            }
+
+            /**
+             * Sets the set of supported route features.
+             */
+            public @NonNull Builder setFeatures(@RouteFeatures int features) {
+                mFeatures = features;
+                return this;
+            }
+
+            /**
+             * Adds a supported route protocol using its fully qualified class name.
+             * <p>
+             * If the protocol was not requested by the client in its selector
+             * then it will be silently discarded.
+             * </p>
+             */
+            public @NonNull <T extends IInterface> Builder addProtocol(@NonNull Class<T> clazz) {
+                if (clazz == null) {
+                    throw new IllegalArgumentException("clazz must not be null");
+                }
+                return addProtocol(clazz.getName());
+            }
+
+            /**
+             * Adds a supported route protocol by name.
+             * <p>
+             * If the protocol was not requested by the client in its selector
+             * then it will be silently discarded.
+             * </p>
+             */
+            public @NonNull Builder addProtocol(@NonNull String name) {
+                if (TextUtils.isEmpty(name)) {
+                    throw new IllegalArgumentException("name must not be null");
+                }
+                if (mSelector.containsProtocol(name)) {
+                    mProtocols.add(name);
+                }
+                return this;
+            }
+
+            /**
+             * Sets optional extra information about the route, or null if none.
+             */
+            public @NonNull Builder setExtras(@Nullable Bundle extras) {
+                mExtras = extras;
+                return this;
+            }
+
+            /**
+             * Builds the {@link RouteInfo} object.
+             * <p>
+             * Ensures that all required protocols have been supplied.
+             * </p>
+             */
+            public @NonNull RouteInfo build() {
+                int missingFeatures = mSelector.getRequiredFeatures() & ~mFeatures;
+                if (missingFeatures != 0) {
+                    throw new IllegalStateException("The media route selector "
+                            + "specified required features which this route does "
+                            + "not appear to support so it should not have been published: "
+                            + "missing 0x" + Integer.toHexString(missingFeatures));
+                }
+                for (String protocol : mSelector.getRequiredProtocols()) {
+                    if (!mProtocols.contains(protocol)) {
+                        throw new IllegalStateException("The media route selector "
+                                + "specified required protocols which this route "
+                                + "does not appear to support so it should not have "
+                                + "been published: missing " + protocol);
+                    }
+                }
+                return new RouteInfo(mId, mDestination, mSelector,
+                        mFeatures, mProtocols, mExtras);
+            }
+        }
+    }
+
+    /**
+     * Describes a destination for media content such as a device,
+     * an individual port on a device, or a group of devices.
+     */
+    public static final class DestinationInfo {
+        private final String mId;
+        private final ServiceMetadata mService;
+        private final CharSequence mName;
+        private final CharSequence mDescription;
+        private final int mIconResourceId;
+        private final Bundle mExtras;
+
+        DestinationInfo(String id, ServiceMetadata service,
+                CharSequence name, CharSequence description,
+                int iconResourceId, Bundle extras) {
+            mId = id;
+            mService = service;
+            mName = name;
+            mDescription = description;
+            mIconResourceId = iconResourceId;
+            mExtras = extras;
+        }
+
+        /**
+         * Gets the destination's stable identifier.
+         * <p>
+         * The id is intended to uniquely identify the destination among all destinations
+         * provided by the media route service in such a way that the client can
+         * refer to it at a later time.  Ideally, the id should be resilient to
+         * user-initiated actions such as changes to the name or description
+         * of the destination.
+         * </p>
+         */
+        public @NonNull String getId() {
+            return mId;
+        }
+
+        /**
+         * Gets metadata about the service that is providing access to this destination.
+         */
+        public @NonNull ServiceMetadata getServiceMetadata() {
+            return mService;
+        }
+
+        /**
+         * Gets the destination's name for display to the user.
+         */
+        public @NonNull CharSequence getName() {
+            return mName;
+        }
+
+        /**
+         * Gets the destination's description for display to the user, or null if none.
+         */
+        public @Nullable CharSequence getDescription() {
+            return mDescription;
+        }
+
+        /**
+         * Gets an icon resource from the service's package which is used
+         * to identify the destination, or -1 if none.
+         */
+        public @DrawableRes int getIconResourceId() {
+            return mIconResourceId;
+        }
+
+        /**
+         * Loads the icon drawable, or null if none.
+         */
+        public @Nullable Drawable loadIcon(@NonNull PackageManager pm) {
+            return mIconResourceId >= 0 ? mService.getDrawable(pm, mIconResourceId) : null;
+        }
+
+        /**
+         * Gets optional extra information about the destination, or null if none.
+         */
+        public @Nullable Bundle getExtras() {
+            return mExtras;
+        }
+
+        @Override
+        public @NonNull String toString() {
+            return "DestinationInfo{ id=" + mId + ", service=" + mService + ", name=" + mName
+                    + ", description=" + mDescription + ", iconResourceId=" + mIconResourceId
+                    + ", extras=" + mExtras + " }";
+        }
+
+        /**
+         * Builds {@link DestinationInfo} objects.
+         */
+        public static final class Builder {
+            private final String mId;
+            private final ServiceMetadata mService;
+            private final CharSequence mName;
+            private CharSequence mDescription;
+            private int mIconResourceId = -1;
+            private Bundle mExtras;
+
+            /**
+             * Creates a builder for destination information.
+             *
+             * @param id The destination's stable identifier.
+             * @param service Metatada about the service that is providing access to
+             * this destination.
+             * @param name The destination's name for display to the user.
+             */
+            public Builder(@NonNull String id, @NonNull ServiceMetadata service,
+                    @NonNull CharSequence name) {
+                if (TextUtils.isEmpty(id)) {
+                    throw new IllegalArgumentException("id must not be null or empty");
+                }
+                if (service == null) {
+                    throw new IllegalArgumentException("service must not be null");
+                }
+                if (TextUtils.isEmpty(name)) {
+                    throw new IllegalArgumentException("name must not be null or empty");
+                }
+                mId = id;
+                mService = service;
+                mName = name;
+            }
+
+            /**
+             * Sets the destination's description for display to the user, or null if none.
+             */
+            public @NonNull Builder setDescription(@Nullable CharSequence description) {
+                mDescription = description;
+                return this;
+            }
+
+            /**
+             * Sets an icon resource from this package used to identify the destination,
+             * or -1 if none.
+             */
+            public @NonNull Builder setIconResourceId(@DrawableRes int resid) {
+                mIconResourceId = resid;
+                return this;
+            }
+
+            /**
+             * Gets optional extra information about the destination, or null if none.
+             */
+            public @NonNull Builder setExtras(@Nullable Bundle extras) {
+                mExtras = extras;
+                return this;
+            }
+
+            /**
+             * Builds the {@link DestinationInfo} object.
+             */
+            public @NonNull DestinationInfo build() {
+                return new DestinationInfo(mId, mService, mName, mDescription,
+                        mIconResourceId, mExtras);
+            }
+        }
+    }
+
+    /**
+     * Describes metadata about a {@link MediaRouteService} which is providing
+     * access to certain kinds of destinations.
+     */
+    public static final class ServiceMetadata {
+        private final ServiceInfo mService;
+        private CharSequence mLabel;
+        private Drawable mIcon;
+
+        ServiceMetadata(Service service) throws NameNotFoundException {
+            mService = service.getPackageManager().getServiceInfo(
+                    new ComponentName(service, service.getClass()),
+                    PackageManager.GET_META_DATA);
+        }
+
+        ServiceMetadata(ServiceInfo service) {
+            mService = service;
+        }
+
+        /**
+         * Gets the service's component information including it name, label and icon.
+         */
+        public @NonNull ServiceInfo getService() {
+            return mService;
+        }
+
+        /**
+         * Gets the service's component name.
+         */
+        public @NonNull ComponentName getComponentName() {
+            return new ComponentName(mService.packageName, mService.name);
+        }
+
+        /**
+         * Gets the service's package name.
+         */
+        public @NonNull String getPackageName() {
+            return mService.packageName;
+        }
+
+        /**
+         * Gets the service's name for display to the user, or null if none.
+         */
+        public @NonNull CharSequence getLabel(@NonNull PackageManager pm) {
+            if (mLabel == null) {
+                mLabel = mService.loadLabel(pm);
+            }
+            return mLabel;
+        }
+
+        /**
+         * Gets the icon drawable, or null if none.
+         */
+        public @Nullable Drawable getIcon(@NonNull PackageManager pm) {
+            if (mIcon == null) {
+                mIcon = mService.loadIcon(pm);
+            }
+            return mIcon;
+        }
+
+        // TODO: add service metadata
+
+        Drawable getDrawable(PackageManager pm, int resid) {
+            return pm.getDrawable(getPackageName(), resid, mService.applicationInfo);
+        }
+
+        @Override
+        public @NonNull String toString() {
+            return "ServiceInfo{ service=" + getComponentName().toShortString() + " }";
+        }
+    }
+
+    /**
+     * Describes a request to discover routes on behalf of an application.
+     */
+    public static final class DiscoveryRequest {
+        private final ArrayList<MediaRouteSelector> mSelectors =
+                new ArrayList<MediaRouteSelector>();
+        private int mFlags;
+
+        DiscoveryRequest(@NonNull List<MediaRouteSelector> selectors) {
+            setSelectors(selectors);
+        }
+
+        /**
+         * Sets the list of media route selectors to consider during discovery.
+         */
+        public void setSelectors(@NonNull List<MediaRouteSelector> selectors) {
+            if (selectors == null) {
+                throw new IllegalArgumentException("selectors");
+            }
+            mSelectors.clear();
+            mSelectors.addAll(selectors);
+        }
+
+        /**
+         * Gets the list of media route selectors to consider during discovery.
+         */
+        public @NonNull List<MediaRouteSelector> getSelectors() {
+            return mSelectors;
+        }
+
+        /**
+         * Gets discovery flags, such as {@link MediaRouter#DISCOVERY_FLAG_BACKGROUND}.
+         */
+        public @DiscoveryFlags int getFlags() {
+            return mFlags;
+        }
+
+        /**
+         * Sets discovery flags, such as {@link MediaRouter#DISCOVERY_FLAG_BACKGROUND}.
+         */
+        public void setFlags(@DiscoveryFlags int flags) {
+            mFlags = flags;
+        }
+
+        @Override
+        public @NonNull String toString() {
+            return "DiscoveryRequest{ selectors=" + mSelectors
+                    + ", flags=0x" + Integer.toHexString(mFlags)
+                    + " }";
+        }
+    }
+
+    /**
+     * Describes a request to connect to a previously discovered route on
+     * behalf of an application.
+     */
+    public static final class ConnectionRequest {
+        private RouteInfo mRoute;
+        private int mFlags;
+        private Bundle mExtras;
+
+        ConnectionRequest(@NonNull RouteInfo route) {
+            setRoute(route);
+        }
+
+        /**
+         * Gets the route to which to connect.
+         */
+        public @NonNull RouteInfo getRoute() {
+            return mRoute;
+        }
+
+        /**
+         * Sets the route to which to connect.
+         */
+        public void setRoute(@NonNull RouteInfo route) {
+            if (route == null) {
+                throw new IllegalArgumentException("route must not be null");
+            }
+            mRoute = route;
+        }
+
+        /**
+         * Gets connection flags, such as {@link MediaRouter#CONNECTION_FLAG_BARGE}.
+         */
+        public @ConnectionFlags int getFlags() {
+            return mFlags;
+        }
+
+        /**
+         * Sets connection flags, such as {@link MediaRouter#CONNECTION_FLAG_BARGE}.
+         */
+        public void setFlags(@ConnectionFlags int flags) {
+            mFlags = flags;
+        }
+
+        /**
+         * Gets optional extras supplied by the application as part of the call to
+         * connect, or null if none.  The media route service may use this
+         * information to configure the route during connection.
+         */
+        public @Nullable Bundle getExtras() {
+            return mExtras;
+        }
+
+        /**
+         * Sets optional extras supplied by the application as part of the call to
+         * connect, or null if none.  The media route service may use this
+         * information to configure the route during connection.
+         */
+        public void setExtras(@Nullable Bundle extras) {
+            mExtras = extras;
+        }
+
+        @Override
+        public @NonNull String toString() {
+            return "ConnectionRequest{ route=" + mRoute
+                    + ", flags=0x" + Integer.toHexString(mFlags)
+                    + ", extras=" + mExtras + " }";
+        }
+    }
+
+    /**
+     * Callback interface to specify policy for route discovery, filtering,
+     * and connection establishment as well as observe media router state changes.
+     */
+    public static abstract class RoutingCallback extends StateCallback {
+        /**
+         * Called to prepare a discovery request object to specify the desired
+         * media route selectors when the media router has been asked to start discovery.
+         * <p>
+         * By default, the discovery request contains all of the selectors which
+         * have been added to the media router.  Subclasses may override the list of
+         * selectors by modifying the discovery request object before returning.
+         * </p>
+         *
+         * @param request The discovery request object which may be modified by
+         * this method to alter how discovery will be performed.
+         * @param selectors The immutable list of media route selectors which were
+         * added to the media router.
+         * @return True to allow discovery to proceed or false to abort it.
+         * By default, this methods returns true.
+         */
+        public boolean onPrepareDiscoveryRequest(@NonNull DiscoveryRequest request,
+                @NonNull List<MediaRouteSelector> selectors) {
+            return true;
+        }
+
+        /**
+         * Called to prepare a connection request object to specify the desired
+         * route and connection parameters when the media router has been asked to
+         * connect to a particular destination.
+         * <p>
+         * By default, the connection request specifies the first available route
+         * to the destination.  Subclasses may override the route and destination
+         * or set additional connection parameters by modifying the connection request
+         * object before returning.
+         * </p>
+         *
+         * @param request The connection request object which may be modified by
+         * this method to alter how the connection will be established.
+         * @param destination The destination to which the media router was asked
+         * to connect.
+         * @param routes The list of routes that belong to that destination sorted
+         * in the same order as their matching media route selectors which were
+         * used during discovery.
+         * @return True to allow the connection to proceed or false to abort it.
+         * By default, this methods returns true.
+         */
+        public boolean onPrepareConnectionRequest(
+                @NonNull ConnectionRequest request,
+                @NonNull DestinationInfo destination, @NonNull List<RouteInfo> routes) {
+            return true;
+        }
+    }
+
+    /**
+     * Callback class to receive events from a {@link MediaRouter.Delegate}.
+     */
+    public static abstract class StateCallback {
+        /**
+         * Called when the media router has been released.
+         */
+        public void onReleased() { }
+
+        /**
+         * Called when the discovery state has changed.
+         *
+         * @param state The new discovery state: one of
+         * {@link #DISCOVERY_STATE_STOPPED} or {@link #DISCOVERY_STATE_STARTED}.
+         */
+        public void onDiscoveryStateChanged(@DiscoveryState int state) { }
+
+        /**
+         * Called when the connection state has changed.
+         *
+         * @param state The new connection state: one of
+         * {@link #CONNECTION_STATE_DISCONNECTED}, {@link #CONNECTION_STATE_CONNECTING}
+         * or {@link #CONNECTION_STATE_CONNECTED}.
+         */
+        public void onConnectionStateChanged(@ConnectionState int state) { }
+
+        /**
+         * Called when the selected destination has changed.
+         *
+         * @param destination The new selected destination, or null if none.
+         */
+        public void onSelectedDestinationChanged(@Nullable DestinationInfo destination) { }
+
+        /**
+         * Called when route discovery has started.
+         */
+        public void onDiscoveryStarted() { }
+
+        /**
+         * Called when route discovery has stopped normally.
+         * <p>
+         * Abnormal termination is reported via {@link #onDiscoveryFailed}.
+         * </p>
+         */
+        public void onDiscoveryStopped() { }
+
+        /**
+         * Called when discovery has failed in a non-recoverable manner.
+         *
+         * @param error The error code: one of
+         * {@link MediaRouter#DISCOVERY_ERROR_UNKNOWN},
+         * {@link MediaRouter#DISCOVERY_ERROR_ABORTED},
+         * or {@link MediaRouter#DISCOVERY_ERROR_NO_CONNECTIVITY}.
+         * @param message The localized error message, or null if none.  This message
+         * may be shown to the user.
+         * @param extras Additional information about the error which a client
+         * may use, or null if none.
+         */
+        public void onDiscoveryFailed(@DiscoveryError int error, @Nullable CharSequence message,
+                @Nullable Bundle extras) { }
+
+        /**
+         * Called when a new destination is found or has changed during discovery.
+         * <p>
+         * Certain destinations may be omitted because they have been filtered
+         * out by the media router's routing callback.
+         * </p>
+         *
+         * @param destination The destination that was found.
+         */
+        public void onDestinationFound(@NonNull DestinationInfo destination) { }
+
+        /**
+         * Called when a destination is no longer reachable or is no longer
+         * offering any routes that satisfy the discovery request.
+         *
+         * @param destination The destination that went away.
+         */
+        public void onDestinationLost(@NonNull DestinationInfo destination) { }
+
+        /**
+         * Called when a connection attempt begins.
+         */
+        public void onConnecting() { }
+
+        /**
+         * Called when the connection succeeds.
+         */
+        public void onConnected() { }
+
+        /**
+         * Called when the connection is terminated normally.
+         * <p>
+         * Abnormal termination is reported via {@link #onConnectionFailed}.
+         * </p>
+         */
+        public void onDisconnected() { }
+
+        /**
+         * Called when a connection attempt or connection in
+         * progress has failed in a non-recoverable manner.
+         *
+         * @param error The error code: one of
+         * {@link MediaRouter#CONNECTION_ERROR_ABORTED},
+         * {@link MediaRouter#CONNECTION_ERROR_UNAUTHORIZED},
+         * {@link MediaRouter#CONNECTION_ERROR_UNREACHABLE},
+         * {@link MediaRouter#CONNECTION_ERROR_BUSY},
+         * {@link MediaRouter#CONNECTION_ERROR_TIMEOUT},
+         * {@link MediaRouter#CONNECTION_ERROR_BROKEN},
+         * or {@link MediaRouter#CONNECTION_ERROR_BARGED}.
+         * @param message The localized error message, or null if none.  This message
+         * may be shown to the user.
+         * @param extras Additional information about the error which a client
+         * may use, or null if none.
+         */
+        public void onConnectionFailed(@ConnectionError int error,
+                @Nullable CharSequence message, @Nullable Bundle extras) { }
+    }
+}
diff --git a/media/java/android/media/session/RouteEvent.aidl b/media/java/android/media/routing/ParcelableConnectionInfo.aidl
similarity index 89%
copy from media/java/android/media/session/RouteEvent.aidl
copy to media/java/android/media/routing/ParcelableConnectionInfo.aidl
index 6966207..4a9ec94 100644
--- a/media/java/android/media/session/RouteEvent.aidl
+++ b/media/java/android/media/routing/ParcelableConnectionInfo.aidl
@@ -13,6 +13,6 @@
 ** limitations under the License.
 */
 
-package android.media.session;
+package android.media.routing;
 
-parcelable RouteEvent;
+parcelable ParcelableConnectionInfo;
diff --git a/media/java/android/media/routing/ParcelableConnectionInfo.java b/media/java/android/media/routing/ParcelableConnectionInfo.java
new file mode 100644
index 0000000..45cfe9f
--- /dev/null
+++ b/media/java/android/media/routing/ParcelableConnectionInfo.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.routing;
+
+import android.media.AudioAttributes;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Internal parcelable representation of a media route connection.
+ */
+class ParcelableConnectionInfo implements Parcelable {
+    public AudioAttributes audioAttributes;
+    public int presentationDisplayId = -1;
+    // todo: volume
+    public IBinder[] protocolBinders;
+    public Bundle extras;
+
+    public static final Parcelable.Creator<ParcelableConnectionInfo> CREATOR =
+            new Parcelable.Creator<ParcelableConnectionInfo>() {
+        @Override
+        public ParcelableConnectionInfo createFromParcel(Parcel source) {
+            ParcelableConnectionInfo info = new ParcelableConnectionInfo();
+            if (source.readInt() != 0) {
+                info.audioAttributes = AudioAttributes.CREATOR.createFromParcel(source);
+            }
+            info.presentationDisplayId = source.readInt();
+            info.protocolBinders = source.createBinderArray();
+            info.extras = source.readBundle();
+            return info;
+        }
+
+        @Override
+        public ParcelableConnectionInfo[] newArray(int size) {
+            return new ParcelableConnectionInfo[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (audioAttributes != null) {
+            dest.writeInt(1);
+            audioAttributes.writeToParcel(dest, flags);
+        } else {
+            dest.writeInt(0);
+        }
+        dest.writeInt(presentationDisplayId);
+        dest.writeBinderArray(protocolBinders);
+        dest.writeBundle(extras);
+    }
+}
diff --git a/media/java/android/media/session/RouteEvent.aidl b/media/java/android/media/routing/ParcelableDestinationInfo.aidl
similarity index 89%
copy from media/java/android/media/session/RouteEvent.aidl
copy to media/java/android/media/routing/ParcelableDestinationInfo.aidl
index 6966207..bf1c198 100644
--- a/media/java/android/media/session/RouteEvent.aidl
+++ b/media/java/android/media/routing/ParcelableDestinationInfo.aidl
@@ -13,6 +13,6 @@
 ** limitations under the License.
 */
 
-package android.media.session;
+package android.media.routing;
 
-parcelable RouteEvent;
+parcelable ParcelableDestinationInfo;
diff --git a/media/java/android/media/routing/ParcelableDestinationInfo.java b/media/java/android/media/routing/ParcelableDestinationInfo.java
new file mode 100644
index 0000000..eca5eec
--- /dev/null
+++ b/media/java/android/media/routing/ParcelableDestinationInfo.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.routing;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+/**
+ * Internal parcelable representation of a media destination.
+ */
+class ParcelableDestinationInfo implements Parcelable {
+    public String id;
+    public CharSequence name;
+    public CharSequence description;
+    public int iconResourceId;
+    public Bundle extras;
+
+    public static final Parcelable.Creator<ParcelableDestinationInfo> CREATOR =
+            new Parcelable.Creator<ParcelableDestinationInfo>() {
+        @Override
+        public ParcelableDestinationInfo createFromParcel(Parcel source) {
+            ParcelableDestinationInfo info = new ParcelableDestinationInfo();
+            info.id = source.readString();
+            info.name = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+            info.description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+            info.iconResourceId = source.readInt();
+            info.extras = source.readBundle();
+            return info;
+        }
+
+        @Override
+        public ParcelableDestinationInfo[] newArray(int size) {
+            return new ParcelableDestinationInfo[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(id);
+        TextUtils.writeToParcel(name, dest, flags);
+        TextUtils.writeToParcel(description, dest, flags);
+        dest.writeInt(iconResourceId);
+        dest.writeBundle(extras);
+    }
+}
diff --git a/media/java/android/media/session/RouteEvent.aidl b/media/java/android/media/routing/ParcelableRouteInfo.aidl
similarity index 90%
rename from media/java/android/media/session/RouteEvent.aidl
rename to media/java/android/media/routing/ParcelableRouteInfo.aidl
index 6966207..126afaa 100644
--- a/media/java/android/media/session/RouteEvent.aidl
+++ b/media/java/android/media/routing/ParcelableRouteInfo.aidl
@@ -13,6 +13,6 @@
 ** limitations under the License.
 */
 
-package android.media.session;
+package android.media.routing;
 
-parcelable RouteEvent;
+parcelable ParcelableRouteInfo;
diff --git a/media/java/android/media/routing/ParcelableRouteInfo.java b/media/java/android/media/routing/ParcelableRouteInfo.java
new file mode 100644
index 0000000..fb1a547
--- /dev/null
+++ b/media/java/android/media/routing/ParcelableRouteInfo.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.routing;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Internal parcelable representation of a media route.
+ */
+class ParcelableRouteInfo implements Parcelable {
+    public String id;
+    public int selectorIndex; // index of selector within list used for discovery
+    public int features;
+    public String[] protocols;
+    public Bundle extras;
+
+    public static final Parcelable.Creator<ParcelableRouteInfo> CREATOR =
+            new Parcelable.Creator<ParcelableRouteInfo>() {
+        @Override
+        public ParcelableRouteInfo createFromParcel(Parcel source) {
+            ParcelableRouteInfo info = new ParcelableRouteInfo();
+            info.id = source.readString();
+            info.selectorIndex = source.readInt();
+            info.features = source.readInt();
+            info.protocols = source.createStringArray();
+            info.extras = source.readBundle();
+            return info;
+        }
+
+        @Override
+        public ParcelableRouteInfo[] newArray(int size) {
+            return new ParcelableRouteInfo[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(id);
+        dest.writeInt(selectorIndex);
+        dest.writeInt(features);
+        dest.writeStringArray(protocols);
+        dest.writeBundle(extras);
+    }
+}
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index 5bc0de4..a92350b 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -17,10 +17,8 @@
 
 import android.content.ComponentName;
 import android.media.MediaMetadata;
+import android.media.routing.IMediaRouter;
 import android.media.session.ISessionController;
-import android.media.session.RouteOptions;
-import android.media.session.RouteCommand;
-import android.media.session.RouteInfo;
 import android.media.session.PlaybackState;
 import android.os.Bundle;
 import android.os.ResultReceiver;
@@ -34,17 +32,10 @@
     ISessionController getController();
     void setFlags(int flags);
     void setActive(boolean active);
+    void setMediaRouter(in IMediaRouter router);
     void setMediaButtonReceiver(in ComponentName mbr);
     void destroy();
 
-    // These commands are for setting up and communicating with routes
-    // Returns true if the route was set for this session
-    boolean setRoute(in RouteInfo route);
-    void setRouteOptions(in List<RouteOptions> options);
-    void connectToRoute(in RouteInfo route, in RouteOptions options);
-    void disconnectFromRoute(in RouteInfo route);
-    void sendRouteCommand(in RouteCommand event, in ResultReceiver cb);
-
     // These commands are for the TransportPerformer
     void setMetadata(in MediaMetadata metadata);
     void setPlaybackState(in PlaybackState state);
@@ -53,4 +44,4 @@
     // These commands relate to volume handling
     void configureVolumeHandling(int type, int arg1, int arg2);
     void setCurrentVolume(int currentVolume);
-}
\ No newline at end of file
+}
diff --git a/media/java/android/media/session/ISessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl
index 0316d1fa..e554e27 100644
--- a/media/java/android/media/session/ISessionCallback.aidl
+++ b/media/java/android/media/session/ISessionCallback.aidl
@@ -16,9 +16,6 @@
 package android.media.session;
 
 import android.media.Rating;
-import android.media.session.RouteEvent;
-import android.media.session.RouteInfo;
-import android.media.session.RouteOptions;
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.ResultReceiver;
@@ -29,11 +26,6 @@
 oneway interface ISessionCallback {
     void onCommand(String command, in Bundle extras, in ResultReceiver cb);
     void onMediaButton(in Intent mediaButtonIntent, int sequenceNumber, in ResultReceiver cb);
-    void onRequestRouteChange(in RouteInfo route);
-    void onRouteConnected(in RouteInfo route, in RouteOptions options);
-    void onRouteDisconnected(in RouteInfo route, int reason);
-    void onRouteStateChange(int state);
-    void onRouteEvent(in RouteEvent event);
 
     // These callbacks are for the TransportPerformer
     void onPlay();
@@ -49,4 +41,4 @@
     // These callbacks are for volume handling
     void onAdjustVolumeBy(int delta);
     void onSetVolumeTo(int value);
-}
\ No newline at end of file
+}
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index b4c11f6..6cf5ef2 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -18,6 +18,8 @@
 import android.content.Intent;
 import android.media.MediaMetadata;
 import android.media.Rating;
+import android.media.routing.IMediaRouterDelegate;
+import android.media.routing.IMediaRouterStateCallback;
 import android.media.session.ISessionControllerCallback;
 import android.media.session.MediaSessionInfo;
 import android.media.session.ParcelableVolumeInfo;
@@ -36,14 +38,15 @@
     void registerCallbackListener(in ISessionControllerCallback cb);
     void unregisterCallbackListener(in ISessionControllerCallback cb);
     boolean isTransportControlEnabled();
-    void showRoutePicker();
     MediaSessionInfo getSessionInfo();
     long getFlags();
     ParcelableVolumeInfo getVolumeAttributes();
     void adjustVolumeBy(int delta, int flags);
     void setVolumeTo(int value, int flags);
 
-    // These commands are for the TransportController
+    IMediaRouterDelegate createMediaRouterDelegate(IMediaRouterStateCallback callback);
+
+    // These commands are for the TransportControls
     void play();
     void pause();
     void stop();
@@ -56,4 +59,4 @@
     MediaMetadata getMetadata();
     PlaybackState getPlaybackState();
     int getRatingType();
-}
\ No newline at end of file
+}
diff --git a/media/java/android/media/session/ISessionControllerCallback.aidl b/media/java/android/media/session/ISessionControllerCallback.aidl
index baa1379..64d2bc7 100644
--- a/media/java/android/media/session/ISessionControllerCallback.aidl
+++ b/media/java/android/media/session/ISessionControllerCallback.aidl
@@ -16,7 +16,6 @@
 package android.media.session;
 
 import android.media.MediaMetadata;
-import android.media.session.RouteInfo;
 import android.media.session.ParcelableVolumeInfo;
 import android.media.session.PlaybackState;
 import android.os.Bundle;
@@ -26,10 +25,9 @@
  */
 oneway interface ISessionControllerCallback {
     void onEvent(String event, in Bundle extras);
-    void onRouteChanged(in RouteInfo route);
 
     // These callbacks are for the TransportController
     void onPlaybackStateChanged(in PlaybackState state);
     void onMetadataChanged(in MediaMetadata metadata);
     void onVolumeInfoChanged(in ParcelableVolumeInfo info);
-}
\ No newline at end of file
+}
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index edb69bc..cc8b31a 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -22,6 +22,7 @@
 import android.media.MediaMetadata;
 import android.media.Rating;
 import android.media.VolumeProvider;
+import android.media.routing.MediaRouter;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -52,8 +53,7 @@
     private static final int MSG_EVENT = 1;
     private static final int MSG_UPDATE_PLAYBACK_STATE = 2;
     private static final int MSG_UPDATE_METADATA = 3;
-    private static final int MSG_ROUTE = 4;
-    private static final int MSG_UPDATE_VOLUME = 5;
+    private static final int MSG_UPDATE_VOLUME = 4;
 
     private final ISessionController mSessionBinder;
 
@@ -64,11 +64,11 @@
     private boolean mCbRegistered = false;
     private MediaSessionInfo mInfo;
 
-    private TransportControls mTransportController;
+    private final TransportControls mTransportControls;
 
     private MediaController(ISessionController sessionBinder) {
         mSessionBinder = sessionBinder;
-        mTransportController = new TransportControls();
+        mTransportControls = new TransportControls();
     }
 
     /**
@@ -91,12 +91,24 @@
     }
 
     /**
-     * Get a {@link TransportControls} instance for this session.
+     * Get a {@link TransportControls} instance to send transport actions to
+     * the associated session.
      *
-     * @return A controls instance
+     * @return A transport controls instance.
      */
     public @NonNull TransportControls getTransportControls() {
-        return mTransportController;
+        return mTransportControls;
+    }
+
+    /**
+     * Creates a media router delegate through which the destination of the media
+     * router may be observed and controlled.
+     *
+     * @return The media router delegate, or null if the media session does
+     * not support media routing.
+     */
+    public @Nullable MediaRouter.Delegate createMediaRouterDelegate() {
+        return new MediaRouter.Delegate();
     }
 
     /**
@@ -308,20 +320,6 @@
     }
 
     /**
-     * Request that the route picker be shown for this session. This should
-     * generally be called in response to a user action.
-     *
-     * @hide
-     */
-    public void showRoutePicker() {
-        try {
-            mSessionBinder.showRoutePicker();
-        } catch (RemoteException e) {
-            Log.d(TAG, "Dead object in showRoutePicker", e);
-        }
-    }
-
-    /**
      * Get the info for the session this controller is connected to.
      *
      * @return The session info for the connected session.
@@ -421,15 +419,6 @@
         }
 
         /**
-         * Override to handle route changes for this session.
-         *
-         * @param route The new route
-         * @hide
-         */
-        public void onRouteChanged(RouteInfo route) {
-        }
-
-        /**
          * Override to handle changes in playback state.
          *
          * @param state The new playback state of the session
@@ -670,14 +659,6 @@
         }
 
         @Override
-        public void onRouteChanged(RouteInfo route) {
-            MediaController controller = mController.get();
-            if (controller != null) {
-                controller.postMessage(MSG_ROUTE, route, null);
-            }
-        }
-
-        @Override
         public void onPlaybackStateChanged(PlaybackState state) {
             MediaController controller = mController.get();
             if (controller != null) {
@@ -719,9 +700,6 @@
                 case MSG_EVENT:
                     mCallback.onSessionEvent((String) msg.obj, msg.getData());
                     break;
-                case MSG_ROUTE:
-                    mCallback.onRouteChanged((RouteInfo) msg.obj);
-                    break;
                 case MSG_UPDATE_PLAYBACK_STATE:
                     mCallback.onPlaybackStateChanged((PlaybackState) msg.obj);
                     break;
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 2cbdc96..34997bd 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -25,6 +25,7 @@
 import android.media.MediaMetadata;
 import android.media.Rating;
 import android.media.VolumeProvider;
+import android.media.routing.MediaRouter;
 import android.media.session.ISessionController;
 import android.media.session.ISession;
 import android.media.session.ISessionCallback;
@@ -93,40 +94,6 @@
     public static final int FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 1 << 16;
 
     /**
-     * Indicates the session was disconnected because the user that the session
-     * belonged to is stopping.
-     *
-     * @hide
-     */
-    public static final int DISCONNECT_REASON_USER_STOPPING = 1;
-
-    /**
-     * Indicates the session was disconnected because the provider disconnected
-     * the route.
-     * @hide
-     */
-    public static final int DISCONNECT_REASON_PROVIDER_DISCONNECTED = 2;
-
-    /**
-     * Indicates the session was disconnected because the route has changed.
-     * @hide
-     */
-    public static final int DISCONNECT_REASON_ROUTE_CHANGED = 3;
-
-    /**
-     * Indicates the session was disconnected because the session owner
-     * requested it disconnect.
-     * @hide
-     */
-    public static final int DISCONNECT_REASON_SESSION_DISCONNECTED = 4;
-
-    /**
-     * Indicates the session was disconnected because it was destroyed.
-     * @hide
-     */
-    public static final int DISCONNECT_REASON_SESSION_DESTROYED = 5;
-
-    /**
      * The session uses local playback.
      */
     public static final int PLAYBACK_TYPE_LOCAL = 1;
@@ -146,11 +113,7 @@
             = new ArrayList<CallbackMessageHandler>();
     private final ArrayList<TransportMessageHandler> mTransportCallbacks
             = new ArrayList<TransportMessageHandler>();
-    // TODO route interfaces
-    private final ArrayMap<String, RouteInterface.EventListener> mInterfaceListeners
-            = new ArrayMap<String, RouteInterface.EventListener>();
 
-    private Route mRoute;
     private VolumeProvider mVolumeProvider;
 
     private boolean mActive = false;
@@ -228,6 +191,23 @@
     }
 
     /**
+     * Associates a {@link MediaRouter} with this session to control the destination
+     * of media content.
+     * <p>
+     * A media router may only be associated with at most one session at a time.
+     * </p>
+     *
+     * @param router The media router, or null to remove the current association.
+     */
+    public void setMediaRouter(@Nullable MediaRouter router) {
+        try {
+            mBinder.setMediaRouter(router != null ? router.getBinder() : null);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Failure in setMediaButtonReceiver.", e);
+        }
+    }
+
+    /**
      * Set a media button event receiver component to use to restart playback
      * after an app has been stopped.
      *
@@ -377,90 +357,6 @@
     }
 
     /**
-     * Connect to the current route using the specified request.
-     * <p>
-     * Connection updates will be sent to the callback's
-     * {@link Callback#onRouteConnected(Route)} and
-     * {@link Callback#onRouteDisconnected(Route, int)} methods. If the
-     * connection fails {@link Callback#onRouteDisconnected(Route, int)} will be
-     * called.
-     * <p>
-     * If you already have a connection to this route it will be disconnected
-     * before the new connection is established. TODO add an easy way to compare
-     * MediaRouteOptions.
-     *
-     * @param route The route the app is trying to connect to.
-     * @param request The connection request to use.
-     * @hide
-     */
-    public void connect(RouteInfo route, RouteOptions request) {
-        if (route == null) {
-            throw new IllegalArgumentException("Must specify the route");
-        }
-        if (request == null) {
-            throw new IllegalArgumentException("Must specify the connection request");
-        }
-        try {
-            mBinder.connectToRoute(route, request);
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Error starting connection to route", e);
-        }
-    }
-
-    /**
-     * Disconnect from the current route. After calling you will be switched
-     * back to the default route.
-     *
-     * @hide
-     */
-    public void disconnect() {
-        if (mRoute != null) {
-            try {
-                mBinder.disconnectFromRoute(mRoute.getRouteInfo());
-            } catch (RemoteException e) {
-                Log.wtf(TAG, "Error disconnecting from route");
-            }
-        }
-    }
-
-    /**
-     * Set the list of route options your app is interested in connecting to. It
-     * will be used for picking valid routes.
-     *
-     * @param options The set of route options your app may use to connect.
-     * @hide
-     */
-    public void setRouteOptions(List<RouteOptions> options) {
-        try {
-            mBinder.setRouteOptions(options);
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Error setting route options.", e);
-        }
-    }
-
-    /**
-     * @hide
-     * TODO allow multiple listeners for the same interface, allow removal
-     */
-    public void addInterfaceListener(String iface,
-            RouteInterface.EventListener listener) {
-        mInterfaceListeners.put(iface, listener);
-    }
-
-    /**
-     * @hide
-     */
-    public boolean sendRouteCommand(RouteCommand command, ResultReceiver cb) {
-        try {
-            mBinder.sendRouteCommand(command, cb);
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Error sending command to route.", e);
-            return false;
-        }
-        return true;
-    }
-
-    /**
      * Add a callback to receive transport controls on, such as play, rewind, or
      * fast forward.
      *
@@ -670,34 +566,6 @@
         }
     }
 
-    private void postRequestRouteChange(RouteInfo route) {
-        synchronized (mLock) {
-            for (int i = mCallbacks.size() - 1; i >= 0; i--) {
-                mCallbacks.get(i).post(CallbackMessageHandler.MSG_ROUTE_CHANGE, route);
-            }
-        }
-    }
-
-    private void postRouteConnected(RouteInfo route, RouteOptions options) {
-        synchronized (mLock) {
-            mRoute = new Route(route, options, this);
-            for (int i = mCallbacks.size() - 1; i >= 0; i--) {
-                mCallbacks.get(i).post(CallbackMessageHandler.MSG_ROUTE_CONNECTED, mRoute);
-            }
-        }
-    }
-
-    private void postRouteDisconnected(RouteInfo route, int reason) {
-        synchronized (mLock) {
-            if (mRoute != null && TextUtils.equals(mRoute.getRouteInfo().getId(), route.getId())) {
-                for (int i = mCallbacks.size() - 1; i >= 0; i--) {
-                    mCallbacks.get(i).post(CallbackMessageHandler.MSG_ROUTE_DISCONNECTED, mRoute,
-                            reason);
-                }
-            }
-        }
-    }
-
     /**
      * Return true if this is considered an active playback state.
      *
@@ -796,47 +664,6 @@
         public void onControlCommand(@NonNull String command, @Nullable Bundle extras,
                 @Nullable ResultReceiver cb) {
         }
-
-        /**
-         * Called when the user has selected a different route to connect to.
-         * The app is responsible for connecting to the new route and migrating
-         * ongoing playback if necessary.
-         *
-         * @param route
-         * @hide
-         */
-        public void onRequestRouteChange(RouteInfo route) {
-        }
-
-        /**
-         * Called when a route has successfully connected. Calls to the route
-         * are now valid.
-         *
-         * @param route The route that was connected
-         * @hide
-         */
-        public void onRouteConnected(Route route) {
-        }
-
-        /**
-         * Called when a route was disconnected. Further calls to the route will
-         * fail. If available a reason for being disconnected will be provided.
-         * <p>
-         * Valid reasons are:
-         * <ul>
-         * <li>{@link #DISCONNECT_REASON_USER_STOPPING}</li>
-         * <li>{@link #DISCONNECT_REASON_PROVIDER_DISCONNECTED}</li>
-         * <li>{@link #DISCONNECT_REASON_ROUTE_CHANGED}</li>
-         * <li>{@link #DISCONNECT_REASON_SESSION_DISCONNECTED}</li>
-         * <li>{@link #DISCONNECT_REASON_SESSION_DESTROYED}</li>
-         * </ul>
-         *
-         * @param route The route that disconnected
-         * @param reason The reason for the disconnect
-         * @hide
-         */
-        public void onRouteDisconnected(Route route, int reason) {
-        }
     }
 
     /**
@@ -902,17 +729,6 @@
          */
         public void onSetRating(@NonNull Rating rating) {
         }
-
-        /**
-         * Report that audio focus has changed on the app. This only happens if
-         * you have indicated you have started playing with
-         * {@link #setPlaybackState}.
-         *
-         * @param focusChange The type of focus change, TBD.
-         * @hide
-         */
-        public void onRouteFocusChange(int focusChange) {
-        }
     }
 
     /**
@@ -926,8 +742,7 @@
         }
 
         @Override
-        public void onCommand(String command, Bundle extras, ResultReceiver cb)
-                throws RemoteException {
+        public void onCommand(String command, Bundle extras, ResultReceiver cb) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
                 session.postCommand(command, extras, cb);
@@ -935,8 +750,8 @@
         }
 
         @Override
-        public void onMediaButton(Intent mediaButtonIntent, int sequenceNumber, ResultReceiver cb)
-                throws RemoteException {
+        public void onMediaButton(Intent mediaButtonIntent, int sequenceNumber,
+                ResultReceiver cb) {
             MediaSession session = mMediaSession.get();
             try {
                 if (session != null) {
@@ -950,31 +765,7 @@
         }
 
         @Override
-        public void onRequestRouteChange(RouteInfo route) throws RemoteException {
-            MediaSession session = mMediaSession.get();
-            if (session != null) {
-                session.postRequestRouteChange(route);
-            }
-        }
-
-        @Override
-        public void onRouteConnected(RouteInfo route, RouteOptions options) {
-            MediaSession session = mMediaSession.get();
-            if (session != null) {
-                session.postRouteConnected(route, options);
-            }
-        }
-
-        @Override
-        public void onRouteDisconnected(RouteInfo route, int reason) {
-            MediaSession session = mMediaSession.get();
-            if (session != null) {
-                session.postRouteDisconnected(route, reason);
-            }
-        }
-
-        @Override
-        public void onPlay() throws RemoteException {
+        public void onPlay() {
             MediaSession session = mMediaSession.get();
             if (session != null) {
                 session.dispatchPlay();
@@ -982,7 +773,7 @@
         }
 
         @Override
-        public void onPause() throws RemoteException {
+        public void onPause() {
             MediaSession session = mMediaSession.get();
             if (session != null) {
                 session.dispatchPause();
@@ -990,7 +781,7 @@
         }
 
         @Override
-        public void onStop() throws RemoteException {
+        public void onStop() {
             MediaSession session = mMediaSession.get();
             if (session != null) {
                 session.dispatchStop();
@@ -998,7 +789,7 @@
         }
 
         @Override
-        public void onNext() throws RemoteException {
+        public void onNext() {
             MediaSession session = mMediaSession.get();
             if (session != null) {
                 session.dispatchNext();
@@ -1006,7 +797,7 @@
         }
 
         @Override
-        public void onPrevious() throws RemoteException {
+        public void onPrevious() {
             MediaSession session = mMediaSession.get();
             if (session != null) {
                 session.dispatchPrevious();
@@ -1014,7 +805,7 @@
         }
 
         @Override
-        public void onFastForward() throws RemoteException {
+        public void onFastForward() {
             MediaSession session = mMediaSession.get();
             if (session != null) {
                 session.dispatchFastForward();
@@ -1022,7 +813,7 @@
         }
 
         @Override
-        public void onRewind() throws RemoteException {
+        public void onRewind() {
             MediaSession session = mMediaSession.get();
             if (session != null) {
                 session.dispatchRewind();
@@ -1030,7 +821,7 @@
         }
 
         @Override
-        public void onSeekTo(long pos) throws RemoteException {
+        public void onSeekTo(long pos) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
                 session.dispatchSeekTo(pos);
@@ -1038,7 +829,7 @@
         }
 
         @Override
-        public void onRate(Rating rating) throws RemoteException {
+        public void onRate(Rating rating) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
                 session.dispatchRate(rating);
@@ -1046,26 +837,7 @@
         }
 
         @Override
-        public void onRouteEvent(RouteEvent event) throws RemoteException {
-            MediaSession session = mMediaSession.get();
-            if (session != null) {
-                RouteInterface.EventListener iface
-                        = session.mInterfaceListeners.get(event.getIface());
-                Log.d(TAG, "Received route event on iface " + event.getIface() + ". Listener is "
-                        + iface);
-                if (iface != null) {
-                    iface.onEvent(event.getEvent(), event.getExtras());
-                }
-            }
-        }
-
-        @Override
-        public void onRouteStateChange(int state) throws RemoteException {
-            // TODO
-        }
-
-        @Override
-        public void onAdjustVolumeBy(int delta) throws RemoteException {
+        public void onAdjustVolumeBy(int delta) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
                 if (session.mVolumeProvider != null) {
@@ -1075,7 +847,7 @@
         }
 
         @Override
-        public void onSetVolumeTo(int value) throws RemoteException {
+        public void onSetVolumeTo(int value) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
                 if (session.mVolumeProvider != null) {
@@ -1089,9 +861,6 @@
     private class CallbackMessageHandler extends Handler {
         private static final int MSG_MEDIA_BUTTON = 1;
         private static final int MSG_COMMAND = 2;
-        private static final int MSG_ROUTE_CHANGE = 3;
-        private static final int MSG_ROUTE_CONNECTED = 4;
-        private static final int MSG_ROUTE_DISCONNECTED = 5;
 
         private MediaSession.Callback mCallback;
 
@@ -1114,15 +883,6 @@
                         Command cmd = (Command) msg.obj;
                         mCallback.onControlCommand(cmd.command, cmd.extras, cmd.stub);
                         break;
-                    case MSG_ROUTE_CHANGE:
-                        mCallback.onRequestRouteChange((RouteInfo) msg.obj);
-                        break;
-                    case MSG_ROUTE_CONNECTED:
-                        mCallback.onRouteConnected((Route) msg.obj);
-                        break;
-                    case MSG_ROUTE_DISCONNECTED:
-                        mCallback.onRouteDisconnected((Route) msg.obj, msg.arg1);
-                        break;
                 }
             }
         }
diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java
index 5d1a7e02..11f7720 100644
--- a/media/java/android/media/session/MediaSessionLegacyHelper.java
+++ b/media/java/android/media/session/MediaSessionLegacyHelper.java
@@ -202,7 +202,7 @@
                 if (up) {
                     flags = AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE;
                 } else {
-                    flags = AudioManager.FLAG_SHOW_UI;
+                    flags = AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE;
                 }
             }
 
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index c73a8d3..c477406 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -35,9 +35,8 @@
 import java.util.List;
 
 /**
- * MediaSessionManager allows the creation and control of MediaSessions in the
- * system. A MediaSession enables publishing information about ongoing media and
- * interacting with MediaControllers and MediaRoutes.
+ * Provides support for interacting with {@link MediaSession media sessions}
+ * that applications have published to express their ongoing media playback state.
  * <p>
  * Use <code>Context.getSystemService(Context.MEDIA_SESSION_SERVICE)</code> to
  * get an instance of this class.
@@ -256,8 +255,8 @@
     }
 
     /**
-     * Dispatch an adjust volume request to the system. It will be routed to the
-     * most relevant stream/session.
+     * Dispatch an adjust volume request to the system. It will be sent to the
+     * most relevant audio stream or media session.
      *
      * @param suggestedStream The stream to fall back to if there isn't a
      *            relevant stream
@@ -292,8 +291,7 @@
 
         private final IActiveSessionsListener.Stub mStub = new IActiveSessionsListener.Stub() {
             @Override
-            public void onActiveSessionsChanged(List<MediaSession.Token> tokens)
-                    throws RemoteException {
+            public void onActiveSessionsChanged(List<MediaSession.Token> tokens) {
                 ArrayList<MediaController> controllers = new ArrayList<MediaController>();
                 int size = tokens.size();
                 for (int i = 0; i < size; i++) {
diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java
index 6125cb4..f7e7176 100644
--- a/media/java/android/media/session/PlaybackState.java
+++ b/media/java/android/media/session/PlaybackState.java
@@ -157,10 +157,11 @@
 
     /**
      * State indicating the class doing playback is currently connecting to a
-     * route. Depending on the implementation you may return to the previous
-     * state when the connection finishes or enter {@link #STATE_NONE}. If
-     * the connection failed {@link #STATE_ERROR} should be used.
-     * @hide
+     * new destination.  Depending on the implementation you may return to the previous
+     * state when the connection finishes or enter {@link #STATE_NONE}.
+     * If the connection failed {@link #STATE_ERROR} should be used.
+     *
+     * @see #setState
      */
     public final static int STATE_CONNECTING = 8;
 
@@ -183,41 +184,29 @@
      */
     public final static long PLAYBACK_POSITION_UNKNOWN = -1;
 
-    private int mState;
-    private long mPosition;
-    private long mBufferPosition;
-    private float mRate;
-    private long mActions;
-    private CharSequence mErrorMessage;
-    private long mUpdateTime;
+    private final int mState;
+    private final long mPosition;
+    private final long mBufferPosition;
+    private final float mSpeed;
+    private final long mActions;
+    private final CharSequence mErrorMessage;
+    private final long mUpdateTime;
 
-    /**
-     * Create an empty PlaybackState. At minimum a state and actions should be
-     * set before publishing a PlaybackState.
-     */
-    public PlaybackState() {
-    }
-
-    /**
-     * Create a new PlaybackState from an existing PlaybackState. All fields
-     * will be copied to the new state.
-     *
-     * @param from The PlaybackState to duplicate
-     */
-    public PlaybackState(PlaybackState from) {
-        mState = from.mState;
-        mPosition = from.mPosition;
-        mRate = from.mRate;
-        mUpdateTime = from.mUpdateTime;
-        mBufferPosition = from.mBufferPosition;
-        mActions = from.mActions;
-        mErrorMessage = from.mErrorMessage;
+    private PlaybackState(int state, long position, long updateTime, float speed,
+            long bufferPosition, long actions, CharSequence error) {
+        mState = state;
+        mPosition = position;
+        mSpeed = speed;
+        mUpdateTime = updateTime;
+        mBufferPosition = bufferPosition;
+        mActions = actions;
+        mErrorMessage = error;
     }
 
     private PlaybackState(Parcel in) {
         mState = in.readInt();
         mPosition = in.readLong();
-        mRate = in.readFloat();
+        mSpeed = in.readFloat();
         mUpdateTime = in.readLong();
         mBufferPosition = in.readLong();
         mActions = in.readLong();
@@ -231,7 +220,7 @@
         bob.append("state=").append(mState);
         bob.append(", position=").append(mPosition);
         bob.append(", buffered position=").append(mBufferPosition);
-        bob.append(", rate=").append(mRate);
+        bob.append(", speed=").append(mSpeed);
         bob.append(", updated=").append(mUpdateTime);
         bob.append(", actions=").append(mActions);
         bob.append(", error=").append(mErrorMessage);
@@ -248,7 +237,7 @@
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(mState);
         dest.writeLong(mPosition);
-        dest.writeFloat(mRate);
+        dest.writeFloat(mSpeed);
         dest.writeLong(mUpdateTime);
         dest.writeLong(mBufferPosition);
         dest.writeLong(mActions);
@@ -270,41 +259,6 @@
     public int getState() {
         return mState;
     }
-
-    /**
-     * Set the current state of playback.
-     * <p>
-     * The position must be in ms and indicates the current playback position
-     * within the track. If the position is unknown use
-     * {@link #PLAYBACK_POSITION_UNKNOWN}.
-     * <p>
-     * The rate is a multiple of normal playback and should be 0 when paused and
-     * negative when rewinding. Normal playback rate is 1.0.
-     * <p>
-     * The state must be one of the following:
-     * <ul>
-     * <li> {@link PlaybackState#STATE_NONE}</li>
-     * <li> {@link PlaybackState#STATE_STOPPED}</li>
-     * <li> {@link PlaybackState#STATE_PLAYING}</li>
-     * <li> {@link PlaybackState#STATE_PAUSED}</li>
-     * <li> {@link PlaybackState#STATE_FAST_FORWARDING}</li>
-     * <li> {@link PlaybackState#STATE_REWINDING}</li>
-     * <li> {@link PlaybackState#STATE_BUFFERING}</li>
-     * <li> {@link PlaybackState#STATE_ERROR}</li>
-     * </ul>
-     *
-     * @param state The current state of playback.
-     * @param position The position in the current track in ms.
-     * @param playbackRate The current rate of playback as a multiple of normal
-     *            playback.
-     */
-    public void setState(int state, long position, float playbackRate) {
-        this.mState = state;
-        this.mPosition = position;
-        this.mRate = playbackRate;
-        mUpdateTime = SystemClock.elapsedRealtime();
-    }
-
     /**
      * Get the current playback position in ms.
      */
@@ -322,23 +276,14 @@
     }
 
     /**
-     * Set the current buffer position in ms. This is the farthest playback
-     * point that can be reached from the current position using only buffered
-     * content.
-     */
-    public void setBufferPosition(long bufferPosition) {
-        mBufferPosition = bufferPosition;
-    }
-
-    /**
-     * Get the current playback rate as a multiple of normal playback. This
+     * Get the current playback speed as a multiple of normal playback. This
      * should be negative when rewinding. A value of 1 means normal playback and
      * 0 means paused.
      *
-     * @return The current rate of playback.
+     * @return The current speed of playback.
      */
-    public float getPlaybackRate() {
-        return mRate;
+    public float getPlaybackSpeed() {
+        return mSpeed;
     }
 
     /**
@@ -361,25 +306,6 @@
     }
 
     /**
-     * Set the current capabilities available on this session. This should use a
-     * bitmask of the available capabilities.
-     * <ul>
-     * <li> {@link PlaybackState#ACTION_SKIP_TO_PREVIOUS}</li>
-     * <li> {@link PlaybackState#ACTION_REWIND}</li>
-     * <li> {@link PlaybackState#ACTION_PLAY}</li>
-     * <li> {@link PlaybackState#ACTION_PAUSE}</li>
-     * <li> {@link PlaybackState#ACTION_STOP}</li>
-     * <li> {@link PlaybackState#ACTION_FAST_FORWARD}</li>
-     * <li> {@link PlaybackState#ACTION_SKIP_TO_NEXT}</li>
-     * <li> {@link PlaybackState#ACTION_SEEK_TO}</li>
-     * <li> {@link PlaybackState#ACTION_SET_RATING}</li>
-     * </ul>
-     */
-    public void setActions(long capabilities) {
-        mActions = capabilities;
-    }
-
-    /**
      * Get a user readable error message. This should be set when the state is
      * {@link PlaybackState#STATE_ERROR}.
      */
@@ -392,21 +318,12 @@
      * position has never been set this will return 0;
      *
      * @return The last time the position was updated.
-     * @hide
      */
     public long getLastPositionUpdateTime() {
         return mUpdateTime;
     }
 
     /**
-     * Set a user readable error message. This should be set when the state is
-     * {@link PlaybackState#STATE_ERROR}.
-     */
-    public void setErrorMessage(CharSequence errorMessage) {
-        mErrorMessage = errorMessage;
-    }
-
-    /**
      * Get the {@link PlaybackState} state for the given
      * {@link RemoteControlClient} state.
      *
@@ -573,4 +490,175 @@
             return new PlaybackState[size];
         }
     };
+
+    /**
+     * Builder for {@link PlaybackState} objects.
+     */
+    public static final class Builder {
+        private int mState;
+        private long mPosition;
+        private long mBufferPosition;
+        private float mSpeed;
+        private long mActions;
+        private CharSequence mErrorMessage;
+        private long mUpdateTime;
+
+        /**
+         * Creates an initially empty state builder.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Creates a builder with the same initial values as those in the from
+         * state.
+         *
+         * @param from The state to use for initializing the builder.
+         */
+        public Builder(PlaybackState from) {
+            if (from == null) {
+                return;
+            }
+            mState = from.mState;
+            mPosition = from.mPosition;
+            mBufferPosition = from.mBufferPosition;
+            mSpeed = from.mSpeed;
+            mActions = from.mActions;
+            mErrorMessage = from.mErrorMessage;
+            mUpdateTime = from.mUpdateTime;
+        }
+
+        /**
+         * Set the current state of playback.
+         * <p>
+         * The position must be in ms and indicates the current playback
+         * position within the track. If the position is unknown use
+         * {@link #PLAYBACK_POSITION_UNKNOWN}. When not using an unknown
+         * position the time at which the position was updated must be provided.
+         * It is okay to use {@link SystemClock#elapsedRealtime()} if the
+         * current position was just retrieved.
+         * <p>
+         * The speed is a multiple of normal playback and should be 0 when
+         * paused and negative when rewinding. Normal playback speed is 1.0.
+         * <p>
+         * The state must be one of the following:
+         * <ul>
+         * <li> {@link PlaybackState#STATE_NONE}</li>
+         * <li> {@link PlaybackState#STATE_STOPPED}</li>
+         * <li> {@link PlaybackState#STATE_PLAYING}</li>
+         * <li> {@link PlaybackState#STATE_PAUSED}</li>
+         * <li> {@link PlaybackState#STATE_FAST_FORWARDING}</li>
+         * <li> {@link PlaybackState#STATE_REWINDING}</li>
+         * <li> {@link PlaybackState#STATE_BUFFERING}</li>
+         * <li> {@link PlaybackState#STATE_ERROR}</li>
+         * </ul>
+         *
+         * @param state The current state of playback.
+         * @param position The position in the current track in ms.
+         * @param playbackSpeed The current speed of playback as a multiple of
+         *            normal playback.
+         * @param updateTime The time in the {@link SystemClock#elapsedRealtime}
+         *            timebase that the position was updated at.
+         * @return this
+         */
+        public Builder setState(int state, long position, float playbackSpeed, long updateTime) {
+            mState = state;
+            mPosition = position;
+            mUpdateTime = updateTime;
+            mSpeed = playbackSpeed;
+            return this;
+        }
+
+        /**
+         * Set the current state of playback.
+         * <p>
+         * The position must be in ms and indicates the current playback
+         * position within the track. If the position is unknown use
+         * {@link #PLAYBACK_POSITION_UNKNOWN}. The update time will be set to
+         * the current {@link SystemClock#elapsedRealtime()}.
+         * <p>
+         * The speed is a multiple of normal playback and should be 0 when
+         * paused and negative when rewinding. Normal playback speed is 1.0.
+         * <p>
+         * The state must be one of the following:
+         * <ul>
+         * <li> {@link PlaybackState#STATE_NONE}</li>
+         * <li> {@link PlaybackState#STATE_STOPPED}</li>
+         * <li> {@link PlaybackState#STATE_PLAYING}</li>
+         * <li> {@link PlaybackState#STATE_PAUSED}</li>
+         * <li> {@link PlaybackState#STATE_FAST_FORWARDING}</li>
+         * <li> {@link PlaybackState#STATE_REWINDING}</li>
+         * <li> {@link PlaybackState#STATE_BUFFERING}</li>
+         * <li> {@link PlaybackState#STATE_ERROR}</li>
+         * </ul>
+         *
+         * @param state The current state of playback.
+         * @param position The position in the current track in ms.
+         * @param playbackSpeed The current speed of playback as a multiple of
+         *            normal playback.
+         * @return this
+         */
+        public Builder setState(int state, long position, float playbackSpeed) {
+            return setState(state, position, playbackSpeed, SystemClock.elapsedRealtime());
+        }
+
+        /**
+         * Set the current actions available on this session. This should use a
+         * bitmask of possible actions.
+         * <ul>
+         * <li> {@link PlaybackState#ACTION_SKIP_TO_PREVIOUS}</li>
+         * <li> {@link PlaybackState#ACTION_REWIND}</li>
+         * <li> {@link PlaybackState#ACTION_PLAY}</li>
+         * <li> {@link PlaybackState#ACTION_PAUSE}</li>
+         * <li> {@link PlaybackState#ACTION_STOP}</li>
+         * <li> {@link PlaybackState#ACTION_FAST_FORWARD}</li>
+         * <li> {@link PlaybackState#ACTION_SKIP_TO_NEXT}</li>
+         * <li> {@link PlaybackState#ACTION_SEEK_TO}</li>
+         * <li> {@link PlaybackState#ACTION_SET_RATING}</li>
+         * </ul>
+         *
+         * @param actions The set of actions allowed.
+         * @return this
+         */
+        public Builder setActions(long actions) {
+            mActions = actions;
+            return this;
+        }
+
+        /**
+         * Set the current buffer position in ms. This is the farthest playback
+         * point that can be reached from the current position using only
+         * buffered content.
+         *
+         * @param bufferPosition The position in ms that playback is buffered
+         *            to.
+         * @return this
+         */
+        public Builder setBufferPosition(long bufferPosition) {
+            mBufferPosition = bufferPosition;
+            return this;
+        }
+
+        /**
+         * Set a user readable error message. This should be set when the state
+         * is {@link PlaybackState#STATE_ERROR}.
+         *
+         * @param error The error message for display to the user.
+         * @return this
+         */
+        public Builder setErrorMessage(CharSequence error) {
+            mErrorMessage = error;
+            return this;
+        }
+
+        /**
+         * Build and return the PlaybackState instance with these values.
+         *
+         * @return A new state instance.
+         */
+        public PlaybackState build() {
+            return new PlaybackState(mState, mPosition, mUpdateTime, mSpeed, mBufferPosition,
+                    mActions, mErrorMessage);
+        }
+    }
 }
diff --git a/media/java/android/media/session/Route.java b/media/java/android/media/session/Route.java
deleted file mode 100644
index 935eb5b..0000000
--- a/media/java/android/media/session/Route.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.media.session;
-
-import android.text.TextUtils;
-import android.util.Log;
-
-import java.util.List;
-
-/**
- * Represents a destination which an application has connected to and may send
- * media content.
- * <p>
- * This allows a session owner to interact with a route it has been connected
- * to. The MediaRoute must be used to get {@link RouteInterface}
- * instances which can be used to communicate over a specific interface on the
- * route.
- * @hide
- */
-public final class Route {
-    private static final String TAG = "Route";
-    private final RouteInfo mInfo;
-    private final MediaSession mSession;
-    private final RouteOptions mOptions;
-
-    /**
-     * @hide
-     */
-    public Route(RouteInfo info, RouteOptions options, MediaSession session) {
-        if (info == null || options == null) {
-            throw new IllegalStateException("Route info was not valid!");
-        }
-        mInfo = info;
-        mOptions = options;
-        mSession = session;
-    }
-
-    /**
-     * Get the {@link RouteInfo} for this route.
-     *
-     * @return The info for this route.
-     */
-    public RouteInfo getRouteInfo() {
-        return mInfo;
-    }
-
-    /**
-     * Get the {@link RouteOptions} that were used to connect this route.
-     *
-     * @return The options used to connect to this route.
-     */
-    public RouteOptions getOptions() {
-        return mOptions;
-    }
-
-    /**
-     * Gets an interface provided by this route. If the interface is not
-     * supported by the route, returns null.
-     *
-     * @see RouteInterface
-     * @param iface The name of the interface to create
-     * @return A {@link RouteInterface} or null if the interface is
-     *         not supported.
-     */
-    public RouteInterface getInterface(String iface) {
-        if (TextUtils.isEmpty(iface)) {
-            throw new IllegalArgumentException("iface may not be empty.");
-        }
-        List<String> ifaces = mOptions.getInterfaceNames();
-        if (ifaces != null) {
-            for (int i = ifaces.size() - 1; i >= 0; i--) {
-                if (iface.equals(ifaces.get(i))) {
-                    return new RouteInterface(this, iface, mSession);
-                }
-            }
-        }
-        Log.e(TAG, "Interface not supported by route");
-        return null;
-    }
-
-    /**
-     * @hide
-     */
-    MediaSession getSession() {
-        return mSession;
-    }
-}
diff --git a/media/java/android/media/session/RouteCommand.aidl b/media/java/android/media/session/RouteCommand.aidl
deleted file mode 100644
index 725b308..0000000
--- a/media/java/android/media/session/RouteCommand.aidl
+++ /dev/null
@@ -1,18 +0,0 @@
-/* Copyright 2014, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-package android.media.session;
-
-parcelable RouteCommand;
diff --git a/media/java/android/media/session/RouteCommand.java b/media/java/android/media/session/RouteCommand.java
deleted file mode 100644
index 358bc0a..0000000
--- a/media/java/android/media/session/RouteCommand.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.media.session;
-
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Represents a command that an application may send to a route.
- * <p>
- * Commands are associated with a specific route and interface supported by that
- * route and sent through the session. This class isn't used directly by apps.
- *
- * @hide
- */
-public final class RouteCommand implements Parcelable {
-    private final String mRoute;
-    private final String mIface;
-    private final String mEvent;
-    private final Bundle mExtras;
-
-    /**
-     * @param route The id of the route this event is being sent on
-     * @param iface The interface the sender used
-     * @param event The event or command
-     * @param extras Any extras included with the event
-     */
-    public RouteCommand(String route, String iface, String event, Bundle extras) {
-        mRoute = route;
-        mIface = iface;
-        mEvent = event;
-        mExtras = extras;
-    }
-
-    private RouteCommand(Parcel in) {
-        mRoute = in.readString();
-        mIface = in.readString();
-        mEvent = in.readString();
-        mExtras = in.readBundle();
-    }
-
-    /**
-     * Get the id for the route this event was sent on.
-     *
-     * @return The route id this event is using
-     */
-    public String getRouteInfo() {
-        return mRoute;
-    }
-
-    /**
-     * Get the interface this event was sent from
-     *
-     * @return The interface for this event
-     */
-    public String getIface() {
-        return mIface;
-    }
-
-    /**
-     * Get the action/name of the event.
-     *
-     * @return The name of event/command.
-     */
-    public String getEvent() {
-        return mEvent;
-    }
-
-    /**
-     * Get any extras included with the event.
-     *
-     * @return The bundle included with the event or null
-     */
-    public Bundle getExtras() {
-        return mExtras;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeString(mRoute);
-        dest.writeString(mIface);
-        dest.writeString(mEvent);
-        dest.writeBundle(mExtras);
-    }
-
-    public static final Parcelable.Creator<RouteCommand> CREATOR
-            = new Parcelable.Creator<RouteCommand>() {
-        @Override
-        public RouteCommand createFromParcel(Parcel in) {
-            return new RouteCommand(in);
-        }
-
-        @Override
-        public RouteCommand[] newArray(int size) {
-            return new RouteCommand[size];
-        }
-    };
-}
diff --git a/media/java/android/media/session/RouteEvent.java b/media/java/android/media/session/RouteEvent.java
deleted file mode 100644
index 918e410..0000000
--- a/media/java/android/media/session/RouteEvent.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.media.session;
-
-import android.media.routeprovider.RouteConnection;
-import android.media.routeprovider.RouteProviderService;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Represents an event that a route provider is sending to a particular
- * {@link RouteConnection}. Events are associated with a specific interface
- * supported by the connection and sent through the {@link RouteProviderService}.
- * This class isn't used directly by apps.
- *
- * @hide
- */
-public class RouteEvent implements Parcelable {
-    private final IBinder mConnection;
-    private final String mIface;
-    private final String mEvent;
-    private final Bundle mExtras;
-
-    /**
-     * @param connection The connection that this event is for
-     * @param iface The interface the sender used
-     * @param event The event or command
-     * @param extras Any extras included with the event
-     */
-    public RouteEvent(IBinder connection, String iface, String event, Bundle extras) {
-        mConnection = connection;
-        mIface = iface;
-        mEvent = event;
-        mExtras = extras;
-    }
-
-    private RouteEvent(Parcel in) {
-        mConnection = in.readStrongBinder();
-        mIface = in.readString();
-        mEvent = in.readString();
-        mExtras = in.readBundle();
-    }
-
-    /**
-     * Get the connection this event was sent on.
-     *
-     * @return The connection this event is using
-     */
-    public IBinder getConnection() {
-        return mConnection;
-    }
-
-    /**
-     * Get the interface this event was sent from
-     *
-     * @return The interface for this event
-     */
-    public String getIface() {
-        return mIface;
-    }
-
-    /**
-     * Get the action/name of the event.
-     *
-     * @return The name of event/command.
-     */
-    public String getEvent() {
-        return mEvent;
-    }
-
-    /**
-     * Get any extras included with the event.
-     *
-     * @return The bundle included with the event or null
-     */
-    public Bundle getExtras() {
-        return mExtras;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeStrongBinder(mConnection);
-        dest.writeString(mIface);
-        dest.writeString(mEvent);
-        dest.writeBundle(mExtras);
-    }
-
-    public static final Parcelable.Creator<RouteEvent> CREATOR
-            = new Parcelable.Creator<RouteEvent>() {
-        @Override
-        public RouteEvent createFromParcel(Parcel in) {
-            return new RouteEvent(in);
-        }
-
-        @Override
-        public RouteEvent[] newArray(int size) {
-            return new RouteEvent[size];
-        }
-    };
-}
diff --git a/media/java/android/media/session/RouteInfo.aidl b/media/java/android/media/session/RouteInfo.aidl
deleted file mode 100644
index c5f50c8..0000000
--- a/media/java/android/media/session/RouteInfo.aidl
+++ /dev/null
@@ -1,18 +0,0 @@
-/* Copyright 2014, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-package android.media.session;
-
-parcelable RouteInfo;
diff --git a/media/java/android/media/session/RouteInfo.java b/media/java/android/media/session/RouteInfo.java
deleted file mode 100644
index 02f78f9..0000000
--- a/media/java/android/media/session/RouteInfo.java
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.media.session;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Information about a route, including its display name, a way to identify it,
- * and the ways it can be connected to.
- * @hide
- */
-public final class RouteInfo implements Parcelable {
-    private final String mName;
-    private final String mId;
-    private final String mProviderId;
-    private final List<RouteOptions> mOptions;
-
-    private RouteInfo(String id, String name, String providerId,
-            List<RouteOptions> connRequests) {
-        mId = id;
-        mName = name;
-        mProviderId = providerId;
-        mOptions = connRequests;
-    }
-
-    private RouteInfo(Parcel in) {
-        mId = in.readString();
-        mName = in.readString();
-        mProviderId = in.readString();
-        mOptions = new ArrayList<RouteOptions>();
-        in.readTypedList(mOptions, RouteOptions.CREATOR);
-    }
-
-    /**
-     * Get the displayable name of this route.
-     *
-     * @return A short, user readable name for this route
-     */
-    public String getName() {
-        return mName;
-    }
-
-    /**
-     * Get the unique id for this route.
-     *
-     * @return A unique route id.
-     */
-    public String getId() {
-        return mId;
-    }
-
-    /**
-     * Get the package name of this route's provider.
-     *
-     * @return The package name of this route's provider.
-     */
-    public String getProvider() {
-        return mProviderId;
-    }
-
-    /**
-     * Get the set of connections that may be used with this route.
-     *
-     * @return An array of connection requests that may be used to connect
-     */
-    public List<RouteOptions> getConnectionMethods() {
-        return mOptions;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeString(mId);
-        dest.writeString(mName);
-        dest.writeString(mProviderId);
-        dest.writeTypedList(mOptions);
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder bob = new StringBuilder();
-        bob.append("RouteInfo: id=").append(mId).append(", name=").append(mName)
-                .append(", provider=").append(mProviderId).append(", options={");
-        for (int i = 0; i < mOptions.size(); i++) {
-            if (i != 0) {
-                bob.append(", ");
-            }
-            bob.append(mOptions.get(i).toString());
-        }
-        bob.append("}");
-        return bob.toString();
-    }
-
-    public static final Parcelable.Creator<RouteInfo> CREATOR
-            = new Parcelable.Creator<RouteInfo>() {
-        @Override
-        public RouteInfo createFromParcel(Parcel in) {
-            return new RouteInfo(in);
-        }
-
-        @Override
-        public RouteInfo[] newArray(int size) {
-            return new RouteInfo[size];
-        }
-    };
-
-    /**
-     * Helper for creating MediaRouteInfos. A route must have a name and an id.
-     * While options are not strictly required the route cannot be connected to
-     * without at least one set of options.
-     */
-    public static final class Builder {
-        private String mName;
-        private String mId;
-        private String mProviderPackage;
-        private ArrayList<RouteOptions> mOptions;
-
-        /**
-         * Copies an existing route info object. TODO Remove once we have
-         * helpers for creating route infos.
-         *
-         * @param from The existing info to copy.
-         */
-        public Builder(RouteInfo from) {
-            mOptions = new ArrayList<RouteOptions>(from.getConnectionMethods());
-            mName = from.mName;
-            mId = from.mId;
-            mProviderPackage = from.mProviderId;
-        }
-
-        public Builder() {
-            mOptions = new ArrayList<RouteOptions>();
-        }
-
-        /**
-         * Set the user visible name for this route.
-         *
-         * @param name The name of the route
-         * @return The builder for easy chaining.
-         */
-        public Builder setName(String name) {
-            mName = name;
-            return this;
-        }
-
-        /**
-         * Set the id of the route. This should be unique to the provider.
-         *
-         * @param id The unique id of the route.
-         * @return The builder for easy chaining.
-         */
-        public Builder setId(String id) {
-            mId = id;
-            return this;
-        }
-
-        /**
-         * @hide
-         */
-        public Builder setProviderId(String packageName) {
-            mProviderPackage = packageName;
-            return this;
-        }
-
-        /**
-         * Add a set of {@link RouteOptions} to the route. Multiple options
-         * may be added to the same route.
-         *
-         * @param options The options to add to this route.
-         * @return The builder for easy chaining.
-         */
-        public Builder addRouteOptions(RouteOptions options) {
-            mOptions.add(options);
-            return this;
-        }
-
-        /**
-         * Clear the set of {@link RouteOptions} on the route.
-         *
-         * @return The builder for easy chaining
-         */
-        public Builder clearRouteOptions() {
-            mOptions.clear();
-            return this;
-        }
-
-        /**
-         * Build a new MediaRouteInfo.
-         *
-         * @return A new MediaRouteInfo with the values that were set.
-         */
-        public RouteInfo build() {
-            if (TextUtils.isEmpty(mName)) {
-                throw new IllegalArgumentException("Must set a name before building");
-            }
-            if (TextUtils.isEmpty(mId)) {
-                throw new IllegalArgumentException("Must set an id before building");
-            }
-            return new RouteInfo(mId, mName, mProviderPackage, mOptions);
-        }
-
-        /**
-         * Get the current number of options that have been added to this
-         * builder.
-         *
-         * @return The number of options that have been added.
-         */
-        public int getOptionsSize() {
-            return mOptions.size();
-        }
-    }
-}
diff --git a/media/java/android/media/session/RouteInterface.java b/media/java/android/media/session/RouteInterface.java
deleted file mode 100644
index 8de4d89..0000000
--- a/media/java/android/media/session/RouteInterface.java
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.media.session;
-
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.ResultReceiver;
-import android.util.Log;
-
-import java.util.ArrayList;
-
-/**
- * A route can support multiple interfaces for a {@link MediaSession} to
- * interact with. To use a specific interface with a route a
- * MediaSessionRouteInterface needs to be retrieved from the route. An
- * implementation of the specific interface, like
- * {@link RoutePlaybackControls}, should be used to simplify communication
- * and reduce errors on that interface.
- *
- * @see RoutePlaybackControls for an example
- * @hide
- */
-public final class RouteInterface {
-    private static final String TAG = "RouteInterface";
-
-    /**
-     * Error indicating the route is currently not connected.
-     */
-    public static final int RESULT_NOT_CONNECTED = -5;
-    /**
-     * Error indicating the session is no longer using the route this command
-     * was sent to.
-     */
-    public static final int RESULT_ROUTE_IS_STALE = -4;
-    /**
-     * Error indicating that the interface does not support the command.
-     */
-    public static final int RESULT_COMMAND_NOT_SUPPORTED = -3;
-    /**
-     * Error indicating that the route does not support the interface.
-     */
-    public static final int RESULT_INTERFACE_NOT_SUPPORTED = -2;
-    /**
-     * Generic error. Extra information about the error may be included in the
-     * result bundle.
-     */
-    public static final int RESULT_ERROR = -1;
-    /**
-     * The command was successful. Extra information may be included in the
-     * result bundle.
-     */
-    public static final int RESULT_SUCCESS = 1;
-
-    private final Route mRoute;
-    private final String mIface;
-    private final MediaSession mSession;
-
-    private final Object mLock = new Object();
-    private final ArrayList<EventHandler> mListeners = new ArrayList<EventHandler>();
-
-    /**
-     * @hide
-     */
-    RouteInterface(Route route, String iface, MediaSession session) {
-        mRoute = route;
-        mIface = iface;
-        mSession = session;
-        mSession.addInterfaceListener(iface, mEventListener);
-    }
-
-    /**
-     * Send a command using this interface.
-     *
-     * @param command The command to send.
-     * @param extras Any extras to include with the command.
-     * @param cb The callback to receive the result on.
-     * @return true if the command was sent, false otherwise.
-     */
-    public boolean sendCommand(String command, Bundle extras, ResultReceiver cb) {
-        RouteCommand cmd = new RouteCommand(mRoute.getRouteInfo().getId(), mIface,
-                command, extras);
-        return mSession.sendRouteCommand(cmd, cb);
-    }
-
-    /**
-     * Add a listener to this interface. Events will be sent on the caller's
-     * thread.
-     *
-     * @param listener The listener to receive events on.
-     */
-    public void addListener(EventListener listener) {
-        addListener(listener, null);
-    }
-
-    /**
-     * Add a listener for this interface. If a handler is specified events will
-     * be performed on the handler's thread, otherwise the caller's thread will
-     * be used.
-     *
-     * @param listener The listener to receive events on
-     * @param handler The handler whose thread to post calls on
-     */
-    public void addListener(EventListener listener, Handler handler) {
-        if (listener == null) {
-            throw new IllegalArgumentException("listener may not be null");
-        }
-        if (handler == null) {
-            handler = new Handler();
-        }
-        synchronized (mLock) {
-            if (findIndexOfListenerLocked(listener) != -1) {
-                Log.d(TAG, "Listener is already added, ignoring");
-                return;
-            }
-            mListeners.add(new EventHandler(handler.getLooper(), listener));
-        }
-    }
-
-    /**
-     * Remove a listener from this interface.
-     *
-     * @param listener The listener to stop receiving events on.
-     */
-    public void removeListener(EventListener listener) {
-        if (listener == null) {
-            throw new IllegalArgumentException("listener may not be null");
-        }
-        synchronized (mLock) {
-            int index = findIndexOfListenerLocked(listener);
-            if (index != -1) {
-                mListeners.remove(index);
-            }
-        }
-    }
-
-    private int findIndexOfListenerLocked(EventListener listener) {
-        if (listener == null) {
-            throw new IllegalArgumentException("Callback cannot be null");
-        }
-        for (int i = mListeners.size() - 1; i >= 0; i--) {
-            EventHandler handler = mListeners.get(i);
-            if (listener == handler.mListener) {
-                return i;
-            }
-        }
-        return -1;
-    }
-
-    private EventListener mEventListener = new EventListener() {
-            @Override
-        public void onEvent(String event, Bundle args) {
-            synchronized (mLock) {
-                for (int i = mListeners.size() - 1; i >= 0; i--) {
-                    mListeners.get(i).postEvent(event, args);
-                }
-            }
-        }
-
-    };
-
-    /**
-     * An EventListener can be registered by an app with TODO to handle events
-     * sent by the session on a specific interface.
-     */
-    public static abstract class EventListener {
-        /**
-         * This is called when an event is received from the interface. Events
-         * are sent by the session owner and will be delivered to all
-         * controllers that are listening to the interface.
-         *
-         * @param event The event that occurred.
-         * @param args Any extras that were included with the event. May be
-         *            null.
-         */
-        public abstract void onEvent(String event, Bundle args);
-    }
-
-    private static final class EventHandler extends Handler {
-
-        private final EventListener mListener;
-
-        public EventHandler(Looper looper, EventListener cb) {
-            super(looper, null, true);
-            mListener = cb;
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            mListener.onEvent((String) msg.obj, msg.getData());
-        }
-
-        public void postEvent(String event, Bundle args) {
-            Message msg = obtainMessage(0, event);
-            msg.setData(args);
-            msg.sendToTarget();
-        }
-    }
-}
diff --git a/media/java/android/media/session/RouteOptions.aidl b/media/java/android/media/session/RouteOptions.aidl
deleted file mode 100644
index feaf517..0000000
--- a/media/java/android/media/session/RouteOptions.aidl
+++ /dev/null
@@ -1,18 +0,0 @@
-/* Copyright 2014, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-package android.media.session;
-
-parcelable RouteOptions;
diff --git a/media/java/android/media/session/RouteOptions.java b/media/java/android/media/session/RouteOptions.java
deleted file mode 100644
index b4fb341..0000000
--- a/media/java/android/media/session/RouteOptions.java
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.media.session;
-
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Specifies options that an application might use when connecting to a route.
- * This includes things like interfaces, connection parameters, and required
- * features.
- * <p>
- * An application may create several different route options that describe
- * alternative sets of capabilities that it can use and choose the most
- * appropriate route options when it is ready to connect to the route. Each
- * route options instance must specify a complete set of capabilities to request
- * when the connection is established.
- * @hide
- */
-public final class RouteOptions implements Parcelable {
-    private static final String TAG = "RouteOptions";
-
-    private final ArrayList<String> mIfaces;
-    private final Bundle mConnectionParams;
-
-    private RouteOptions(List<String> ifaces, Bundle params) {
-        mIfaces = new ArrayList<String>(ifaces);
-        mConnectionParams = params;
-    }
-
-    private RouteOptions(Parcel in) {
-        mIfaces = new ArrayList<String>();
-        in.readStringList(mIfaces);
-        mConnectionParams = in.readBundle();
-    }
-
-    /**
-     * Get the interfaces this connection wants to use.
-     *
-     * @return The interfaces for this connection
-     */
-    public List<String> getInterfaceNames() {
-        return mIfaces;
-    }
-
-    /**
-     * Get the parameters that will be used for connecting.
-     *
-     * @return The set of connection parameters this connections uses
-     */
-    public Bundle getConnectionParams() {
-        return mConnectionParams;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeStringList(mIfaces);
-        dest.writeBundle(mConnectionParams);
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder bob = new StringBuilder();
-        bob.append("Options: interfaces={");
-        for (int i = 0; i < mIfaces.size(); i++) {
-            if (i != 0) {
-                bob.append(", ");
-            }
-            bob.append(mIfaces.get(i));
-        }
-        bob.append("}");
-        bob.append(", parameters=");
-        bob.append(mConnectionParams == null ? "null" : mConnectionParams.toString());
-        return bob.toString();
-    }
-
-    public static final Parcelable.Creator<RouteOptions> CREATOR
-            = new Parcelable.Creator<RouteOptions>() {
-        @Override
-        public RouteOptions createFromParcel(Parcel in) {
-            return new RouteOptions(in);
-        }
-
-        @Override
-        public RouteOptions[] newArray(int size) {
-            return new RouteOptions[size];
-        }
-    };
-
-    /**
-     * Builder for creating {@link RouteOptions}.
-     */
-    public final static class Builder {
-        private ArrayList<String> mIfaces = new ArrayList<String>();
-        private Bundle mConnectionParams;
-
-        public Builder() {
-        }
-
-        /**
-         * Add a required interface to the options.
-         *
-         * @param interfaceName The name of the interface to add.
-         * @return The builder to allow chaining commands.
-         */
-        public Builder addInterface(String interfaceName) {
-            if (TextUtils.isEmpty(interfaceName)) {
-                throw new IllegalArgumentException("interfaceName cannot be empty");
-            }
-            if (!mIfaces.contains(interfaceName)) {
-                mIfaces.add(interfaceName);
-            } else {
-                Log.w(TAG, "Attempted to add interface that is already added");
-            }
-            return this;
-        }
-
-        /**
-         * Set the connection parameters to use with the options. TODO replace
-         * with more specific calls once we decide on the standard way to
-         * express parameters.
-         *
-         * @param parameters The parameters to use.
-         * @return The builder to allow chaining commands.
-         */
-        public Builder setParameters(Bundle parameters) {
-            mConnectionParams = parameters;
-            return this;
-        }
-
-        /**
-         * Generate a set of options.
-         *
-         * @return The options with the specified components.
-         */
-        public RouteOptions build() {
-            return new RouteOptions(mIfaces, mConnectionParams);
-        }
-    }
-}
diff --git a/media/java/android/media/session/RoutePlaybackControls.java b/media/java/android/media/session/RoutePlaybackControls.java
deleted file mode 100644
index 8211983..0000000
--- a/media/java/android/media/session/RoutePlaybackControls.java
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.media.session;
-
-import android.media.MediaMetadata;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.ResultReceiver;
-
-/**
- * A standard media control interface for Routes that support queueing and
- * transport controls. Routes may support multiple interfaces for MediaSessions
- * to interact with.
- * @hide
- */
-public final class RoutePlaybackControls {
-    private static final String TAG = "RoutePlaybackControls";
-    public static final String NAME = "android.media.session.RoutePlaybackControls";
-
-    /** @hide */
-    public static final String KEY_VALUE1 = "value1";
-
-    /** @hide */
-    public static final String CMD_FAST_FORWARD = "fastForward";
-    /** @hide */
-    public static final String CMD_GET_CURRENT_POSITION = "getCurrentPosition";
-    /** @hide */
-    public static final String CMD_GET_CAPABILITIES = "getCapabilities";
-    /** @hide */
-    public static final String CMD_PLAY_NOW = "playNow";
-    /** @hide */
-    public static final String CMD_RESUME = "resume";
-    /** @hide */
-    public static final String CMD_PAUSE = "pause";
-
-    /** @hide */
-    public static final String EVENT_PLAYSTATE_CHANGE = "playstateChange";
-    /** @hide */
-    public static final String EVENT_METADATA_CHANGE = "metadataChange";
-
-    private final RouteInterface mIface;
-
-    private RoutePlaybackControls(RouteInterface iface) {
-        mIface = iface;
-    }
-
-    /**
-     * Get a new MediaRoutePlaybackControls instance for sending commands using
-     * this interface. If the provided route doesn't support this interface null
-     * will be returned.
-     *
-     * @param route The route to send commands to.
-     * @return A MediaRoutePlaybackControls instance or null if not supported.
-     */
-    public static RoutePlaybackControls from(Route route) {
-        RouteInterface iface = route.getInterface(NAME);
-        if (iface != null) {
-            return new RoutePlaybackControls(iface);
-        }
-        return null;
-    }
-
-    /**
-     * Send a resume command to the route.
-     */
-    public void resume() {
-        mIface.sendCommand(CMD_RESUME, null, null);
-    }
-
-    /**
-     * Send a pause command to the route.
-     */
-    public void pause() {
-        mIface.sendCommand(CMD_PAUSE, null, null);
-    }
-
-    /**
-     * Send a fast forward command.
-     */
-    public void fastForward() {
-        Bundle b = new Bundle();
-        mIface.sendCommand(CMD_FAST_FORWARD, b, null);
-    }
-
-    /**
-     * Retrieves the current playback position.
-     *
-     * @param cb The callback to receive the result on.
-     */
-    public void getCurrentPosition(ResultReceiver cb) {
-        mIface.sendCommand(CMD_GET_CURRENT_POSITION, null, cb);
-    }
-
-    public void getCapabilities(ResultReceiver cb) {
-        mIface.sendCommand(CMD_GET_CAPABILITIES, null, cb);
-    }
-
-    public void addListener(Listener listener) {
-        mIface.addListener(listener);
-    }
-
-    public void addListener(Listener listener, Handler handler) {
-        mIface.addListener(listener, handler);
-    }
-
-    public void removeListener(Listener listener) {
-        mIface.removeListener(listener);
-    }
-
-    public void playNow(String content) {
-        Bundle bundle = new Bundle();
-        bundle.putString(KEY_VALUE1, content);
-        mIface.sendCommand(CMD_PLAY_NOW, bundle, null);
-    }
-
-    /**
-     * Register this event listener using {@link #addListener} to receive
-     * RoutePlaybackControl events from a session.
-     */
-    public static abstract class Listener extends RouteInterface.EventListener {
-        @Override
-        public final void onEvent(String event, Bundle args) {
-            if (EVENT_PLAYSTATE_CHANGE.equals(event)) {
-                onPlaybackStateChange(args.getInt(KEY_VALUE1, 0));
-            } else if (EVENT_METADATA_CHANGE.equals(event)) {
-                onMetadataUpdate((MediaMetadata) args.getParcelable(KEY_VALUE1));
-            }
-        }
-
-        /**
-         * Override to handle updates to the playback state. Valid values are in
-         * {@link TransportPerformer}. TODO put playstate values somewhere more
-         * generic.
-         *
-         * @param state
-         */
-        public void onPlaybackStateChange(int state) {
-        }
-
-        /**
-         * Override to handle metadata changes for this session's media. The
-         * default supported fields are those in {@link MediaMetadata}.
-         *
-         * @param metadata
-         */
-        public void onMetadataUpdate(MediaMetadata metadata) {
-        }
-    }
-
-}
diff --git a/media/java/android/media/tv/ITvInputClient.aidl b/media/java/android/media/tv/ITvInputClient.aidl
index cac8a14..423e317 100644
--- a/media/java/android/media/tv/ITvInputClient.aidl
+++ b/media/java/android/media/tv/ITvInputClient.aidl
@@ -30,7 +30,6 @@
  */
 oneway interface ITvInputClient {
     void onSessionCreated(in String inputId, IBinder token, in InputChannel channel, int seq);
-    void onAvailabilityChanged(in String inputId, boolean isAvailable);
     void onSessionReleased(int seq);
     void onSessionEvent(in String name, in Bundle args, int seq);
     void onChannelRetuned(in Uri channelUri, int seq);
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index 9a6a648..6a0c592 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -18,9 +18,10 @@
 
 import android.content.ComponentName;
 import android.graphics.Rect;
+import android.media.tv.ITvInputClient;
 import android.media.tv.ITvInputHardware;
 import android.media.tv.ITvInputHardwareCallback;
-import android.media.tv.ITvInputClient;
+import android.media.tv.ITvInputManagerCallback;
 import android.media.tv.TvInputHardwareInfo;
 import android.media.tv.TvInputInfo;
 import android.media.tv.TvTrackInfo;
@@ -34,10 +35,8 @@
 interface ITvInputManager {
     List<TvInputInfo> getTvInputList(int userId);
 
-    boolean getAvailability(in ITvInputClient client, in String inputId, int userId);
-
-    void registerCallback(in ITvInputClient client, in String inputId, int userId);
-    void unregisterCallback(in ITvInputClient client, in String inputId, int userId);
+    void registerCallback(in ITvInputManagerCallback callback, int userId);
+    void unregisterCallback(in ITvInputManagerCallback callback, int userId);
 
     void createSession(in ITvInputClient client, in String inputId, int seq, int userId);
     void releaseSession(in IBinder sessionToken, int userId);
@@ -56,7 +55,12 @@
 
     // For TV input hardware binding
     List<TvInputHardwareInfo> getHardwareList();
+    /*
+     * All TvInputServices which want to use hardware must call this method on
+     * BOOT_COMPLETE.
+     */
+    void registerTvInputInfo(in TvInputInfo info, int deviceId);
     ITvInputHardware acquireTvInputHardware(int deviceId, in ITvInputHardwareCallback callback,
-            int userId);
+            in TvInputInfo info, int userId);
     void releaseTvInputHardware(int deviceId, in ITvInputHardware hardware, int userId);
 }
diff --git a/telephony/java/com/android/internal/telephony/IThirdPartyCallSendDtmfCallback.aidl b/media/java/android/media/tv/ITvInputManagerCallback.aidl
similarity index 65%
rename from telephony/java/com/android/internal/telephony/IThirdPartyCallSendDtmfCallback.aidl
rename to media/java/android/media/tv/ITvInputManagerCallback.aidl
index 3a02b060..5c8a0a3 100644
--- a/telephony/java/com/android/internal/telephony/IThirdPartyCallSendDtmfCallback.aidl
+++ b/media/java/android/media/tv/ITvInputManagerCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2014 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,14 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.internal.telephony;
+package android.media.tv;
 
 /**
- * Callback interface for when DTMF has been sent.
+ * Interface to receive callbacks from ITvInputManager regardless of sessions.
+ * @hide
  */
-oneway interface IThirdPartyCallSendDtmfCallback {
-    /**
-     * Called when the DTMF code has been sent.
-     */
-    void onSendDtmfCompleted();
+oneway interface ITvInputManagerCallback {
+    void onInputStateChanged(in String inputId, int state);
 }
diff --git a/media/java/android/media/tv/ITvInputServiceCallback.aidl b/media/java/android/media/tv/ITvInputServiceCallback.aidl
index c9484dd..1fdb8c5 100644
--- a/media/java/android/media/tv/ITvInputServiceCallback.aidl
+++ b/media/java/android/media/tv/ITvInputServiceCallback.aidl
@@ -24,5 +24,5 @@
  * @hide
  */
 oneway interface ITvInputServiceCallback {
-    void onAvailabilityChanged(in String inputId, boolean isAvailable);
+    void onInputStateChanged(int state);
 }
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 7b8f2ec..5624f3e 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -80,7 +80,7 @@
     // Attributes from XML meta data.
     private String mSetupActivity;
     private String mSettingsActivity;
-    private int mType;
+    private int mType = TYPE_VIRTUAL;
 
     /**
      * Create a new instance of the TvInputInfo class,
@@ -128,10 +128,13 @@
                 Log.d(TAG, "Settings activity loaded. [" + input.mSettingsActivity + "] for "
                         + si.name);
             }
-            input.mType = sa.getInt(
-                    com.android.internal.R.styleable.TvInputService_tvInputType, TYPE_VIRTUAL);
-            if (DEBUG) {
-                Log.d(TAG, "Type loaded. [" + input.mType + "] for " + si.name);
+            if (pm.checkPermission(android.Manifest.permission.TV_INPUT_HARDWARE, si.packageName)
+                    == PackageManager.PERMISSION_GRANTED) {
+                input.mType = sa.getInt(
+                        com.android.internal.R.styleable.TvInputService_tvInputType, TYPE_VIRTUAL);
+                if (DEBUG) {
+                    Log.d(TAG, "Type loaded. [" + input.mType + "] for " + si.name);
+                }
             }
             sa.recycle();
 
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 867b0db..79a83b0 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -24,6 +24,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Pools.Pool;
 import android.util.Pools.SimplePool;
@@ -37,6 +38,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
@@ -65,11 +67,43 @@
      */
     public static final int VIDEO_UNAVAILABLE_REASON_BUFFERING = 3;
 
+    /**
+     * The TV input is connected.
+     * <p>
+     * State for {@link #getInputState} and {@link
+     * TvInputManager.TvInputListener#onInputStateChanged}.
+     * </p>
+     */
+    public static final int INPUT_STATE_CONNECTED = 0;
+    /**
+     * The TV input is connected but in standby mode. It would take a while until it becomes
+     * fully ready.
+     * <p>
+     * State for {@link #getInputState} and {@link
+     * TvInputManager.TvInputListener#onInputStateChanged}.
+     * </p>
+     */
+    public static final int INPUT_STATE_CONNECTED_STANDBY = 1;
+    /**
+     * The TV input is disconnected.
+     * <p>
+     * State for {@link #getInputState} and {@link
+     * TvInputManager.TvInputListener#onInputStateChanged}.
+     * </p>
+     */
+    public static final int INPUT_STATE_DISCONNECTED = 2;
+
     private final ITvInputManager mService;
 
-    // A mapping from an input to the list of its TvInputListenerRecords.
-    private final Map<String, List<TvInputListenerRecord>> mTvInputListenerRecordsMap =
-            new HashMap<String, List<TvInputListenerRecord>>();
+    private final Object mLock = new Object();
+
+    // @GuardedBy(mLock)
+    private final List<TvInputListenerRecord> mTvInputListenerRecordsList =
+            new LinkedList<TvInputListenerRecord>();
+
+    // A mapping from TV input ID to the state of corresponding input.
+    // @GuardedBy(mLock)
+    private final Map<String, Integer> mStateMap = new ArrayMap<String, Integer>();
 
     // A mapping from the sequence number of a session to its SessionCallbackRecord.
     private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap =
@@ -81,6 +115,8 @@
 
     private final ITvInputClient mClient;
 
+    private final ITvInputManagerCallback mCallback;
+
     private final int mUserId;
 
     /**
@@ -242,13 +278,17 @@
      */
     public abstract static class TvInputListener {
         /**
-         * This is called when the availability status of a given TV input is changed.
+         * This is called when the state of a given TV input is changed.
          *
          * @param inputId the id of the TV input.
-         * @param isAvailable {@code true} if the given TV input is available to show TV programs.
-         *            {@code false} otherwise.
+         * @param state state of the TV input. The value is one of the following:
+         * <ul>
+         * <li>{@link TvInputManager#INPUT_STATE_CONNECTED}
+         * <li>{@link TvInputManager#INPUT_STATE_CONNECTED_STANDBY}
+         * <li>{@link TvInputManager#INPUT_STATE_DISCONNECTED}
+         * </ul>
          */
-        public void onAvailabilityChanged(String inputId, boolean isAvailable) {
+        public void onInputStateChanged(String inputId, int state) {
         }
     }
 
@@ -265,11 +305,11 @@
             return mListener;
         }
 
-        public void postAvailabilityChanged(final String inputId, final boolean isAvailable) {
+        public void postStateChanged(final String inputId, final int state) {
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    mListener.onAvailabilityChanged(inputId, isAvailable);
+                    mListener.onInputStateChanged(inputId, state);
                 }
             });
         }
@@ -373,22 +413,23 @@
                     record.postSessionEvent(eventType, eventArgs);
                 }
             }
-
+        };
+        mCallback = new ITvInputManagerCallback.Stub() {
             @Override
-            public void onAvailabilityChanged(String inputId, boolean isAvailable) {
-                synchronized (mTvInputListenerRecordsMap) {
-                    List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId);
-                    if (records == null) {
-                        // Silently ignore - no listener is registered yet.
-                        return;
-                    }
-                    int recordsCount = records.size();
-                    for (int i = 0; i < recordsCount; i++) {
-                        records.get(i).postAvailabilityChanged(inputId, isAvailable);
+            public void onInputStateChanged(String inputId, int state) {
+                synchronized (mLock) {
+                    mStateMap.put(inputId, state);
+                    for (TvInputListenerRecord record : mTvInputListenerRecordsList) {
+                        record.postStateChanged(inputId, state);
                     }
                 }
             }
         };
+        try {
+            mService.registerCallback(mCallback, mUserId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "mService.registerCallback failed: " + e);
+        }
     }
 
     /**
@@ -405,98 +446,66 @@
     }
 
     /**
-     * Returns the availability of a given TV input.
+     * Returns the state of a given TV input. It retuns one of the following:
+     * <ul>
+     * <li>{@link #INPUT_STATE_CONNECTED}
+     * <li>{@link #INPUT_STATE_CONNECTED_STANDBY}
+     * <li>{@link #INPUT_STATE_DISCONNECTED}
+     * </ul>
      *
      * @param inputId the id of the TV input.
-     * @throws IllegalArgumentException if the argument is {@code null}.
-     * @throws IllegalStateException If there is no {@link TvInputListener} registered on the given
-     *             TV input.
+     * @throws IllegalArgumentException if the argument is {@code null} or if there is no
+     *        {@link TvInputInfo} corresponding to {@code inputId}.
      */
-    public boolean getAvailability(String inputId) {
+    public int getInputState(String inputId) {
         if (inputId == null) {
             throw new IllegalArgumentException("id cannot be null");
         }
-        synchronized (mTvInputListenerRecordsMap) {
-            List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId);
-            if (records == null || records.size() == 0) {
-                throw new IllegalStateException("At least one listener should be registered.");
+        synchronized (mLock) {
+            Integer state = mStateMap.get(inputId);
+            if (state == null) {
+                throw new IllegalArgumentException("Unrecognized input ID: " + inputId);
             }
-        }
-        try {
-            return mService.getAvailability(mClient, inputId, mUserId);
-        } catch (RemoteException e) {
-            throw new RuntimeException(e);
+            return state.intValue();
         }
     }
 
     /**
-     * Registers a {@link TvInputListener} for a given TV input.
+     * Registers a {@link TvInputListener}.
      *
-     * @param inputId the id of the TV input.
-     * @param listener a listener used to monitor status of the given TV input.
+     * @param listener a listener used to monitor status of the TV inputs.
      * @param handler a {@link Handler} that the status change will be delivered to.
      * @throws IllegalArgumentException if any of the arguments is {@code null}.
-     * @hide
      */
-    public void registerListener(String inputId, TvInputListener listener, Handler handler) {
-        if (inputId == null) {
-            throw new IllegalArgumentException("id cannot be null");
-        }
+    public void registerListener(TvInputListener listener, Handler handler) {
         if (listener == null) {
             throw new IllegalArgumentException("listener cannot be null");
         }
         if (handler == null) {
             throw new IllegalArgumentException("handler cannot be null");
         }
-        synchronized (mTvInputListenerRecordsMap) {
-            List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId);
-            if (records == null) {
-                records = new ArrayList<TvInputListenerRecord>();
-                mTvInputListenerRecordsMap.put(inputId, records);
-                try {
-                    mService.registerCallback(mClient, inputId, mUserId);
-                } catch (RemoteException e) {
-                    throw new RuntimeException(e);
-                }
-            }
-            records.add(new TvInputListenerRecord(listener, handler));
+        synchronized (mLock) {
+            mTvInputListenerRecordsList.add(new TvInputListenerRecord(listener, handler));
         }
     }
 
     /**
-     * Unregisters the existing {@link TvInputListener} for a given TV input.
+     * Unregisters the existing {@link TvInputListener}.
      *
-     * @param inputId the id of the TV input.
-     * @param listener the existing listener to remove for the given TV input.
+     * @param listener the existing listener to remove.
      * @throws IllegalArgumentException if any of the arguments is {@code null}.
-     * @hide
      */
-    public void unregisterListener(String inputId, final TvInputListener listener) {
-        if (inputId == null) {
-            throw new IllegalArgumentException("id cannot be null");
-        }
+    public void unregisterListener(final TvInputListener listener) {
         if (listener == null) {
             throw new IllegalArgumentException("listener cannot be null");
         }
-        synchronized (mTvInputListenerRecordsMap) {
-            List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId);
-            if (records == null) {
-                Log.e(TAG, "No listener found for " + inputId);
-                return;
-            }
-            for (Iterator<TvInputListenerRecord> it = records.iterator(); it.hasNext();) {
+        synchronized (mLock) {
+            for (Iterator<TvInputListenerRecord> it = mTvInputListenerRecordsList.iterator();
+                    it.hasNext(); ) {
                 TvInputListenerRecord record = it.next();
                 if (record.getListener() == listener) {
                     it.remove();
-                }
-            }
-            if (records.isEmpty()) {
-                try {
-                    mService.unregisterCallback(mClient, inputId, mUserId);
-                } catch (RemoteException e) {
-                    throw new RuntimeException(e);
-                } finally {
-                    mTvInputListenerRecordsMap.remove(inputId);
+                    break;
                 }
             }
         }
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index a994f54..3206320 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -82,20 +82,9 @@
      */
     public static final String SERVICE_META_DATA = "android.media.tv.input";
 
-    private String mId;
     private final Handler mHandler = new ServiceHandler();
     private final RemoteCallbackList<ITvInputServiceCallback> mCallbacks =
             new RemoteCallbackList<ITvInputServiceCallback>();
-    // STOPSHIP: Redesign the API around the availability change. For now, the service will be
-    // always available.
-    private final boolean mAvailable = true;
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        mId = TvInputInfo.generateInputIdForComponentName(
-                new ComponentName(getPackageName(), getClass().getName()));
-    }
 
     @Override
     public final IBinder onBind(Intent intent) {
@@ -104,13 +93,6 @@
             public void registerCallback(ITvInputServiceCallback cb) {
                 if (cb != null) {
                     mCallbacks.register(cb);
-                    // The first time a callback is registered, the service needs to report its
-                    // availability status so that the system can know its initial value.
-                    try {
-                        cb.onAvailabilityChanged(mId, mAvailable);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "error in onAvailabilityChanged", e);
-                    }
                 }
             }
 
@@ -733,7 +715,6 @@
     @SuppressLint("HandlerLeak")
     private final class ServiceHandler extends Handler {
         private static final int DO_CREATE_SESSION = 1;
-        private static final int DO_BROADCAST_AVAILABILITY_CHANGE = 2;
 
         @Override
         public final void handleMessage(Message msg) {
@@ -759,20 +740,6 @@
                     args.recycle();
                     return;
                 }
-                case DO_BROADCAST_AVAILABILITY_CHANGE: {
-                    boolean isAvailable = (Boolean) msg.obj;
-                    int n = mCallbacks.beginBroadcast();
-                    try {
-                        for (int i = 0; i < n; i++) {
-                            mCallbacks.getBroadcastItem(i).onAvailabilityChanged(mId, isAvailable);
-                        }
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Unexpected exception", e);
-                    } finally {
-                        mCallbacks.finishBroadcast();
-                    }
-                    return;
-                }
                 default: {
                     Log.w(TAG, "Unhandled message code: " + msg.what);
                     return;
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 6a835d6a..b7294b8 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -449,13 +449,13 @@
             byteBuffer, orderID, nativeByteOrderObj);
     env->DeleteLocalRef(me);
     me = env->CallObjectMethod(
-            byteBuffer, positionID,
-            input ? 0 : buffer->offset());
-    env->DeleteLocalRef(me);
-    me = env->CallObjectMethod(
             byteBuffer, limitID,
             input ? buffer->capacity() : (buffer->offset() + buffer->size()));
     env->DeleteLocalRef(me);
+    me = env->CallObjectMethod(
+            byteBuffer, positionID,
+            input ? 0 : buffer->offset());
+    env->DeleteLocalRef(me);
     me = NULL;
 
     env->DeleteLocalRef(nativeByteOrderObj);
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index 3dbf77b..52e9910 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -36,6 +36,8 @@
 #include <media/stagefright/MetaData.h>
 #include <media/stagefright/NuMediaExtractor.h>
 
+#include <nativehelper/ScopedLocalRef.h>
+
 #include "android_util_Binder.h"
 
 namespace android {
@@ -206,12 +208,12 @@
     size_t dstSize;
     jbyteArray byteArray = NULL;
 
-    if (dst == NULL) {
-        jclass byteBufClass = env->FindClass("java/nio/ByteBuffer");
-        CHECK(byteBufClass != NULL);
+    ScopedLocalRef<jclass> byteBufClass(env, env->FindClass("java/nio/ByteBuffer"));
+    CHECK(byteBufClass.get() != NULL);
 
+    if (dst == NULL) {
         jmethodID arrayID =
-            env->GetMethodID(byteBufClass, "array", "()[B");
+            env->GetMethodID(byteBufClass.get(), "array", "()[B");
         CHECK(arrayID != NULL);
 
         byteArray =
@@ -251,6 +253,24 @@
 
     *sampleSize = buffer->size();
 
+    jmethodID positionID = env->GetMethodID(
+            byteBufClass.get(), "position", "(I)Ljava/nio/Buffer;");
+
+    CHECK(positionID != NULL);
+
+    jmethodID limitID = env->GetMethodID(
+            byteBufClass.get(), "limit", "(I)Ljava/nio/Buffer;");
+
+    CHECK(limitID != NULL);
+
+    jobject me = env->CallObjectMethod(
+            byteBuf, limitID, offset + *sampleSize);
+    env->DeleteLocalRef(me);
+    me = env->CallObjectMethod(
+            byteBuf, positionID, offset);
+    env->DeleteLocalRef(me);
+    me = NULL;
+
     return OK;
 }
 
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index 1685a447..5646740 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -157,6 +157,10 @@
         return;
     }
     sp<Camera> c = get_native_camera(env, camera, NULL);
+    if (c == NULL) {
+        // get_native_camera will throw an exception in this case
+        return;
+    }
     sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
     process_media_recorder_call(env, mr->setCamera(c->remote(), c->getRecordingProxy()),
             "java/lang/RuntimeException", "setCamera failed.");
diff --git a/packages/PrintSpooler/AndroidManifest.xml b/packages/PrintSpooler/AndroidManifest.xml
index 223013f..260ee397 100644
--- a/packages/PrintSpooler/AndroidManifest.xml
+++ b/packages/PrintSpooler/AndroidManifest.xml
@@ -74,7 +74,7 @@
         </activity>
 
         <receiver
-            android:name=".NotificationController$NotificationBroadcastReceiver"
+            android:name=".model.NotificationController$NotificationBroadcastReceiver"
             android:exported="false" >
         </receiver>
 
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
index d37ccc0..3134e93 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
@@ -56,8 +56,6 @@
     private static final String INTENT_ACTION_RESTART_PRINTJOB = "INTENT_ACTION_RESTART_PRINTJOB";
 
     private static final String EXTRA_PRINT_JOB_ID = "EXTRA_PRINT_JOB_ID";
-    private static final String EXTRA_PRINTJOB_LABEL = "EXTRA_PRINTJOB_LABEL";
-    private static final String EXTRA_PRINTER_NAME = "EXTRA_PRINTER_NAME";
 
     private final Context mContext;
     private final NotificationManager mNotificationManager;
@@ -69,7 +67,7 @@
     }
 
     public void onUpdateNotifications(List<PrintJobInfo> printJobs) {
-        List<PrintJobInfo> notifyPrintJobs = new ArrayList<PrintJobInfo>();
+        List<PrintJobInfo> notifyPrintJobs = new ArrayList<>();
 
         final int printJobCount = printJobs.size();
         for (int i = 0; i < printJobCount; i++) {
@@ -252,8 +250,6 @@
         Intent intent = new Intent(mContext, NotificationBroadcastReceiver.class);
         intent.setAction(INTENT_ACTION_CANCEL_PRINTJOB + "_" + printJob.getId().flattenToString());
         intent.putExtra(EXTRA_PRINT_JOB_ID, printJob.getId());
-        intent.putExtra(EXTRA_PRINTJOB_LABEL, printJob.getLabel());
-        intent.putExtra(EXTRA_PRINTER_NAME, printJob.getPrinterName());
         return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
     }
 
@@ -302,17 +298,14 @@
             String action = intent.getAction();
             if (action != null && action.startsWith(INTENT_ACTION_CANCEL_PRINTJOB)) {
                 PrintJobId printJobId = intent.getExtras().getParcelable(EXTRA_PRINT_JOB_ID);
-                String printJobLabel = intent.getExtras().getString(EXTRA_PRINTJOB_LABEL);
-                String printerName = intent.getExtras().getString(EXTRA_PRINTER_NAME);
-                handleCancelPrintJob(context, printJobId, printJobLabel, printerName);
+                handleCancelPrintJob(context, printJobId);
             } else if (action != null && action.startsWith(INTENT_ACTION_RESTART_PRINTJOB)) {
                 PrintJobId printJobId = intent.getExtras().getParcelable(EXTRA_PRINT_JOB_ID);
                 handleRestartPrintJob(context, printJobId);
             }
         }
 
-        private void handleCancelPrintJob(final Context context, final PrintJobId printJobId,
-                final String printJobLabel, final String printerName) {
+        private void handleCancelPrintJob(final Context context, final PrintJobId printJobId) {
             if (DEBUG) {
                 Log.i(LOG_TAG, "handleCancelPrintJob() printJobId:" + printJobId);
             }
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
index e4716da..8a65a2e 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
@@ -27,6 +27,7 @@
 import android.print.PrinterId;
 import android.print.PrinterInfo;
 import android.printservice.PrintServiceInfo;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
@@ -68,10 +69,10 @@
     private static final int MAX_FAVORITE_PRINTER_COUNT = 4;
 
     private final List<PrinterInfo> mPrinters =
-            new ArrayList<PrinterInfo>();
+            new ArrayList<>();
 
     private final List<PrinterInfo> mFavoritePrinters =
-            new ArrayList<PrinterInfo>();
+            new ArrayList<>();
 
     private final PersistenceManager mPersistenceManager;
 
@@ -92,7 +93,7 @@
 
     private void computeAndDeliverResult(ArrayMap<PrinterId, PrinterInfo> discoveredPrinters,
             ArrayMap<PrinterId, PrinterInfo> favoritePrinters) {
-        List<PrinterInfo> printers = new ArrayList<PrinterInfo>();
+        List<PrinterInfo> printers = new ArrayList<>();
 
         // Add the updated favorite printers.
         final int favoritePrinterCount = favoritePrinters.size();
@@ -142,7 +143,7 @@
         // The contract is that if we already have a valid,
         // result the we have to deliver it immediately.
         if (!mPrinters.isEmpty()) {
-            deliverResult(new ArrayList<PrinterInfo>(mPrinters));
+            deliverResult(new ArrayList<>(mPrinters));
         }
         // Always load the data to ensure discovery period is
         // started and to make sure obsolete printers are updated.
@@ -184,11 +185,12 @@
                                 + mDiscoverySession.getPrinters().size()
                                 + " " + FusedPrintersProvider.this.hashCode());
                     }
+
                     updatePrinters(mDiscoverySession.getPrinters(), mFavoritePrinters);
                 }
             });
             final int favoriteCount = mFavoritePrinters.size();
-            List<PrinterId> printerIds = new ArrayList<PrinterId>(favoriteCount);
+            List<PrinterId> printerIds = new ArrayList<>(favoriteCount);
             for (int i = 0; i < favoriteCount; i++) {
                 printerIds.add(mFavoritePrinters.get(i).getId());
             }
@@ -208,16 +210,19 @@
 
         mPrintersUpdatedBefore = true;
 
-        ArrayMap<PrinterId, PrinterInfo> printersMap =
-                new ArrayMap<PrinterId, PrinterInfo>();
+        // Some of the found printers may have be a printer that is in the
+        // history but with its name changed. Hence, we try to update the
+        // printer to use its current name instead of the historical one.
+        mPersistenceManager.updatePrintersHistoricalNamesIfNeeded(printers);
+
+        ArrayMap<PrinterId, PrinterInfo> printersMap = new ArrayMap<>();
         final int printerCount = printers.size();
         for (int i = 0; i < printerCount; i++) {
             PrinterInfo printer = printers.get(i);
             printersMap.put(printer.getId(), printer);
         }
 
-        ArrayMap<PrinterId, PrinterInfo> favoritePrintersMap =
-                new ArrayMap<PrinterId, PrinterInfo>();
+        ArrayMap<PrinterId, PrinterInfo> favoritePrintersMap = new ArrayMap<>();
         final int favoritePrinterCount = favoritePrinters.size();
         for (int i = 0; i < favoritePrinterCount; i++) {
             PrinterInfo favoritePrinter = favoritePrinters.get(i);
@@ -310,7 +315,7 @@
         for (int i = 0; i < favoritePrinterCount; i++) {
             PrinterInfo favoritePrinter = mFavoritePrinters.get(i);
             if (favoritePrinter.getId().equals(printerId)) {
-                newFavoritePrinters = new ArrayList<PrinterInfo>();
+                newFavoritePrinters = new ArrayList<>();
                 newFavoritePrinters.addAll(mPrinters);
                 newFavoritePrinters.remove(i);
                 break;
@@ -344,7 +349,7 @@
 
         private final AtomicFile mStatePersistFile;
 
-        private List<PrinterInfo> mHistoricalPrinters = new ArrayList<PrinterInfo>();
+        private List<PrinterInfo> mHistoricalPrinters = new ArrayList<>();
 
         private boolean mReadHistoryCompleted;
         private boolean mReadHistoryInProgress;
@@ -382,17 +387,42 @@
             mReadTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
         }
 
-        @SuppressWarnings("unchecked")
+        public void updatePrintersHistoricalNamesIfNeeded(List<PrinterInfo> printers) {
+            boolean writeHistory = false;
+
+            final int printerCount = printers.size();
+            for (int i = 0; i < printerCount; i++) {
+                PrinterInfo printer = printers.get(i);
+                writeHistory |= renamePrinterIfNeeded(printer);
+            }
+
+            if (writeHistory) {
+                writePrinterHistory();
+            }
+        }
+
+        public boolean renamePrinterIfNeeded(PrinterInfo printer) {
+            boolean renamed = false;
+            final int printerCount = mHistoricalPrinters.size();
+            for (int i = 0; i < printerCount; i++) {
+                PrinterInfo historicalPrinter = mHistoricalPrinters.get(i);
+                if (historicalPrinter.getId().equals(printer.getId())
+                        && !TextUtils.equals(historicalPrinter.getName(), printer.getName())) {
+                    mHistoricalPrinters.set(i, printer);
+                    renamed = true;
+                }
+            }
+            return renamed;
+        }
+
         public void addPrinterAndWritePrinterHistory(PrinterInfo printer) {
             if (mHistoricalPrinters.size() >= MAX_HISTORY_LENGTH) {
                 mHistoricalPrinters.remove(0);
             }
             mHistoricalPrinters.add(printer);
-            new WriteTask().executeOnExecutor(AsyncTask.SERIAL_EXECUTOR,
-                    new ArrayList<PrinterInfo>(mHistoricalPrinters));
+            writePrinterHistory();
         }
 
-        @SuppressWarnings("unchecked")
         public void removeHistoricalPrinterAndWritePrinterHistory(PrinterId printerId) {
             boolean writeHistory = false;
             final int printerCount = mHistoricalPrinters.size();
@@ -404,18 +434,22 @@
                 }
             }
             if (writeHistory) {
-                new WriteTask().executeOnExecutor(AsyncTask.SERIAL_EXECUTOR,
-                        new ArrayList<PrinterInfo>(mHistoricalPrinters));
+                writePrinterHistory();
             }
         }
 
+        @SuppressWarnings("unchecked")
+        private void writePrinterHistory() {
+            new WriteTask().executeOnExecutor(AsyncTask.SERIAL_EXECUTOR,
+                    new ArrayList<>(mHistoricalPrinters));
+        }
+
         public boolean isHistoryChanged() {
             return mLastReadHistoryTimestamp != mStatePersistFile.getBaseFile().lastModified();
         }
 
         private List<PrinterInfo> computeFavoritePrinters(List<PrinterInfo> printers) {
-            Map<PrinterId, PrinterRecord> recordMap =
-                    new ArrayMap<PrinterId, PrinterRecord>();
+            Map<PrinterId, PrinterRecord> recordMap = new ArrayMap<>();
 
             // Recompute the weights.
             float currentWeight = 1.0f;
@@ -433,14 +467,14 @@
             }
 
             // Soft the favorite printers.
-            List<PrinterRecord> favoriteRecords = new ArrayList<PrinterRecord>(
+            List<PrinterRecord> favoriteRecords = new ArrayList<>(
                     recordMap.values());
             Collections.sort(favoriteRecords);
 
             // Write the favorites to the output.
             final int favoriteCount = Math.min(favoriteRecords.size(),
                     MAX_FAVORITE_PRINTER_COUNT);
-            List<PrinterInfo> favoritePrinters = new ArrayList<PrinterInfo>(favoriteCount);
+            List<PrinterInfo> favoritePrinters = new ArrayList<>(favoriteCount);
             for (int i = 0; i < favoriteCount; i++) {
                 PrinterInfo printer = favoriteRecords.get(i).printer;
                 favoritePrinters.add(printer);
@@ -482,7 +516,7 @@
                 List<PrintServiceInfo> services = printManager
                         .getEnabledPrintServices();
 
-                Set<ComponentName> enabledComponents = new ArraySet<ComponentName>();
+                Set<ComponentName> enabledComponents = new ArraySet<>();
                 final int installedServiceCount = services.size();
                 for (int i = 0; i < installedServiceCount; i++) {
                     ServiceInfo serviceInfo = services.get(i).getResolveInfo().serviceInfo;
@@ -528,28 +562,23 @@
                         Log.i(LOG_TAG, "No existing printer history "
                                 + FusedPrintersProvider.this.hashCode());
                     }
-                    return new ArrayList<PrinterInfo>();
+                    return new ArrayList<>();
                 }
                 try {
-                    List<PrinterInfo> printers = new ArrayList<PrinterInfo>();
+                    List<PrinterInfo> printers = new ArrayList<>();
                     XmlPullParser parser = Xml.newPullParser();
                     parser.setInput(in, null);
                     parseState(parser, printers);
                     // Take a note which version of the history was read.
                     mLastReadHistoryTimestamp = mStatePersistFile.getBaseFile().lastModified();
                     return printers;
-                } catch (IllegalStateException ise) {
-                    Slog.w(LOG_TAG, "Failed parsing ", ise);
-                } catch (NullPointerException npe) {
-                    Slog.w(LOG_TAG, "Failed parsing ", npe);
-                } catch (NumberFormatException nfe) {
-                    Slog.w(LOG_TAG, "Failed parsing ", nfe);
-                } catch (XmlPullParserException xppe) {
-                    Slog.w(LOG_TAG, "Failed parsing ", xppe);
-                } catch (IOException ioe) {
-                    Slog.w(LOG_TAG, "Failed parsing ", ioe);
-                } catch (IndexOutOfBoundsException iobe) {
-                    Slog.w(LOG_TAG, "Failed parsing ", iobe);
+                } catch (IllegalStateException
+                        | NullPointerException
+                        | NumberFormatException
+                        | XmlPullParserException
+                        | IOException
+                        | IndexOutOfBoundsException e) {
+                    Slog.w(LOG_TAG, "Failed parsing ", e);
                 } finally {
                     IoUtils.closeQuietly(in);
                 }
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
index eaf268d..094edf8 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
@@ -389,6 +389,7 @@
             mSelectedPages = selectedPages;
             mSelectedPageCount = PageRangeUtils.getNormalizedPageCount(
                     mSelectedPages, mDocumentPageCount);
+            updatePreviewAreaAndPageSize();
             notifyDataSetChanged();
         }
         return mSelectedPages;
diff --git a/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java b/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java
index afdbb2a..555aa97 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/widget/PrintContentView.java
@@ -147,6 +147,7 @@
         mExpandCollapseIcon = findViewById(R.id.expand_collapse_icon);
 
         mExpandCollapseHandle.setOnClickListener(this);
+        mSummaryContent.setOnClickListener(this);
 
         // Make sure we start in a closed options state.
         onDragProgress(1.0f);
@@ -154,7 +155,7 @@
 
     @Override
     public void onClick(View view) {
-        if (view == mExpandCollapseHandle) {
+        if (view == mExpandCollapseHandle || view == mSummaryContent) {
             if (isOptionsClosed() && mOptionsStateController.canOpenOptions()) {
                 openOptions();
             } else if (isOptionsOpened() && mOptionsStateController.canCloseOptions()) {
diff --git a/packages/Shell/res/values/strings.xml b/packages/Shell/res/values/strings.xml
index e5606c7..51e2c95 100644
--- a/packages/Shell/res/values/strings.xml
+++ b/packages/Shell/res/values/strings.xml
@@ -19,8 +19,12 @@
 
     <!-- Title of notification indicating a bugreport has been successfully captured. [CHAR LIMIT=50] -->
     <string name="bugreport_finished_title">Bug report captured</string>
+
+    <!-- Text of notification indicating that swipe left will share the captured bugreport. [CHAR LIMIT=100] -->
+    <string name="bugreport_finished_text" product="watch">Swipe left to share your bug report</string>
     <!-- Text of notification indicating that touching will share the captured bugreport. [CHAR LIMIT=100] -->
-    <string name="bugreport_finished_text">Touch to share your bug report</string>
+    <string name="bugreport_finished_text" product="default">Touch to share your bug report</string>
+
 
     <!-- Body of dialog informing user about contents of a bugreport. [CHAR LIMIT=NONE] -->
     <string name="bugreport_confirm">Bug reports contain data from the system\'s various log files, including personal and private information.  Only share bug reports with apps and people you trust.</string>
diff --git a/packages/SystemUI/res/drawable/ic_qs_rotation_01.xml b/packages/SystemUI/res/drawable/ic_qs_rotation_01.xml
deleted file mode 100644
index a6c2cf8..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_rotation_01.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
-    <size
-        android:width="64dp"
-        android:height="64dp"/>
-
-    <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
-
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M10.25,1.75c-0.6,-0.6 -1.5,-0.6 -2.1,0.0l-6.4,6.4c-0.6,0.6 -0.6,1.5 0.0,2.1l12.0,12.0c0.6,0.6 1.5,0.6 2.1,0.0l6.4,-6.4c0.6,-0.6 0.6,-1.5 0.0,-2.1L10.25,1.75zM14.85,21.25l-12.0,-12.0l6.4,-6.4l12.0,12.0L14.85,21.25z"/>
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M16.55,2.5c3.3,1.5 5.6,4.7 6.0,8.5l1.5,0.0c-0.6,-6.2 -5.7,-11.0 -12.0,-11.0c-0.2,0.0 -0.4,0.0 -0.7,0.0l3.8,3.8L16.55,2.5z"/>
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M7.55,21.5c-3.3,-1.5 -5.6,-4.7 -6.0,-8.5l-1.4,0.0c0.5,6.2 5.6,11.0 11.9,11.0c0.2,0.0 0.4,0.0 0.7,0.0l-3.8,-3.8L7.55,21.5z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_rotation_02.xml b/packages/SystemUI/res/drawable/ic_qs_rotation_02.xml
deleted file mode 100644
index 4107c46..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_rotation_02.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
-    <size
-        android:width="64dp"
-        android:height="64dp"/>
-
-    <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
-
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M10.24,2.43C9.67,1.88 8.83,1.88 8.28,2.45L2.34,8.48c-0.56,0.57 -0.55,1.41 0.02,1.96l11.3,11.13c0.57,0.56 1.41,0.55 1.96,-0.02l5.93,-6.03c0.56,-0.57 0.55,-1.41 -0.02,-1.96L10.24,2.43zM14.68,20.62L3.38,9.5l5.93,-6.03l11.3,11.13L14.68,20.62z"/>
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M16.91,2.71c3.23,1.64 5.39,4.94 5.62,8.76l1.5,0.07c-0.33,-6.22 -5.21,-11.24 -11.5,-11.52c-0.2,-0.01 -0.4,-0.02 -0.7,-0.03l3.63,3.96L16.91,2.71z"/>
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M7.09,21.29c-3.23,-1.64 -5.39,-4.94 -5.62,-8.76l-1.4,-0.06c0.23,6.22 5.11,11.24 11.4,11.51c0.2,0.01 0.4,0.02 0.7,0.03l-3.63,-3.96L7.09,21.29z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_rotation_03.xml b/packages/SystemUI/res/drawable/ic_qs_rotation_03.xml
deleted file mode 100644
index 127296c4..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_rotation_03.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
-    <size
-        android:width="64dp"
-        android:height="64dp"/>
-
-    <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
-
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M11.07,4.12c-0.43,-0.49 -1.11,-0.53 -1.6,-0.1L4.3,8.57C3.81,9.0 3.77,9.68 4.19,10.17l8.54,9.71c0.43,0.49 1.11,0.53 1.6,0.1l5.18,-4.55c0.49,-0.43 0.53,-1.11 0.1,-1.6L11.07,4.12zM13.61,19.17L5.08,9.46l5.18,-4.55l8.54,9.71L13.61,19.17z"/>
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M17.72,3.2c3.06,1.94 4.9,5.43 4.77,9.25l1.49,0.21C24.23,6.43 19.84,0.97 13.6,0.11c-0.2,-0.03 -0.4,-0.06 -0.69,-0.1l3.24,4.29L17.72,3.2z"/>
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M6.19,20.78c-3.06,-1.94 -4.9,-5.43 -4.77,-9.25l-1.39,-0.19c-0.36,6.21 4.03,11.67 10.27,12.53c0.2,0.03 0.4,0.06 0.69,0.1l-3.24,-4.29L6.19,20.78z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_rotation_04.xml b/packages/SystemUI/res/drawable/ic_qs_rotation_04.xml
deleted file mode 100644
index d00262ab..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_rotation_04.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
-    <size
-        android:width="64dp"
-        android:height="64dp"/>
-
-    <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
-
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M11.88,5.72c-0.3,-0.42 -0.83,-0.51 -1.25,-0.21L6.19,8.69c-0.42,0.3 -0.51,0.83 -0.21,1.25l5.95,8.34c0.3,0.42 0.83,0.51 1.25,0.21l4.45,-3.17c0.42,-0.3 0.51,-0.83 0.21,-1.25L11.88,5.72zM12.68,17.79L6.73,9.45l4.45,-3.17l5.95,8.34L12.68,17.79z"/>
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M18.8,4.01c2.79,2.32 4.16,6.01 3.54,9.78l1.45,0.4c1.06,-6.14 -2.59,-12.11 -8.67,-13.78c-0.19,-0.05 -0.39,-0.11 -0.68,-0.18l2.66,4.67L18.8,4.01z"/>
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M5.11,19.96c-2.79,-2.32 -4.16,-6.01 -3.54,-9.78L0.21,9.81c-1.15,6.11 2.5,12.09 8.57,13.75c0.19,0.05 0.39,0.11 0.68,0.18L6.8,19.08L5.11,19.96z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_rotation_05.xml b/packages/SystemUI/res/drawable/ic_qs_rotation_05.xml
deleted file mode 100644
index 570f51f..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_rotation_05.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
-    <size
-        android:width="64dp"
-        android:height="64dp"/>
-
-    <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
-
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M15.41,8.14c0.05,-0.42 -0.23,-0.78 -0.65,-0.83l-4.5,-0.54C9.84,6.72 9.48,7.0 9.43,7.42l-1.01,8.44c-0.05,0.42 0.23,0.78 0.65,0.83l4.5,0.54C14.0,17.28 14.35,17.0 14.4,16.58L15.41,8.14zM9.16,15.99l1.01,-8.44l4.5,0.54l-1.01,8.44L9.16,15.99z"/>
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M22.35,10.28c0.64,3.57 -0.69,7.28 -3.59,9.77l0.85,1.23c4.76,-4.01 5.82,-10.94 2.24,-16.12c-0.11,-0.16 -0.23,-0.33 -0.4,-0.58l-0.97,5.29L22.35,10.28z"/>
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M1.6,13.66c-0.64,-3.57 0.69,-7.28 3.59,-9.77L4.4,2.74c-4.82,3.93 -5.88,10.86 -2.3,16.04c0.11,0.16 0.23,0.33 0.4,0.58l0.97,-5.29L1.6,13.66z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_rotation_06.xml b/packages/SystemUI/res/drawable/ic_qs_rotation_06.xml
deleted file mode 100644
index aaf9356..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_rotation_06.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
-    <size
-        android:width="64dp"
-        android:height="64dp"/>
-
-    <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
-
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M15.62,13.08c0.29,-0.1 0.44,-0.39 0.34,-0.69L14.9,9.27c-0.1,-0.29 -0.39,-0.44 -0.69,-0.34l-5.86,1.98c-0.29,0.1 -0.44,0.39 -0.34,0.69l1.06,3.12c0.1,0.29 0.39,0.44 0.69,0.34L15.62,13.08zM8.51,11.44l5.86,-1.98l1.06,3.12l-5.86,1.98L8.51,11.44z"/>
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M18.53,20.21c-2.8,2.3 -6.69,2.95 -10.27,1.64L7.6,23.2c5.83,2.2 12.39,-0.26 15.16,-5.92c0.09,-0.18 0.18,-0.36 0.31,-0.63l-5.09,1.73L18.53,20.21z"/>
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M5.45,3.76c2.8,-2.3 6.69,-2.95 10.27,-1.64l0.62,-1.26C10.56,-1.42 4.0,1.04 1.22,6.69C1.13,6.87 1.05,7.05 0.91,7.32L6.0,5.59L5.45,3.76z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_rotation_07.xml b/packages/SystemUI/res/drawable/ic_qs_rotation_07.xml
deleted file mode 100644
index 330ce6a..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_rotation_07.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
-    <size
-        android:width="64dp"
-        android:height="64dp"/>
-
-    <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
-
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M12.23,14.48c0.13,0.16 0.35,0.17 0.5,0.04l1.66,-1.4c0.16,-0.13 0.17,-0.35 0.04,-0.5l-2.62,-3.11c-0.13,-0.16 -0.35,-0.17 -0.5,-0.04l-1.66,1.4c-0.16,0.13 -0.17,0.35 -0.04,0.5L12.23,14.48zM11.53,9.73l2.62,3.11l-1.66,1.4l-2.62,-3.11L11.53,9.73z"/>
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M6.04,20.63c-3.01,-2.02 -4.75,-5.56 -4.52,-9.37l-1.48,-0.25c-0.43,6.21 3.81,11.79 10.02,12.83c0.2,0.03 0.39,0.07 0.69,0.12l-3.12,-4.37L6.04,20.63z"/>
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M18.04,3.37c3.01,2.02 4.75,5.56 4.52,9.37l1.38,0.23c0.53,-6.2 -3.71,-11.77 -9.93,-12.81c-0.2,-0.03 -0.39,-0.07 -0.69,-0.12l3.12,4.37L18.04,3.37z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_rotation_08.xml b/packages/SystemUI/res/drawable/ic_qs_rotation_08.xml
deleted file mode 100644
index 1c7f1a1..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_rotation_08.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
-    <size
-        android:width="64dp"
-        android:height="64dp"/>
-
-    <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
-
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M10.55,12.73c-0.06,0.12 -0.02,0.25 0.1,0.32l1.26,0.68c0.12,0.06 0.25,0.02 0.32,-0.1l1.27,-2.36c0.06,-0.12 0.02,-0.25 -0.1,-0.32l-1.26,-0.68c-0.12,-0.06 -0.25,-0.02 -0.32,0.1L10.55,12.73zM13.29,11.15l-1.27,2.36l-1.26,-0.68l1.27,-2.36L13.29,11.15z"/>
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M1.55,12.95C1.18,9.34 2.78,5.74 5.86,3.47L5.1,2.18C0.05,5.83 -1.51,12.66 1.67,18.09c0.1,0.17 0.2,0.35 0.35,0.6l1.36,-5.2L1.55,12.95z"/>
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M22.5,11.12c0.37,3.61 -1.23,7.21 -4.31,9.47l0.71,1.21c5.1,-3.56 6.67,-10.39 3.48,-15.83c-0.1,-0.17 -0.2,-0.35 -0.35,-0.6l-1.36,5.2L22.5,11.12z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_rotation_09.xml b/packages/SystemUI/res/drawable/ic_qs_rotation_09.xml
deleted file mode 100644
index ebfbad6..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_rotation_09.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
-    <size
-        android:width="64dp"
-        android:height="64dp"/>
-
-    <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
-
-    <path
-        android:pathData="M11.45,12c0,0.14,0.12,0.25,0.25,0.25l0.06,0l0,-0.5l-0.06,0C11.57,11.75,11.45,11.86,11.45,12z"
-        android:fill="#00000000"/>
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M11.77,11.62l-0.06,0.0c-0.21,0.0 -0.37,0.17 -0.38,0.37c0.0,0.21 0.17,0.37 0.37,0.37l0.06,0.0c0.0,0.07 0.06,0.12 0.12,0.13l0.62,0.0c0.07,0.0 0.12,-0.06 0.13,-0.12l0.0,-0.75c0.0,-0.07 -0.06,-0.12 -0.12,-0.13l-0.62,0.0C11.83,11.5 11.77,11.56 11.77,11.62zM12.33,12.0c0.0,0.07 -0.06,0.12 -0.13,0.12c-0.07,0.0 -0.12,-0.06 -0.12,-0.13c0.0,-0.07 0.06,-0.12 0.13,-0.12C12.28,11.88 12.33,11.93 12.33,12.0zM11.77,11.75l0.0,0.5l-0.06,0.0c-0.14,0.0 -0.26,-0.11 -0.25,-0.25c0.0,-0.14 0.12,-0.25 0.26,-0.25L11.77,11.75z"/>
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M3.83,5.44c2.21,-2.87 5.85,-4.38 9.64,-3.9l0.34,-1.46C7.64,-0.75 1.81,3.12 0.37,9.25C0.32,9.45 0.28,9.64 0.21,9.93L4.78,7.1L3.83,5.44zM20.28,18.53c-2.21,2.87 -5.85,4.38 -9.64,3.9l-0.32,1.36c6.15,0.93 11.99,-2.95 13.42,-9.08c0.05,-0.19 0.09,-0.39 0.16,-0.68l-4.57,2.83L20.28,18.53z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_rotation_10.xml b/packages/SystemUI/res/drawable/ic_qs_rotation_10.xml
deleted file mode 100644
index 21dda8c..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_rotation_10.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
-    <size
-        android:width="64dp"
-        android:height="64dp"/>
-
-    <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
-
-    <path
-        android:pathData="M10.42,9.96c-0.42,0.42,-0.39,1.12,0.03,1.54l0.19,0.19l1.51,-1.53l-0.19,-0.19       C11.54,9.55,10.84,9.54,10.42,9.96z"
-        android:fill="#00000000"/>
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M12.53,9.78l-0.19,-0.19c-0.63,-0.63 -1.66,-0.62 -2.28,0.02c-0.63,0.63 -0.62,1.66 0.02,2.28l0.19,0.19c-0.21,0.21 -0.21,0.55 0.01,0.76l1.92,1.89c0.21,0.21 0.55,0.21 0.76,-0.01l2.27,-2.3c0.21,-0.21 0.21,-0.55 -0.01,-0.76l-1.92,-1.89C13.08,9.56 12.74,9.56 12.53,9.78zM13.12,12.62c-0.21,0.21 -0.55,0.21 -0.76,0.01c-0.21,-0.21 -0.21,-0.55 -0.01,-0.76c0.21,-0.21 0.55,-0.21 0.76,-0.01C13.33,12.07 13.33,12.41 13.12,12.62zM12.15,10.16l-1.51,1.53l-0.19,-0.19c-0.42,-0.42 -0.45,-1.12 -0.03,-1.54c0.42,-0.42 1.12,-0.41 1.54,0.01L12.15,10.16z"/>
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M7.82,2.36c3.3,-1.49 7.23,-1.12 10.35,1.09l0.99,-1.13C14.09,-1.32 7.12,-0.64 2.97,4.1C2.84,4.25 2.71,4.4 2.51,4.62l5.36,-0.36L7.82,2.36zM16.18,21.64c-3.3,1.49 -7.23,1.12 -10.35,-1.09l-0.92,1.05c4.99,3.71 11.97,3.03 16.12,-1.71c0.13,-0.15 0.26,-0.3 0.46,-0.53l-5.36,0.36L16.18,21.64z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_rotation_11.xml b/packages/SystemUI/res/drawable/ic_qs_rotation_11.xml
deleted file mode 100644
index f4186fe..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_rotation_11.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
-    <size
-        android:width="64dp"
-        android:height="64dp"/>
-
-    <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
-
-    <path
-        android:pathData="M10.53,8.85c-0.7,0.37,-0.95,1.28,-0.58,1.98l0.17,0.32l2.55,-1.36L12.5,9.48       C12.12,8.78,11.23,8.48,10.53,8.85z"
-        android:fill="#00000000"/>
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M13.3,9.46l-0.17,-0.32c-0.56,-1.05 -1.87,-1.45 -2.93,-0.89s-1.45,1.87 -0.89,2.93l0.17,0.32c-0.35,0.19 -0.48,0.62 -0.3,0.98l1.7,3.18c0.19,0.35 0.62,0.48 0.98,0.3l3.82,-2.04c0.35,-0.19 0.48,-0.62 0.3,-0.98l-1.7,-3.18C14.09,9.4 13.65,9.27 13.3,9.46zM12.92,13.34c-0.35,0.19 -0.79,0.06 -0.98,-0.3c-0.19,-0.35 -0.05,-0.79 0.3,-0.98c0.35,-0.19 0.79,-0.05 0.98,0.3C13.41,12.72 13.27,13.15 12.92,13.34zM12.67,9.8l-2.55,1.36l-0.17,-0.32c-0.37,-0.7 -0.13,-1.61 0.58,-1.98c0.7,-0.37 1.59,-0.08 1.97,0.63L12.67,9.8z"/>
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M11.48,1.5c3.62,-0.23 7.16,1.5 9.3,4.66l1.32,-0.71C18.65,0.27 11.89,-1.55 6.34,1.42C6.16,1.51 5.98,1.61 5.72,1.75l5.14,1.56L11.48,1.5zM12.52,22.5c-3.62,0.23 -7.16,-1.5 -9.3,-4.66L1.98,18.5c3.37,5.23 10.13,7.06 15.68,4.08c0.18,-0.09 0.35,-0.19 0.62,-0.33l-5.14,-1.56L12.52,22.5z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_rotation_12.xml b/packages/SystemUI/res/drawable/ic_qs_rotation_12.xml
deleted file mode 100644
index d408e28..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_rotation_12.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
-    <size
-        android:width="64dp"
-        android:height="64dp"/>
-
-    <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
-
-    <path
-        android:pathData="M10.83,8.49c-0.91,0.3,-1.39,1.31,-1.09,2.22l0.13,0.41l3.28,-1.08l-0.13,-0.41       C12.72,8.72,11.73,8.19,10.83,8.49z"
-        android:fill="#00000000"/>
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M13.98,9.77l-0.13,-0.41C13.4,8.0 11.93,7.26 10.57,7.71c-1.36,0.45 -2.1,1.91 -1.65,3.27l0.13,0.41c-0.45,0.15 -0.7,0.64 -0.55,1.09l1.35,4.1c0.15,0.45 0.64,0.7 1.09,0.55l4.92,-1.62c0.45,-0.15 0.7,-0.64 0.55,-1.09l-1.35,-4.1C14.92,9.87 14.43,9.62 13.98,9.77zM12.73,14.27c-0.45,0.15 -0.94,-0.1 -1.09,-0.55c-0.15,-0.45 0.1,-0.94 0.55,-1.09c0.45,-0.15 0.94,0.1 1.09,0.55C13.43,13.64 13.18,14.12 12.73,14.27zM13.16,10.04l-3.28,1.08l-0.13,-0.41c-0.3,-0.91 0.18,-1.92 1.09,-2.22c0.91,-0.3 1.9,0.24 2.19,1.14L13.16,10.04z"/>
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M13.55,1.6c3.59,0.48 6.72,2.87 8.21,6.39l1.44,-0.44C20.82,1.8 14.54,-1.31 8.51,0.52c-0.19,0.06 -0.38,0.12 -0.67,0.2l4.74,2.53L13.55,1.6zM10.45,22.4c-3.59,-0.48 -6.72,-2.87 -8.21,-6.39L0.9,16.41c2.28,5.79 8.55,8.9 14.58,7.07c0.19,-0.06 0.38,-0.12 0.67,-0.2l-4.74,-2.53L10.45,22.4z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_rotation_13.xml b/packages/SystemUI/res/drawable/ic_qs_rotation_13.xml
deleted file mode 100644
index 1ac6b39..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_rotation_13.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
-    <size
-        android:width="64dp"
-        android:height="64dp"/>
-
-    <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
-
-    <path
-        android:pathData="M11.17,7.71c-1.09,0.19,-1.8,1.28,-1.61,2.37l0.09,0.49l3.94,-0.7l-0.09,-0.49C13.3,8.3,12.26,7.52,11.17,7.71       z"
-        android:fill="#00000000"/>
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M14.57,9.7l-0.09,-0.49C14.19,7.58 12.63,6.49 11.0,6.78c-1.63,0.29 -2.71,1.85 -2.43,3.48l0.08,0.49c-0.54,0.1 -0.91,0.62 -0.81,1.16l0.87,4.92c0.1,0.54 0.62,0.91 1.16,0.81l5.91,-1.05c0.54,-0.1 0.91,-0.61 0.81,-1.16l-0.87,-4.92C15.63,9.97 15.11,9.61 14.57,9.7zM12.4,14.66c-0.54,0.1 -1.06,-0.27 -1.16,-0.81c-0.1,-0.54 0.27,-1.06 0.81,-1.16s1.06,0.27 1.16,0.81C13.3,14.04 12.94,14.56 12.4,14.66zM13.58,9.88l-3.94,0.7l-0.09,-0.49c-0.19,-1.09 0.52,-2.17 1.61,-2.37s2.13,0.58 2.33,1.67L13.58,9.88z"/>
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M14.99,1.92c3.49,0.97 6.26,3.78 7.24,7.48l1.48,-0.23c-1.55,-6.03 -7.32,-9.99 -13.55,-9.02c-0.2,0.03 -0.4,0.06 -0.69,0.11l4.34,3.17L14.99,1.92zM9.01,22.08C5.52,21.1 2.76,18.3 1.78,14.6L0.4,14.82c1.45,6.05 7.22,10.01 13.45,9.04c0.2,-0.03 0.4,-0.06 0.69,-0.11l-4.34,-3.17L9.01,22.08z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_rotation_14.xml b/packages/SystemUI/res/drawable/ic_qs_rotation_14.xml
deleted file mode 100644
index c43e363c..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_rotation_14.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
-    <size
-        android:width="64dp"
-        android:height="64dp"/>
-
-    <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
-
-    <path
-        android:pathData="M11.72,7.61c-1.1,0.08,-1.93,1.08,-1.86,2.18l0.03,0.5l3.99,-0.27l-0.03,-0.5       C13.78,8.42,12.82,7.54,11.72,7.61z"
-        android:fill="#00000000"/>
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M14.88,9.95l-0.03,-0.5c-0.11,-1.65 -1.54,-2.9 -3.2,-2.79C10.0,6.78 8.75,8.21 8.87,9.86l0.03,0.5c-0.55,0.04 -0.97,0.52 -0.93,1.07l0.34,4.99c0.04,0.55 0.52,0.97 1.07,0.93l5.99,-0.41c0.55,-0.04 0.97,-0.51 0.93,-1.07l-0.34,-4.99C15.91,10.33 15.43,9.91 14.88,9.95zM12.2,14.64c-0.55,0.04 -1.03,-0.38 -1.07,-0.93c-0.04,-0.55 0.38,-1.03 0.93,-1.07c0.55,-0.04 1.03,0.38 1.07,0.93C13.16,14.13 12.75,14.61 12.2,14.64zM13.88,10.02l-3.99,0.27l-0.03,-0.5c-0.08,-1.1 0.75,-2.11 1.86,-2.18c1.1,-0.08 2.06,0.81 2.13,1.91L13.88,10.02z"/>
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M15.71,2.17c3.41,1.23 5.96,4.23 6.67,7.98l1.5,-0.12c-1.1,-6.13 -6.58,-10.5 -12.86,-9.99c-0.2,0.02 -0.4,0.03 -0.7,0.06l4.1,3.48L15.71,2.17zM8.29,21.83c-3.41,-1.23 -5.96,-4.23 -6.67,-7.98l-1.4,0.11c1.0,6.14 6.48,10.51 12.76,9.99c0.2,-0.02 0.4,-0.03 0.7,-0.06l-4.1,-3.48L8.29,21.83z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_rotation_15.xml b/packages/SystemUI/res/drawable/ic_qs_rotation_15.xml
deleted file mode 100644
index 22fa428..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_rotation_15.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
-    <size
-        android:width="64dp"
-        android:height="64dp"/>
-
-    <viewport
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0"/>
-
-    <path
-        android:pathData="M12.05,7.7c-1.1,0,-2,0.94,-2,2.05v0.5h4v-0.5C14.05,8.65,13.15,7.7,12.05,7.7z"
-        android:fill="#00000000"/>
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M15.05,10.25l0.0,-0.5c0.0,-1.66 -1.34,-3.0 -3.0,-3.0s-2.99,1.34 -2.99,3.0l-0.01,0.5c-0.55,0.0 -1.0,0.45 -1.0,1.0l0.0,5.0c0.0,0.55 0.45,1.0 1.0,1.0l6.0,0.0c0.55,0.0 1.0,-0.45 1.0,-1.0l0.0,-5.0C16.05,10.7 15.6,10.25 15.05,10.25zM12.05,14.75c-0.55,0.0 -1.0,-0.45 -1.0,-1.0c0.0,-0.55 0.45,-1.0 1.0,-1.0s1.0,0.45 1.0,1.0C13.05,14.3 12.6,14.75 12.05,14.75zM14.05,10.25l-4.0,0.0l0.0,-0.5c0.0,-1.1 0.9,-2.05 2.0,-2.05s2.0,0.94 2.0,2.05L14.05,10.25z"/>
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M16.5,2.5c3.3,1.5 5.6,4.7 6.0,8.5L24.0,11.0C23.4,4.8 18.3,0.0 12.0,0.0c-0.2,0.0 -0.4,0.0 -0.7,0.0l3.8,3.8L16.5,2.5zM7.5,21.5c-3.3,-1.5 -5.6,-4.7 -6.0,-8.5L0.1,13.0C0.6,19.2 5.7,24.0 12.0,24.0c0.2,0.0 0.4,0.0 0.7,0.0l-3.8,-3.8L7.5,21.5z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_rotation_landscape.xml b/packages/SystemUI/res/drawable/ic_qs_rotation_landscape.xml
new file mode 100644
index 0000000..e4c7cb5
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_rotation_landscape.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="64dp"
+        android:height="64dp"/>
+
+    <viewport
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0"/>
+
+    <path
+        android:fill="#FFFFFFFF"
+        android:pathData="M2.0,14.0l0.0,20.0c0.0,2.2 1.8,4.0 4.0,4.0l36.0,0.0c2.2,0.0 4.0,-1.8 4.0,-4.0L46.0,14.0c0.0,-2.2 -1.8,-4.0 -4.0,-4.0L6.0,10.0C3.8,10.0 2.0,11.8 2.0,14.0zM38.0,14.0l0.0,20.0L10.0,34.0L10.0,14.0L38.0,14.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_rotation_locked.xml b/packages/SystemUI/res/drawable/ic_qs_rotation_locked.xml
deleted file mode 100644
index 75e20f0..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_rotation_locked.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<animation-list
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:oneshot="true">
-    <item android:drawable="@drawable/ic_qs_rotation_01" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_rotation_02" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_rotation_03" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_rotation_04" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_rotation_05" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_rotation_06" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_rotation_07" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_rotation_08" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_rotation_09" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_rotation_10" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_rotation_11" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_rotation_12" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_rotation_13" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_rotation_14" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_rotation_15" android:duration="16" />
-</animation-list>
diff --git a/packages/SystemUI/res/drawable/ic_qs_rotation_portrait.xml b/packages/SystemUI/res/drawable/ic_qs_rotation_portrait.xml
new file mode 100644
index 0000000..e4bf367
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_rotation_portrait.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="64dp"
+        android:height="64dp"/>
+
+    <viewport
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0"/>
+
+    <path
+        android:fill="#FFFFFFFF"
+        android:pathData="M34.0,2.0L14.0,2.0c-2.2,0.0 -4.0,1.8 -4.0,4.0l0.0,36.0c0.0,2.2 1.8,4.0 4.0,4.0l20.0,0.0c2.2,0.0 4.0,-1.8 4.0,-4.0L38.0,6.0C38.0,3.8 36.2,2.0 34.0,2.0zM34.0,38.0L14.0,38.0L14.0,10.0l20.0,0.0L34.0,38.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_rotation_unlocked.xml b/packages/SystemUI/res/drawable/ic_qs_rotation_unlocked.xml
index a1cedb9..a6c2cf8 100644
--- a/packages/SystemUI/res/drawable/ic_qs_rotation_unlocked.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_rotation_unlocked.xml
@@ -1,4 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
 <!--
 Copyright (C) 2014 The Android Open Source Project
 
@@ -14,22 +13,22 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 -->
-<animation-list
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:oneshot="true">
-    <item android:drawable="@drawable/ic_qs_rotation_15" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_rotation_14" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_rotation_13" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_rotation_12" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_rotation_11" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_rotation_10" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_rotation_09" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_rotation_08" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_rotation_07" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_rotation_06" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_rotation_05" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_rotation_04" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_rotation_03" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_rotation_02" android:duration="16" />
-    <item android:drawable="@drawable/ic_qs_rotation_01" android:duration="16" />
-</animation-list>
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="64dp"
+        android:height="64dp"/>
+
+    <viewport
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"/>
+
+    <path
+        android:fill="#FFFFFFFF"
+        android:pathData="M10.25,1.75c-0.6,-0.6 -1.5,-0.6 -2.1,0.0l-6.4,6.4c-0.6,0.6 -0.6,1.5 0.0,2.1l12.0,12.0c0.6,0.6 1.5,0.6 2.1,0.0l6.4,-6.4c0.6,-0.6 0.6,-1.5 0.0,-2.1L10.25,1.75zM14.85,21.25l-12.0,-12.0l6.4,-6.4l12.0,12.0L14.85,21.25z"/>
+    <path
+        android:fill="#FFFFFFFF"
+        android:pathData="M16.55,2.5c3.3,1.5 5.6,4.7 6.0,8.5l1.5,0.0c-0.6,-6.2 -5.7,-11.0 -12.0,-11.0c-0.2,0.0 -0.4,0.0 -0.7,0.0l3.8,3.8L16.55,2.5z"/>
+    <path
+        android:fill="#FFFFFFFF"
+        android:pathData="M7.55,21.5c-3.3,-1.5 -5.6,-4.7 -6.0,-8.5l-1.4,0.0c0.5,6.2 5.6,11.0 11.9,11.0c0.2,0.0 0.4,0.0 0.7,0.0l-3.8,-3.8L7.55,21.5z"/>
+</vector>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index bae7ed7..3d53f9c 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -44,6 +44,7 @@
     <color name="qs_detail_empty">#24B0BEC5</color><!-- 14% blue grey 200-->
     <color name="data_usage_secondary">#99FFFFFF</color><!-- 60% white -->
     <color name="data_usage_graph_track">#33FFFFFF</color><!-- 20% white -->
+    <color name="data_usage_graph_warning">#FFFFFFFF</color>
     <color name="status_bar_clock_color">#33FFFFFF</color>
 
     <!-- Tint color for the content on the notification overflow card. -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6a7b450..d4feccd 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -516,6 +516,10 @@
     <string name="quick_settings_time_label">Time</string>
     <!-- QuickSettings: User [CHAR LIMIT=NONE] -->
     <string name="quick_settings_user_label">Me</string>
+    <!-- QuickSettings: Title of the user detail panel [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_user_title">User</string>
+    <!-- QuickSettings: Label on the item for adding a new user [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_user_new_user">New user</string>
     <!-- QuickSettings: Wifi [CHAR LIMIT=NONE] -->
     <string name="quick_settings_wifi_label">Wi-Fi</string>
     <!-- QuickSettings: Wifi (Not connected) [CHAR LIMIT=NONE] -->
@@ -572,6 +576,8 @@
     <string name="quick_settings_cellular_detail_data_used"><xliff:g id="data_used" example="2.0 GB">%s</xliff:g> used</string>
     <!-- QuickSettings: Cellular detail panel, data limit format string [CHAR LIMIT=NONE] -->
     <string name="quick_settings_cellular_detail_data_limit"><xliff:g id="data_limit" example="2.0 GB">%s</xliff:g> limit</string>
+    <!-- QuickSettings: Cellular detail panel, data warning format string [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_cellular_detail_data_warning"><xliff:g id="data_limit" example="2.0 GB">%s</xliff:g> warning</string>
 
     <!-- Recents: The empty recents string. [CHAR LIMIT=NONE] -->
     <string name="recents_empty_message">No recent apps</string>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/DataUsageGraph.java b/packages/SystemUI/src/com/android/systemui/qs/DataUsageGraph.java
index fa11af6..d55ceaa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/DataUsageGraph.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/DataUsageGraph.java
@@ -28,34 +28,34 @@
 
 public class DataUsageGraph extends View {
 
-    private final int mBackgroundColor;
     private final int mTrackColor;
     private final int mUsageColor;
     private final int mOverlimitColor;
+    private final int mWarningColor;
     private final int mMarkerWidth;
     private final RectF mTmpRect = new RectF();
     private final Paint mTmpPaint = new Paint();
 
-    private long mMaxLevel = 1;
     private long mLimitLevel;
     private long mWarningLevel;
     private long mUsageLevel;
+    private long mMaxLevel;
 
     public DataUsageGraph(Context context, AttributeSet attrs) {
         super(context, attrs);
         final Resources res = context.getResources();
-        mBackgroundColor = res.getColor(R.color.system_primary_color);
         mTrackColor = res.getColor(R.color.data_usage_graph_track);
         mUsageColor = res.getColor(R.color.system_accent_color);
         mOverlimitColor = res.getColor(R.color.system_warning_color);
+        mWarningColor = res.getColor(R.color.data_usage_graph_warning);
         mMarkerWidth = res.getDimensionPixelSize(R.dimen.data_usage_graph_marker_width);
     }
 
-    public void setLevels(long maxLevel, long limitLevel, long warningLevel, long usageLevel) {
-        mMaxLevel = Math.max(maxLevel, 1);
-        mLimitLevel = limitLevel;
-        mWarningLevel = warningLevel;
-        mUsageLevel = usageLevel;
+    public void setLevels(long limitLevel, long warningLevel, long usageLevel) {
+        mLimitLevel = Math.max(0, limitLevel);
+        mWarningLevel = Math.max(0, warningLevel);
+        mUsageLevel = Math.max(0, usageLevel);
+        mMaxLevel = Math.max(Math.max(Math.max(mLimitLevel, mWarningLevel), mUsageLevel), 1);
         postInvalidate();
     }
 
@@ -68,21 +68,22 @@
         final int w = getWidth();
         final int h = getHeight();
 
-        // draw track
-        r.set(0, 0, w, h);
-        p.setColor(mTrackColor);
-        canvas.drawRect(r, p);
-
-        final boolean hasLimit = mLimitLevel > 0;
-        final boolean overLimit = hasLimit && mUsageLevel > mLimitLevel;
-
-        final long maxLevel = hasLimit ? Math.max(mUsageLevel, mLimitLevel) : mMaxLevel;
-        final long usageLevel = hasLimit ? Math.min(mUsageLevel, mLimitLevel) : mUsageLevel;
-        float usageRight = w * (usageLevel / (float) maxLevel);
+        final boolean overLimit = mLimitLevel > 0 && mUsageLevel > mLimitLevel;
+        float usageRight = w * (mUsageLevel / (float) mMaxLevel);
         if (overLimit) {
-            usageRight -= (mMarkerWidth / 2);
-            usageRight = Math.min(usageRight, w - mMarkerWidth * 2);
-            usageRight = Math.max(usageRight, mMarkerWidth);
+            // compute the gap
+            usageRight = w * (mLimitLevel / (float) mMaxLevel) - (mMarkerWidth / 2);
+            usageRight = Math.min(Math.max(usageRight, mMarkerWidth), w - mMarkerWidth * 2);
+
+            // draw overlimit
+            r.set(usageRight + mMarkerWidth, 0, w, h);
+            p.setColor(mOverlimitColor);
+            canvas.drawRect(r, p);
+        } else {
+            // draw track
+            r.set(0, 0, w, h);
+            p.setColor(mTrackColor);
+            canvas.drawRect(r, p);
         }
 
         // draw usage
@@ -90,16 +91,11 @@
         p.setColor(mUsageColor);
         canvas.drawRect(r, p);
 
-        if (overLimit) {
-            // draw gap
-            r.set(usageRight, 0, usageRight + mMarkerWidth, h);
-            p.setColor(mBackgroundColor);
-            canvas.drawRect(r, p);
-
-            // draw overlimit
-            r.set(usageRight + mMarkerWidth, 0, w, h);
-            p.setColor(mOverlimitColor);
-            canvas.drawRect(r, p);
-        }
+        // draw warning marker
+        float warningLeft = w * (mWarningLevel / (float) mMaxLevel) - mMarkerWidth / 2;
+        warningLeft = Math.min(Math.max(warningLeft, 0), w - mMarkerWidth);
+        r.set(warningLeft, 0, warningLeft + mMarkerWidth, h);
+        p.setColor(mWarningColor);
+        canvas.drawRect(r, p);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 449cc1d..72474b8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -108,6 +108,9 @@
             mColumns = columns;
             postInvalidate();
         }
+        if (mListening) {
+            refreshAllTiles();
+        }
     }
 
     public void setExpanded(boolean expanded) {
@@ -123,9 +126,9 @@
         mListening = listening;
         for (TileRecord r : mRecords) {
             r.tile.setListening(mListening);
-            if (mListening) {
-                r.tile.refreshState();
-            }
+        }
+        if (mListening) {
+            refreshAllTiles();
         }
         if (listening) {
             mBrightnessController.registerCallbacks();
@@ -134,6 +137,12 @@
         }
     }
 
+    private void refreshAllTiles() {
+        for (TileRecord r : mRecords) {
+            r.tile.refreshState();
+        }
+    }
+
     private void showDetail(boolean show, TileRecord r) {
         mHandler.obtainMessage(H.SHOW_DETAIL, show ? 1 : 0, 0, r).sendToTarget();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 1f12b2a..d76a2fe 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -216,21 +216,27 @@
             final DataUsageInfo info = mController.getDataUsageInfo();
             if (info == null) return v;
             final Resources res = mContext.getResources();
-            int titleId;
-            long bytes;
+            final int titleId;
+            final long bytes;
             int usageColor = R.color.system_accent_color;
-            String top = null, bottom = null;
-            if (info.limitLevel <= 0) { // no limit
+            final String top;
+            String bottom = null;
+            if (info.usageLevel < info.warningLevel || info.limitLevel <= 0) {
+                // under warning, or no limit
                 titleId = R.string.quick_settings_cellular_detail_data_usage;
                 bytes = info.usageLevel;
-            } else if (info.usageLevel <= info.limitLevel) { // under limit
+                top = res.getString(R.string.quick_settings_cellular_detail_data_warning,
+                        formatBytes(info.warningLevel));
+            } else if (info.usageLevel <= info.limitLevel) {
+                // over warning, under limit
                 titleId = R.string.quick_settings_cellular_detail_remaining_data;
                 bytes = info.limitLevel - info.usageLevel;
                 top = res.getString(R.string.quick_settings_cellular_detail_data_used,
                         formatBytes(info.usageLevel));
                 bottom = res.getString(R.string.quick_settings_cellular_detail_data_limit,
                         formatBytes(info.limitLevel));
-            } else { // over limit
+            } else {
+                // over limit
                 titleId = R.string.quick_settings_cellular_detail_over_limit;
                 bytes = info.usageLevel - info.limitLevel;
                 top = res.getString(R.string.quick_settings_cellular_detail_data_used,
@@ -246,7 +252,7 @@
             usage.setText(formatBytes(bytes));
             usage.setTextColor(res.getColor(usageColor));
             final DataUsageGraph graph = (DataUsageGraph) v.findViewById(R.id.usage_graph);
-            graph.setLevels(info.maxLevel, info.limitLevel, info.warningLevel, info.usageLevel);
+            graph.setLevels(info.limitLevel, info.warningLevel, info.usageLevel);
             final TextView carrier = (TextView) v.findViewById(R.id.usage_carrier_text);
             carrier.setText(info.carrier);
             final TextView period = (TextView) v.findViewById(R.id.usage_period_text);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index 3be97cc..21cf9ec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -18,7 +18,6 @@
 
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.graphics.drawable.AnimationDrawable;
 
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
@@ -61,35 +60,19 @@
         final boolean rotationLocked = mController.isRotationLocked();
         state.visible = mController.isRotationLockAffordanceVisible();
         final Resources res = mContext.getResources();
-        if (state.value != rotationLocked) {
-            state.value = rotationLocked;
-            final AnimationDrawable d = (AnimationDrawable) res.getDrawable(rotationLocked
-                    ? R.drawable.ic_qs_rotation_locked
-                    : R.drawable.ic_qs_rotation_unlocked);
-            state.icon = d;
-            mUiHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    d.start();
-                }
-            });
-        }
+        state.value = rotationLocked;
         if (rotationLocked) {
-            final int lockOrientation = mController.getRotationLockOrientation();
-            final int label = lockOrientation == Configuration.ORIENTATION_PORTRAIT
-                    ? R.string.quick_settings_rotation_locked_portrait_label
-                    : lockOrientation == Configuration.ORIENTATION_LANDSCAPE
-                    ? R.string.quick_settings_rotation_locked_landscape_label
-                    : R.string.quick_settings_rotation_locked_label;
+            final boolean portrait = res.getConfiguration().orientation
+                    != Configuration.ORIENTATION_LANDSCAPE;
+            final int label = portrait ? R.string.quick_settings_rotation_locked_portrait_label
+                    : R.string.quick_settings_rotation_locked_landscape_label;
+            final int icon = portrait ? R.drawable.ic_qs_rotation_portrait
+                    : R.drawable.ic_qs_rotation_landscape;
             state.label = mContext.getString(label);
-            if (state.icon == null) {
-                state.icon = res.getDrawable(R.drawable.ic_qs_rotation_15);
-            }
+            state.icon = mContext.getDrawable(icon);
         } else {
             state.label = mContext.getString(R.string.quick_settings_rotation_unlocked_label);
-            if (state.icon == null) {
-                state.icon = res.getDrawable(R.drawable.ic_qs_rotation_01);
-            }
+            state.icon = res.getDrawable(R.drawable.ic_qs_rotation_unlocked);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index a9a606f..b6d7d7e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -64,11 +64,9 @@
 
     Context mContext;
     SystemServicesProxy mSystemServicesProxy;
-
-    // Recents service binding
     Handler mHandler;
-    boolean mBootCompleted = false;
-    boolean mStartAnimationTriggered = false;
+    boolean mBootCompleted;
+    boolean mStartAnimationTriggered;
 
     // Task launching
     RecentsConfiguration mConfig;
@@ -95,9 +93,7 @@
     }
 
     public void onStart() {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|start]");
-        }
+        // Do nothing
     }
 
     public void onBootCompleted() {
@@ -106,9 +102,6 @@
 
     /** Shows the recents */
     public void onShowRecents(boolean triggeredFromAltTab, View statusBarView) {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|showRecents]");
-        }
         mStatusBarView = statusBarView;
         mTriggeredFromAltTab = triggeredFromAltTab;
 
@@ -121,10 +114,6 @@
 
     /** Hides the recents */
     public void onHideRecents(boolean triggeredFromAltTab) {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|hideRecents]");
-        }
-
         if (mBootCompleted) {
             if (isRecentsTopMost(getTopMostTask(), null)) {
                 // Notify recents to hide itself
@@ -139,13 +128,6 @@
 
     /** Toggles the alternate recents activity */
     public void onToggleRecents(View statusBarView) {
-        if (Console.Enabled) {
-            Console.logStartTracingTime(Constants.Log.App.TimeRecentsStartup,
-                    Constants.Log.App.TimeRecentsStartupKey);
-            Console.logStartTracingTime(Constants.Log.App.TimeRecentsLaunchTask,
-                    Constants.Log.App.TimeRecentsLaunchKey);
-            Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|toggleRecents]", "");
-        }
         mStatusBarView = statusBarView;
         mTriggeredFromAltTab = false;
 
@@ -223,14 +205,6 @@
             intent.setPackage(mContext.getPackageName());
             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
             mContext.sendBroadcast(intent);
-
-            // Time this path
-            if (Console.Enabled) {
-                Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
-                        Constants.Log.App.TimeRecentsStartupKey, "receivedToggleRecents");
-                Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
-                        Constants.Log.App.TimeRecentsLaunchKey, "receivedToggleRecents");
-            }
             mLastToggleTime = System.currentTimeMillis();
             return;
         } else {
@@ -395,11 +369,6 @@
                 startAlternateRecentsActivity(topTask, opts, null);
             }
         }
-
-        if (Console.Enabled) {
-            Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
-                    Constants.Log.App.TimeRecentsStartupKey, "startRecentsActivity");
-        }
         mLastToggleTime = System.currentTimeMillis();
     }
 
@@ -417,10 +386,9 @@
         intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, mTriggeredFromAltTab);
         intent.putExtra(EXTRA_TRIGGERED_FROM_TASK_ID, (topTask != null) ? topTask.id : -1);
         if (opts != null) {
-            mContext.startActivityAsUser(intent, opts.toBundle(), new UserHandle(
-                    UserHandle.USER_CURRENT));
+            mContext.startActivityAsUser(intent, opts.toBundle(), UserHandle.CURRENT);
         } else {
-            mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
+            mContext.startActivityAsUser(intent, UserHandle.CURRENT);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index 8a80b76..c49e244 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -32,7 +32,7 @@
             // Enables the filtering of tasks according to their grouping
             public static final boolean EnableTaskFiltering = false;
             // Enables clipping of tasks against each other
-            public static final boolean EnableTaskStackClipping = true;
+            public static final boolean EnableTaskStackClipping = false;
             // Enables tapping on the TaskBar to launch the task
             public static final boolean EnableTaskBarTouchEvents = true;
             // Enables app-info pane on long-pressing the icon
@@ -52,43 +52,6 @@
         }
     }
 
-    public static class Log {
-        public static class App {
-            public static final String TimeRecentsStartupKey = "startup";
-            public static final String TimeRecentsLaunchKey = "launchTask";
-            public static final String TimeRecentsScreenshotTransitionKey = "screenshot";
-            public static final boolean TimeRecentsStartup = false;
-            public static final boolean TimeRecentsLaunchTask = false;
-            public static final boolean TimeRecentsScreenshotTransition = false;
-
-
-            public static final boolean RecentsComponent = false;
-            public static final boolean TaskDataLoader = false;
-            public static final boolean SystemUIHandshake = false;
-            public static final boolean TimeSystemCalls = false;
-            public static final boolean Memory = false;
-            public static final boolean Search = false;
-        }
-
-        public static class UI {
-            public static final boolean Draw = false;
-            public static final boolean ClickEvents = false;
-            public static final boolean TouchEvents = false;
-            public static final boolean MeasureAndLayout = false;
-            public static final boolean HwLayers = false;
-            public static final boolean Focus = false;
-        }
-
-        public static class TaskStack {
-            public static final boolean SynchronizeViewsWithModel = false;
-        }
-
-        public static class ViewPool {
-            public static final boolean PoolCallbacks = false;
-        }
-    }
-
-    /** XXX: We are going to move almost all of these into a resource once they are nailed down. */
     public static class Values {
         public static class App {
             public static int AppWidgetHostId = 1024;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 56de0be..29a0262 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -52,16 +52,23 @@
 import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
 
-/* Activity */
+/**
+ * The main Recents activity that is started from AlternateRecentsComponent.
+ */
 public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks,
         RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks,
         FullscreenTransitionOverlayView.FullScreenTransitionViewCallbacks {
 
+    // Actions and Extras sent from AlternateRecentsComponent
     final static String EXTRA_TRIGGERED_FROM_ALT_TAB = "extra_triggered_from_alt_tab";
     final static String ACTION_START_ENTER_ANIMATION = "action_start_enter_animation";
     final static String ACTION_TOGGLE_RECENTS_ACTIVITY = "action_toggle_recents_activity";
     final static String ACTION_HIDE_RECENTS_ACTIVITY = "action_hide_recents_activity";
 
+    RecentsConfiguration mConfig;
+    boolean mVisible;
+
+    // Top level views
     RecentsView mRecentsView;
     SystemBarScrimViews mScrimViews;
     ViewStub mEmptyViewStub;
@@ -69,29 +76,29 @@
     ViewStub mFullscreenOverlayStub;
     FullscreenTransitionOverlayView mFullScreenOverlayView;
 
-    RecentsConfiguration mConfig;
-
+    // Search AppWidget
     RecentsAppWidgetHost mAppWidgetHost;
     AppWidgetProviderInfo mSearchAppWidgetInfo;
     AppWidgetHostView mSearchAppWidgetHostView;
 
-    boolean mVisible;
 
     // Runnables to finish the Recents activity
-    FinishRecentsRunnable mFinishRunnable = new FinishRecentsRunnable(true);
+    FinishRecentsRunnable mFinishRunnable = new FinishRecentsRunnable();
     FinishRecentsRunnable mFinishLaunchHomeRunnable;
 
     /**
-     * A Runnable to finish Recents either with/without a transition, and either by calling finish()
-     * or just launching the specified intent.
+     * A common Runnable to finish Recents either by calling finish() (with a custom animation) or
+     * launching Home with some ActivityOptions.  Generally we always launch home when we exit
+     * Recents rather than just finishing the activity since we don't know what is behind Recents in
+     * the task stack.  The only case where we finish() directly is when we are cancelling the full
+     * screen transition from the app.
      */
     class FinishRecentsRunnable implements Runnable {
-        boolean mUseCustomFinishTransition;
         Intent mLaunchIntent;
         ActivityOptions mLaunchOpts;
 
-        public FinishRecentsRunnable(boolean withTransition) {
-            mUseCustomFinishTransition = withTransition;
+        public FinishRecentsRunnable() {
+            // Do nothing
         }
 
         /**
@@ -111,77 +118,66 @@
             // Finish Recents
             if (mLaunchIntent != null) {
                 if (mLaunchOpts != null) {
-                    startActivityAsUser(mLaunchIntent, new UserHandle(UserHandle.USER_CURRENT));
+                    startActivityAsUser(mLaunchIntent, UserHandle.CURRENT);
                 } else {
-                    startActivityAsUser(mLaunchIntent, mLaunchOpts.toBundle(),
-                            new UserHandle(UserHandle.USER_CURRENT));
+                    startActivityAsUser(mLaunchIntent, mLaunchOpts.toBundle(), UserHandle.CURRENT);
                 }
             } else {
                 finish();
-                if (mUseCustomFinishTransition) {
-                    overridePendingTransition(R.anim.recents_to_launcher_enter,
-                            R.anim.recents_to_launcher_exit);
-                }
+                overridePendingTransition(R.anim.recents_to_launcher_enter,
+                        R.anim.recents_to_launcher_exit);
             }
         }
     }
 
-    // Broadcast receiver to handle messages from AlternateRecentsComponent
+    /**
+     * Broadcast receiver to handle messages from AlternateRecentsComponent.
+     */
     final BroadcastReceiver mServiceBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
-            if (Console.Enabled) {
-                Console.log(Constants.Log.App.SystemUIHandshake,
-                        "[RecentsActivity|serviceBroadcast]", action, Console.AnsiRed);
-            }
             if (action.equals(ACTION_HIDE_RECENTS_ACTIVITY)) {
                 if (intent.getBooleanExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, false)) {
-                    // Dismiss recents, launching the focused task
-                    dismissRecentsIfVisible();
+                    // If we are hiding from releasing Alt-Tab, dismiss Recents to the focused app
+                    dismissRecentsToFocusedTaskOrHome(false);
                 } else {
-                    // If we are mid-animation into Recents, then reverse it and finish
-                    if (mFullScreenOverlayView == null ||
-                            !mFullScreenOverlayView.cancelAnimateOnEnterRecents(mFinishRunnable)) {
-                        // Otherwise, either finish Recents, or launch Home directly
-                        ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(context,
-                                null, mFinishLaunchHomeRunnable, null);
-                        mRecentsView.startExitToHomeAnimation(
-                                new ViewAnimation.TaskViewExitContext(exitTrigger));
-                    }
+                    // Otherwise, dismiss Recents to Home
+                    dismissRecentsToHome(true);
                 }
             } else if (action.equals(ACTION_TOGGLE_RECENTS_ACTIVITY)) {
-                // Try and unfilter and filtered stacks
-                if (!mRecentsView.unfilterFilteredStacks()) {
-                    // If there are no filtered stacks, dismiss recents and launch the first task
-                    dismissRecentsIfVisible();
-                }
+                // If we are toggling Recents, then first unfilter any filtered stacks first
+                dismissRecentsToFocusedTaskOrHome(true);
             } else if (action.equals(ACTION_START_ENTER_ANIMATION)) {
                 // Try and start the enter animation (or restart it on configuration changed)
                 ReferenceCountedTrigger t = new ReferenceCountedTrigger(context, null, null, null);
                 mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(
                         mFullScreenOverlayView, t));
-                // Call our callback
                 onEnterAnimationTriggered();
             }
         }
     };
 
-    // Broadcast receiver to handle messages from the system
+    /**
+     * Broadcast receiver to handle messages from the system
+     */
     final BroadcastReceiver mSystemBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
-            if (action.equals(Intent.ACTION_SCREEN_OFF) && mVisible) {
-                mFinishLaunchHomeRunnable.run();
+            if (action.equals(Intent.ACTION_SCREEN_OFF)) {
+                // When the screen turns off, dismiss Recents to Home
+                dismissRecentsToHome(false);
             } else if (action.equals(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED)) {
-                // Refresh the search widget
+                // When the search activity changes, update the Search widget
                 refreshSearchWidget();
             }
         }
     };
 
-    // Debug trigger
+    /**
+     * A custom debug trigger to listen for a debug key chord.
+     */
     final DebugTrigger mDebugTrigger = new DebugTrigger(new Runnable() {
         @Override
         public void run() {
@@ -211,15 +207,17 @@
         mConfig.launchedToTaskId = launchIntent.getIntExtra(
                 AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_TASK_ID, -1);
 
-        // Add the default no-recents layout
-        if (mEmptyView == null) {
-            mEmptyView = mEmptyViewStub.inflate();
-        }
+        // Update the top level view's visibilities
         if (mConfig.launchedWithNoRecentTasks) {
+            if (mEmptyView == null) {
+                mEmptyView = mEmptyViewStub.inflate();
+            }
             mEmptyView.setVisibility(View.VISIBLE);
             mRecentsView.setSearchBarVisibility(View.GONE);
         } else {
-            mEmptyView.setVisibility(View.GONE);
+            if (mEmptyView != null) {
+                mEmptyView.setVisibility(View.GONE);
+            }
             if (mRecentsView.hasSearchBar()) {
                 mRecentsView.setSearchBarVisibility(View.VISIBLE);
             } else {
@@ -227,7 +225,7 @@
             }
         }
 
-        // Show the scrim if we animate into Recents without window transitions
+        // Animate the SystemUI scrims into view
         mScrimViews.prepareEnterRecentsAnimation();
     }
 
@@ -250,12 +248,6 @@
                     ssp.unbindSearchAppWidget(mAppWidgetHost, appWidgetId);
                     appWidgetId = -1;
                 }
-                if (Console.Enabled) {
-                    Console.log(Constants.Log.App.SystemUIHandshake,
-                            "[RecentsActivity|onCreate|settings|appWidgetId]",
-                            "Id: " + appWidgetId,
-                            Console.AnsiBlue);
-                }
             }
 
             // If there is no id, then bind a new search app widget
@@ -263,13 +255,6 @@
                 Pair<Integer, AppWidgetProviderInfo> widgetInfo =
                         ssp.bindSearchAppWidget(mAppWidgetHost);
                 if (widgetInfo != null) {
-                    if (Console.Enabled) {
-                        Console.log(Constants.Log.App.SystemUIHandshake,
-                                "[RecentsActivity|onCreate|searchWidget]",
-                                "Id: " + widgetInfo.first + " Info: " + widgetInfo.second,
-                                Console.AnsiBlue);
-                    }
-
                     // Save the app widget id into the settings
                     mConfig.updateSearchBarAppWidgetId(this, widgetInfo.first);
                     mSearchAppWidgetInfo = widgetInfo.second;
@@ -283,12 +268,6 @@
         if (Constants.DebugFlags.App.EnableSearchLayout) {
             int appWidgetId = mConfig.searchBarAppWidgetId;
             if (appWidgetId >= 0) {
-                if (Console.Enabled) {
-                    Console.log(Constants.Log.App.SystemUIHandshake,
-                            "[RecentsActivity|onCreate|addSearchAppWidgetView]",
-                            "Id: " + appWidgetId,
-                            Console.AnsiBlue);
-                }
                 mSearchAppWidgetHostView = mAppWidgetHost.createView(this, appWidgetId,
                         mSearchAppWidgetInfo);
                 Bundle opts = new Bundle();
@@ -305,28 +284,50 @@
     }
 
     /** Dismisses recents if we are already visible and the intent is to toggle the recents view */
-    boolean dismissRecentsIfVisible() {
+    boolean dismissRecentsToFocusedTaskOrHome(boolean checkFilteredStackState) {
         if (mVisible) {
-            // If we are mid-animation into Recents, then reverse it and finish
-            if (mFullScreenOverlayView == null ||
-                    !mFullScreenOverlayView.cancelAnimateOnEnterRecents(mFinishRunnable)) {
-                // If we have a focused task, then launch that task
-                if (!mRecentsView.launchFocusedTask()) {
-                    if (mConfig.launchedFromHome) {
-                        // Just start the animation out of recents
-                        ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
-                                null, mFinishLaunchHomeRunnable, null);
-                        mRecentsView.startExitToHomeAnimation(
-                                new ViewAnimation.TaskViewExitContext(exitTrigger));
-                    } else {
-                        // Otherwise, try and launch the first task
-                        if (!mRecentsView.launchFirstTask()) {
-                            // If there are no tasks, then just finish recents
-                            mFinishLaunchHomeRunnable.run();
-                        }
-                    }
-                }
+            // If we are mid-animation into Recents, reverse the animation now
+            if (mFullScreenOverlayView != null &&
+                mFullScreenOverlayView.cancelAnimateOnEnterRecents(mFinishRunnable)) return true;
+            // If we currently have filtered stacks, then unfilter those first
+            if (checkFilteredStackState &&
+                mRecentsView.unfilterFilteredStacks()) return true;
+            // If we have a focused Task, launch that Task now
+            if (mRecentsView.launchFocusedTask()) return true;
+            // If we launched from Home, then return to Home
+            if (mConfig.launchedFromHome) {
+                dismissRecentsToHomeRaw(true);
+                return true;
             }
+            // Otherwise, try and return to the first Task in the stack
+            if (mRecentsView.launchFirstTask()) return true;
+            // If none of the other cases apply, then just go Home
+            dismissRecentsToHomeRaw(true);
+            return true;
+        }
+        return false;
+    }
+
+    /** Dismisses Recents directly to Home. */
+    void dismissRecentsToHomeRaw(boolean animated) {
+        if (animated) {
+            ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
+                    null, mFinishLaunchHomeRunnable, null);
+            mRecentsView.startExitToHomeAnimation(
+                    new ViewAnimation.TaskViewExitContext(exitTrigger));
+        } else {
+            mFinishLaunchHomeRunnable.run();
+        }
+    }
+
+    /** Dismisses Recents directly to Home if we currently aren't transitioning. */
+    boolean dismissRecentsToHome(boolean animated) {
+        if (mVisible) {
+            // If we are mid-animation into Recents, reverse the animation now
+            if (mFullScreenOverlayView != null &&
+                mFullScreenOverlayView.cancelAnimateOnEnterRecents(mFinishRunnable)) return true;
+            // Return to Home
+            dismissRecentsToHomeRaw(animated);
             return true;
         }
         return false;
@@ -336,13 +337,6 @@
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        if (Console.Enabled) {
-            Console.logDivider(Constants.Log.App.SystemUIHandshake);
-            Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onCreate]",
-                    getIntent().getAction() + " visible: " + mVisible, Console.AnsiRed);
-            Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
-                    Constants.Log.App.TimeRecentsStartupKey, "onCreate");
-        }
 
         // Initialize the loader and the configuration
         RecentsTaskLoader.initialize(this);
@@ -410,11 +404,13 @@
     }
 
     void onConfigurationChange() {
+        // Update RecentsConfiguration
+        mConfig = RecentsConfiguration.reinitialize(this);
+
         // Try and start the enter animation (or restart it on configuration changed)
         ReferenceCountedTrigger t = new ReferenceCountedTrigger(this, null, null, null);
         mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(
                 mFullScreenOverlayView, t));
-        // Call our callback
         onEnterAnimationTriggered();
     }
 
@@ -423,18 +419,6 @@
         super.onNewIntent(intent);
         setIntent(intent);
 
-        if (Console.Enabled) {
-            Console.logDivider(Constants.Log.App.SystemUIHandshake);
-            Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onNewIntent]",
-                    intent.getAction() + " visible: " + mVisible, Console.AnsiRed);
-            Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
-                    Constants.Log.App.TimeRecentsStartupKey, "onNewIntent");
-        }
-
-        // Initialize the loader and the configuration
-        RecentsTaskLoader.initialize(this);
-        mConfig = RecentsConfiguration.reinitialize(this);
-
         // Update the recent tasks
         updateRecentsTasks(intent);
 
@@ -446,10 +430,6 @@
 
     @Override
     protected void onStart() {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onStart]", "",
-                    Console.AnsiRed);
-        }
         super.onStart();
 
         // Register the broadcast receiver to handle messages from our service
@@ -458,16 +438,10 @@
         filter.addAction(ACTION_TOGGLE_RECENTS_ACTIVITY);
         filter.addAction(ACTION_START_ENTER_ANIMATION);
         registerReceiver(mServiceBroadcastReceiver, filter);
-
-        mVisible = true;
     }
 
     @Override
     protected void onResume() {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onResume]", "",
-                    Console.AnsiRed);
-        }
         super.onResume();
 
         // Start listening for widget package changes if there is one bound, post it since we don't
@@ -485,63 +459,29 @@
                 }
             }, 1);
         }
-    }
 
-    @Override
-    public void onAttachedToWindow() {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.App.SystemUIHandshake,
-                    "[RecentsActivity|onAttachedToWindow]", "",
-                    Console.AnsiRed);
-        }
-        super.onAttachedToWindow();
-    }
-
-    @Override
-    public void onDetachedFromWindow() {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.App.SystemUIHandshake,
-                    "[RecentsActivity|onDetachedFromWindow]", "",
-                    Console.AnsiRed);
-        }
-        super.onDetachedFromWindow();
-    }
-
-    @Override
-    protected void onPause() {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onPause]", "",
-                    Console.AnsiRed);
-        }
-        super.onPause();
+        // Mark Recents as visible
+        mVisible = true;
     }
 
     @Override
     protected void onStop() {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onStop]", "",
-                    Console.AnsiRed);
-        }
         super.onStop();
 
         // Unregister the RecentsService receiver
         unregisterReceiver(mServiceBroadcastReceiver);
 
         // Stop listening for widget package changes if there was one bound
-        if (mConfig.searchBarAppWidgetId >= 0) {
+        if (mAppWidgetHost.isListening()) {
             mAppWidgetHost.stopListening();
         }
     }
 
     @Override
     protected void onDestroy() {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onDestroy]", "",
-                    Console.AnsiRed);
-        }
         super.onDestroy();
 
-        // Unregister the screen off receiver
+        // Unregister the system broadcast receivers
         unregisterReceiver(mSystemBroadcastReceiver);
         RecentsTaskLoader.getInstance().unregisterReceivers();
     }
@@ -583,26 +523,8 @@
         // Test mode where back does not do anything
         if (mConfig.debugModeEnabled) return;
 
-        // If we are mid-animation into Recents, then reverse it and finish
-        if (mFullScreenOverlayView == null ||
-                !mFullScreenOverlayView.cancelAnimateOnEnterRecents(mFinishRunnable)) {
-            // If we are currently filtering in any stacks, unfilter them first
-            if (!mRecentsView.unfilterFilteredStacks()) {
-                if (mConfig.launchedFromHome) {
-                    // Just start the animation out of recents
-                    ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
-                            null, mFinishLaunchHomeRunnable, null);
-                    mRecentsView.startExitToHomeAnimation(
-                            new ViewAnimation.TaskViewExitContext(exitTrigger));
-                } else {
-                    // Otherwise, try and launch the first task
-                    if (!mRecentsView.launchFirstTask()) {
-                        // If there are no tasks, then just finish recents
-                        mFinishLaunchHomeRunnable.run();
-                    }
-                }
-            }
-        }
+        // Dismiss Recents to the focused Task or Home
+        dismissRecentsToFocusedTaskOrHome(true);
     }
 
     /** Called when debug mode is triggered */
@@ -623,7 +545,7 @@
 
     /** Called when the enter recents animation is triggered. */
     public void onEnterAnimationTriggered() {
-        // Animate the scrims in
+        // Animate the SystemUI scrim views
         mScrimViews.startEnterRecentsAnimation();
     }
 
@@ -644,7 +566,7 @@
 
     @Override
     public void onExitToHomeAnimationTriggered() {
-        // Animate the scrims out
+        // Animate the SystemUI scrim views out
         mScrimViews.startExitRecentsAnimation();
     }
 
@@ -664,7 +586,6 @@
 
     @Override
     public void refreshSearchWidget() {
-        // Load the Search widget again
         bindSearchBarAppWidget();
         addSearchBarAppWidgetView();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
index 43d7a54..a63e167 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
@@ -33,6 +33,7 @@
     Context mContext;
     RecentsAppWidgetHostCallbacks mCb;
     RecentsConfiguration mConfig;
+    boolean mIsListening;
 
     public RecentsAppWidgetHost(Context context, int hostId) {
         super(context, hostId);
@@ -42,6 +43,7 @@
 
     public void startListening(RecentsAppWidgetHostCallbacks cb) {
         mCb = cb;
+        mIsListening = true;
         super.startListening();
     }
 
@@ -51,6 +53,11 @@
         // Ensure that we release any references to the callbacks
         mCb = null;
         mContext = null;
+        mIsListening = false;
+    }
+
+    public boolean isListening() {
+        return mIsListening;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index e62d989..439765e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -35,6 +35,7 @@
  * NOTE: We should not hold any references to a Context from a static instance */
 public class RecentsConfiguration {
     static RecentsConfiguration sInstance;
+    static int sPrevConfigurationHashCode;
 
     DisplayMetrics mDisplayMetrics;
 
@@ -138,7 +139,11 @@
         if (sInstance == null) {
             sInstance = new RecentsConfiguration(context);
         }
-        sInstance.update(context);
+        int configHashCode = context.getResources().getConfiguration().hashCode();
+        if (sPrevConfigurationHashCode != configHashCode) {
+            sInstance.update(context);
+            sPrevConfigurationHashCode = configHashCode;
+        }
         return sInstance;
     }
 
@@ -179,10 +184,8 @@
         transposeRecentsLayoutWithOrientation =
                 res.getBoolean(R.bool.recents_transpose_layout_with_orientation);
 
-        // Search bar
+        // Search Bar
         searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height);
-
-        // Update the search widget id
         searchBarAppWidgetId = settings.getInt(Constants.Values.App.Key_SearchAppWidgetId, -1);
 
         // Task stack
@@ -242,12 +245,6 @@
         // Nav bar scrim
         navBarScrimEnterDuration =
                 res.getInteger(R.integer.recents_nav_bar_scrim_enter_duration);
-
-        if (Console.Enabled) {
-            Console.log(Constants.Log.UI.MeasureAndLayout,
-                    "[RecentsConfiguration|orientation]", isLandscape ? "Landscape" : "Portrait",
-                    Console.AnsiGreen);
-        }
     }
 
     /** Updates the system insets */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
index 31825af..4c0ff48 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/ReferenceCountedTrigger.java
@@ -72,7 +72,12 @@
 
     /** Adds a runnable to the last-decrement runnables list. */
     public void addLastDecrementRunnable(Runnable r) {
+        // To ensure that the last decrement always calls, we increment and decrement after setting
+        // the last decrement runnable
+        boolean ensureLastDecrement = (mCount == 0);
+        if (ensureLastDecrement) increment();
         mLastDecRunnables.add(r);
+        if (ensureLastDecrement) decrement();
     }
 
     /** Decrements the ref count */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index b8beda6f..ced4043 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -31,6 +31,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
@@ -80,6 +81,8 @@
     ComponentName mAssistComponent;
 
     Bitmap mDummyIcon;
+    int mDummyThumbnailWidth;
+    int mDummyThumbnailHeight;
     Paint mBgProtectionPaint;
     Canvas mBgProtectionCanvas;
 
@@ -96,6 +99,13 @@
         mDisplay = mWm.getDefaultDisplay();
         mRecentsPackage = context.getPackageName();
 
+        // Get the dummy thumbnail width/heights
+        Resources res = context.getResources();
+        int wId = com.android.internal.R.dimen.thumbnail_width;
+        int hId = com.android.internal.R.dimen.thumbnail_height;
+        mDummyThumbnailWidth = res.getDimensionPixelSize(wId);
+        mDummyThumbnailHeight = res.getDimensionPixelSize(hId);
+
         // Create the protection paints
         mBgProtectionPaint = new Paint();
         mBgProtectionPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));
@@ -213,7 +223,8 @@
 
         // If we are mocking, then just return a dummy thumbnail
         if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
-            Bitmap thumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+            Bitmap thumbnail = Bitmap.createBitmap(mDummyThumbnailWidth, mDummyThumbnailHeight,
+                    Bitmap.Config.ARGB_8888);
             thumbnail.eraseColor(0xff333333);
             return thumbnail;
         }
@@ -239,6 +250,8 @@
      */
     public static Bitmap getThumbnail(ActivityManager activityManager, int taskId) {
         ActivityManager.TaskThumbnail taskThumbnail = activityManager.getTaskThumbnail(taskId);
+        if (taskThumbnail == null) return null;
+
         Bitmap thumbnail = taskThumbnail.mainThumbnail;
         ParcelFileDescriptor descriptor = taskThumbnail.thumbnailFileDescriptor;
         if (thumbnail == null && descriptor != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
index bda195b..607e155 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
@@ -16,16 +16,10 @@
 
 package com.android.systemui.recents.misc;
 
-import android.app.ActivityManager;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
 import android.graphics.Color;
 import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.ParcelFileDescriptor;
 import com.android.systemui.recents.RecentsConfiguration;
 
-import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 
@@ -73,22 +67,25 @@
         }
     }
 
-    /** Calculates the luminance-preserved greyscale of a given color. */
-    public static int colorToGreyscale(int color) {
-        return Math.round(0.2126f * Color.red(color) + 0.7152f * Color.green(color) +
-                0.0722f * Color.blue(color));
-    }
+    /** Calculates the constrast between two colors, using the algorithm provided by the WCAG v2. */
+    public static float computeContrastBetweenColors(int bg, int fg) {
+        float bgR = Color.red(bg) / 255f;
+        float bgG = Color.green(bg) / 255f;
+        float bgB = Color.blue(bg) / 255f;
+        bgR = (bgR < 0.03928f) ? bgR / 12.92f : (float) Math.pow((bgR + 0.055f) / 1.055f, 2.4f);
+        bgG = (bgG < 0.03928f) ? bgG / 12.92f : (float) Math.pow((bgG + 0.055f) / 1.055f, 2.4f);
+        bgB = (bgB < 0.03928f) ? bgB / 12.92f : (float) Math.pow((bgB + 0.055f) / 1.055f, 2.4f);
+        float bgL = 0.2126f * bgR + 0.7152f * bgG + 0.0722f * bgB;
+        
+        float fgR = Color.red(fg) / 255f;
+        float fgG = Color.green(fg) / 255f;
+        float fgB = Color.blue(fg) / 255f;
+        fgR = (fgR < 0.03928f) ? fgR / 12.92f : (float) Math.pow((fgR + 0.055f) / 1.055f, 2.4f);
+        fgG = (fgG < 0.03928f) ? fgG / 12.92f : (float) Math.pow((fgG + 0.055f) / 1.055f, 2.4f);
+        fgB = (fgB < 0.03928f) ? fgB / 12.92f : (float) Math.pow((fgB + 0.055f) / 1.055f, 2.4f);
+        float fgL = 0.2126f * fgR + 0.7152f * fgG + 0.0722f * fgB;
 
-    /** Returns the ideal color to draw on top of a specified background color. */
-    public static int getIdealColorForBackgroundColorGreyscale(int greyscale, int lightRes,
-                                                               int darkRes) {
-        return (greyscale < 128) ? lightRes : darkRes;
-    }
-    /** Returns the ideal drawable to draw on top of a specified background color. */
-    public static Drawable getIdealResourceForBackgroundColorGreyscale(int greyscale,
-                                                                       Drawable lightRes,
-                                                                       Drawable darkRes) {
-        return (greyscale < 128) ? lightRes : darkRes;
+        return Math.abs((fgL + 0.05f) / (bgL + 0.05f));
     }
 
     /** Sets some private shadow properties. */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/BitmapLruCache.java b/packages/SystemUI/src/com/android/systemui/recents/model/BitmapLruCache.java
index 1344729..757c07f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/BitmapLruCache.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/BitmapLruCache.java
@@ -29,6 +29,6 @@
     @Override
     protected int computeSize(Bitmap b) {
         // The cache size will be measured in kilobytes rather than number of items
-        return b.getAllocationByteCount() / 1024;
+        return b.getAllocationByteCount();
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/DrawableLruCache.java b/packages/SystemUI/src/com/android/systemui/recents/model/DrawableLruCache.java
index 61d19da..5b50358 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/DrawableLruCache.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/DrawableLruCache.java
@@ -31,6 +31,6 @@
         // The cache size will be measured in kilobytes rather than number of items
         // NOTE: this isn't actually correct, as the icon may be smaller
         int maxBytes = (d.getIntrinsicWidth() * d.getIntrinsicHeight() * 4);
-        return maxBytes / 1024;
+        return maxBytes;
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/KeyStoreLruCache.java b/packages/SystemUI/src/com/android/systemui/recents/model/KeyStoreLruCache.java
index 3ccca9a2..5f4fabe 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/KeyStoreLruCache.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/KeyStoreLruCache.java
@@ -73,11 +73,6 @@
         return mCache.get(key);
     }
 
-    /** Gets the previous task key that matches the specified key. */
-    final Task.TaskKey getKey(Task.TaskKey key) {
-        return mKeys.get(key);
-    }
-
     /** Puts an entry in the cache for a specific key. */
     final void put(Task.TaskKey key, V value) {
         mCache.put(key, value);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
index 2d50659..2f1c1c4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
@@ -45,7 +45,7 @@
         mSystemServicesProxy = new SystemServicesProxy(context);
         mCb = cb;
         try {
-            register(context, Looper.getMainLooper(), false);
+            register(context, Looper.getMainLooper(), true);
         } catch (IllegalStateException e) {
             e.printStackTrace();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index 854ea1c..71979c4f7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -27,38 +27,25 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.UserHandle;
-import android.util.Pair;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.misc.Console;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 
-import java.util.ArrayList;
 import java.util.Collections;
+import java.util.LinkedHashSet;
 import java.util.List;
-import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentLinkedQueue;
 
 
 /** A bitmap load queue */
 class TaskResourceLoadQueue {
     ConcurrentLinkedQueue<Task> mQueue = new ConcurrentLinkedQueue<Task>();
-    ConcurrentHashMap<Task.TaskKey, Boolean> mForceLoadSet =
-            new ConcurrentHashMap<Task.TaskKey, Boolean>();
-
-    static final Boolean sFalse = new Boolean(false);
 
     /** Adds a new task to the load queue */
-    void addTask(Task t, boolean forceLoad) {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.App.TaskDataLoader, "  [TaskResourceLoadQueue|addTask]");
-        }
+    void addTask(Task t) {
         if (!mQueue.contains(t)) {
             mQueue.add(t);
         }
-        if (forceLoad) {
-            mForceLoadSet.put(t.key, new Boolean(true));
-        }
         synchronized(this) {
             notifyAll();
         }
@@ -68,37 +55,18 @@
      * Retrieves the next task from the load queue, as well as whether we want that task to be
      * force reloaded.
      */
-    Pair<Task, Boolean> nextTask() {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.App.TaskDataLoader, "  [TaskResourceLoadQueue|nextTask]");
-        }
-        Task task = mQueue.poll();
-        Boolean forceLoadTask = null;
-        if (task != null) {
-            forceLoadTask = mForceLoadSet.remove(task.key);
-        }
-        if (forceLoadTask == null) {
-            forceLoadTask = sFalse;
-        }
-        return new Pair<Task, Boolean>(task, forceLoadTask);
+    Task nextTask() {
+        return mQueue.poll();
     }
 
     /** Removes a task from the load queue */
     void removeTask(Task t) {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.App.TaskDataLoader, "  [TaskResourceLoadQueue|removeTask]");
-        }
         mQueue.remove(t);
-        mForceLoadSet.remove(t.key);
     }
 
     /** Clears all the tasks from the load queue */
     void clearTasks() {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.App.TaskDataLoader, "  [TaskResourceLoadQueue|clearTasks]");
-        }
         mQueue.clear();
-        mForceLoadSet.clear();
     }
 
     /** Returns whether the load queue is empty */
@@ -119,19 +87,20 @@
     DrawableLruCache mApplicationIconCache;
     BitmapLruCache mThumbnailCache;
     Bitmap mDefaultThumbnail;
+    BitmapDrawable mDefaultApplicationIcon;
 
     boolean mCancelled;
     boolean mWaitingOnLoadQueue;
 
     /** Constructor, creates a new loading thread that loads task resources in the background */
-    public TaskResourceLoader(TaskResourceLoadQueue loadQueue,
-                              DrawableLruCache applicationIconCache,
-                              BitmapLruCache thumbnailCache,
-                              Bitmap defaultThumbnail) {
+    public TaskResourceLoader(TaskResourceLoadQueue loadQueue, DrawableLruCache applicationIconCache,
+                              BitmapLruCache thumbnailCache, Bitmap defaultThumbnail,
+                              BitmapDrawable defaultApplicationIcon) {
         mLoadQueue = loadQueue;
         mApplicationIconCache = applicationIconCache;
         mThumbnailCache = thumbnailCache;
         mDefaultThumbnail = defaultThumbnail;
+        mDefaultApplicationIcon = defaultApplicationIcon;
         mMainThreadHandler = new Handler();
         mLoadThread = new HandlerThread("Recents-TaskResourceLoader");
         mLoadThread.setPriority(Thread.NORM_PRIORITY - 1);
@@ -142,9 +111,6 @@
 
     /** Restarts the loader thread */
     void start(Context context) {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.App.TaskDataLoader, "[TaskResourceLoader|start]");
-        }
         mContext = context;
         mCancelled = false;
         mSystemServicesProxy = new SystemServicesProxy(context);
@@ -156,9 +122,6 @@
 
     /** Requests the loader thread to stop after the current iteration */
     void stop() {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.App.TaskDataLoader, "[TaskResourceLoader|stop]");
-        }
         // Mark as cancelled for the thread to pick up
         mCancelled = true;
         mSystemServicesProxy = null;
@@ -172,25 +135,13 @@
     @Override
     public void run() {
         while (true) {
-            if (Console.Enabled) {
-                Console.log(Constants.Log.App.TaskDataLoader,
-                        "[TaskResourceLoader|run|" + Thread.currentThread().getId() + "]");
-            }
             if (mCancelled) {
-                if (Console.Enabled) {
-                    Console.log(Constants.Log.App.TaskDataLoader,
-                            "[TaskResourceLoader|cancel|" + Thread.currentThread().getId() + "]");
-                }
                 // We have to unset the context here, since the background thread may be using it
                 // when we call stop()
                 mContext = null;
                 // If we are cancelled, then wait until we are started again
                 synchronized(mLoadThread) {
                     try {
-                        if (Console.Enabled) {
-                            Console.log(Constants.Log.App.TaskDataLoader,
-                                    "[TaskResourceLoader|waitOnLoadThreadCancelled]");
-                        }
                         mLoadThread.wait();
                     } catch (InterruptedException ie) {
                         ie.printStackTrace();
@@ -200,59 +151,38 @@
                 SystemServicesProxy ssp = mSystemServicesProxy;
 
                 // Load the next item from the queue
-                Pair<Task, Boolean> nextTaskData = mLoadQueue.nextTask();
-                final Task t = nextTaskData.first;
-                final boolean forceLoadTask = nextTaskData.second;
+                final Task t = mLoadQueue.nextTask();
                 if (t != null) {
-                    Drawable loadIcon = mApplicationIconCache.getCheckLastActiveTime(t.key);
-                    Bitmap loadThumbnail = mThumbnailCache.getCheckLastActiveTime(t.key);
-                    if (Console.Enabled) {
-                        Console.log(Constants.Log.App.TaskDataLoader,
-                                "  [TaskResourceLoader|load]",
-                                t + " icon: " + loadIcon + " thumbnail: " + loadThumbnail +
-                                        " forceLoad: " + forceLoadTask);
-                    }
-                    // Load the application icon
-                    if (loadIcon == null || forceLoadTask) {
+                    Drawable cachedIcon = mApplicationIconCache.getCheckLastActiveTime(t.key);
+                    Bitmap cachedThumbnail = mThumbnailCache.getCheckLastActiveTime(t.key);
+                    // Load the application icon if it is stale or we haven't cached one yet
+                    if (cachedIcon == null) {
+                        Drawable icon = null;
                         ActivityInfo info = ssp.getActivityInfo(t.key.baseIntent.getComponent(),
                                 t.userId);
-                        Drawable icon = ssp.getActivityIcon(info, t.userId);
-                        if (!mCancelled) {
-                            if (icon != null) {
-                                if (Console.Enabled) {
-                                    Console.log(Constants.Log.App.TaskDataLoader,
-                                            "    [TaskResourceLoader|loadIcon]", icon);
-                                }
-                                loadIcon = icon;
-                                mApplicationIconCache.put(t.key, icon);
-                            }
+                        if (info != null) {
+                            icon = ssp.getActivityIcon(info, t.userId);
                         }
+                        // If we can't load the icon, then set the default application icon into the
+                        // cache.  This will remain until the task's last active time is updated.
+                        cachedIcon = icon != null ? icon : mDefaultApplicationIcon;
+                        mApplicationIconCache.put(t.key, cachedIcon);
                     }
-                    // Load the thumbnail
-                    if (loadThumbnail == null || forceLoadTask) {
+                    // Load the thumbnail if it is stale or we haven't cached one yet
+                    if (cachedThumbnail == null) {
                         Bitmap thumbnail = ssp.getTaskThumbnail(t.key.id);
-                        if (!mCancelled) {
-                            if (thumbnail != null) {
-                                if (Console.Enabled) {
-                                    Console.log(Constants.Log.App.TaskDataLoader,
-                                            "    [TaskResourceLoader|loadThumbnail]", thumbnail);
-                                }
-                                thumbnail.setHasAlpha(false);
-                                loadThumbnail = thumbnail;
-                            } else {
-                                loadThumbnail = mDefaultThumbnail;
-                                Console.logError(mContext,
-                                        "Failed to load task top thumbnail for: " +
-                                                t.key.baseIntent.getComponent().getPackageName());
-                            }
-                            // We put the default thumbnail in the cache anyways
-                            mThumbnailCache.put(t.key, loadThumbnail);
+                        if (thumbnail != null) {
+                            thumbnail.setHasAlpha(false);
                         }
+                        // Even if we can't load the icon, we set the default thumbnail into the
+                        // cache.  This will remain until the task's last active time is updated.
+                        cachedThumbnail = thumbnail != null ? thumbnail : mDefaultThumbnail;
+                        mThumbnailCache.put(t.key, cachedThumbnail);
                     }
                     if (!mCancelled) {
                         // Notify that the task data has changed
-                        final Drawable newIcon = loadIcon;
-                        final Bitmap newThumbnail = loadThumbnail;
+                        final Drawable newIcon = cachedIcon;
+                        final Bitmap newThumbnail = cachedThumbnail;
                         mMainThreadHandler.post(new Runnable() {
                             @Override
                             public void run() {
@@ -266,10 +196,6 @@
                 if (!mCancelled && mLoadQueue.isEmpty()) {
                     synchronized(mLoadQueue) {
                         try {
-                            if (Console.Enabled) {
-                                Console.log(Constants.Log.App.TaskDataLoader,
-                                        "[TaskResourceLoader|waitOnLoadQueue]");
-                            }
                             mWaitingOnLoadQueue = true;
                             mLoadQueue.wait();
                             mWaitingOnLoadQueue = false;
@@ -306,22 +232,16 @@
     /** Private Constructor */
     private RecentsTaskLoader(Context context) {
         // Calculate the cache sizes, we just use a reasonable number here similar to those
-        // suggested in the Android docs, 1/8th for the thumbnail cache and 1/32 of the max memory
+        // suggested in the Android docs, 1/6th for the thumbnail cache and 1/30 of the max memory
         // for icons.
-        int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
-        mMaxThumbnailCacheSize = maxMemory / 8;
-        mMaxIconCacheSize = mMaxThumbnailCacheSize / 4;
+        int maxMemory = (int) Runtime.getRuntime().maxMemory();
+        mMaxThumbnailCacheSize = maxMemory / 6;
+        mMaxIconCacheSize = mMaxThumbnailCacheSize / 5;
         int iconCacheSize = Constants.DebugFlags.App.DisableBackgroundCache ? 1 :
                 mMaxIconCacheSize;
         int thumbnailCacheSize = Constants.DebugFlags.App.DisableBackgroundCache ? 1 :
                 mMaxThumbnailCacheSize;
 
-        if (Console.Enabled) {
-            Console.log(Constants.Log.App.TaskDataLoader,
-                    "[RecentsTaskLoader|init]", "thumbnailCache: " + thumbnailCacheSize +
-                    " iconCache: " + iconCacheSize);
-        }
-
         // Create the default assets
         Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
         icon.eraseColor(0x00000000);
@@ -340,14 +260,7 @@
         mApplicationIconCache = new DrawableLruCache(iconCacheSize);
         mThumbnailCache = new BitmapLruCache(thumbnailCacheSize);
         mLoader = new TaskResourceLoader(mLoadQueue, mApplicationIconCache, mThumbnailCache,
-                mDefaultThumbnail);
-
-        if (Console.Enabled) {
-            Console.log(Constants.Log.App.TaskDataLoader,
-                    "[RecentsTaskLoader|defaultBitmaps]",
-                    "icon: " + mDefaultApplicationIcon +
-                    " default thumbnail: " + mDefaultThumbnail, Console.AnsiRed);
-        }
+                mDefaultThumbnail, mDefaultApplicationIcon);
     }
 
     /** Initializes the recents task loader */
@@ -369,32 +282,18 @@
     }
 
     private static List<ActivityManager.RecentTaskInfo> getRecentTasks(SystemServicesProxy ssp) {
-        long t1 = System.currentTimeMillis();
-
         List<ActivityManager.RecentTaskInfo> tasks =
                 ssp.getRecentTasks(50, UserHandle.CURRENT.getIdentifier());
         Collections.reverse(tasks);
-        if (Console.Enabled) {
-            Console.log(Constants.Log.App.TimeSystemCalls,
-                    "[RecentsTaskLoader|getRecentTasks]",
-                    "" + (System.currentTimeMillis() - t1) + "ms");
-            Console.log(Constants.Log.App.TaskDataLoader,
-                    "[RecentsTaskLoader|tasks]", "" + tasks.size());
-        }
 
         return tasks;
     }
 
     /** Reload the set of recent tasks */
     public SpaceNode reload(Context context, int preloadCount) {
-        long t1 = System.currentTimeMillis();
-
-        if (Console.Enabled) {
-            Console.log(Constants.Log.App.TaskDataLoader, "[RecentsTaskLoader|reload]");
-        }
         RecentsConfiguration config = RecentsConfiguration.getInstance();
         Resources res = context.getResources();
-        ArrayList<Task> tasksToForceLoad = new ArrayList<Task>();
+        LinkedHashSet<Task> tasksToLoad = new LinkedHashSet<Task>();
         TaskStack stack = new TaskStack();
         SpaceNode root = new SpaceNode();
         root.setStack(stack);
@@ -404,7 +303,6 @@
         List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(ssp);
 
         // Add each task to the task stack
-        t1 = System.currentTimeMillis();
         int taskCount = tasks.size();
         for (int i = 0; i < taskCount; i++) {
             ActivityManager.RecentTaskInfo t = tasks.get(i);
@@ -434,37 +332,28 @@
 
             // Preload the specified number of apps
             if (i >= (taskCount - preloadCount)) {
-                if (Console.Enabled) {
-                    Console.log(Constants.Log.App.TaskDataLoader,
-                            "[RecentsTaskLoader|preloadTask]",
-                            "i: " + i + " task: " + t.baseIntent.getComponent().getPackageName());
-                }
-
                 // Load the icon from the cache if possible
                 task.applicationIcon = mApplicationIconCache.getCheckLastActiveTime(task.key);
                 if (task.applicationIcon == null) {
                     if (isForemostTask) {
                         // We force loading the application icon for the foremost task
                         task.applicationIcon = ssp.getActivityIcon(info, task.userId);
-                        if (task.applicationIcon != null) {
-                            mApplicationIconCache.put(task.key, task.applicationIcon);
-                        } else {
+                        if (task.applicationIcon == null) {
                             task.applicationIcon = mDefaultApplicationIcon;
                         }
+                        // Even if we can't load the icon we set the default application icon into
+                        // the cache.  This will remain until the task's last active time is updated.
+                        mApplicationIconCache.put(task.key, task.applicationIcon);
                     } else {
-                        // Either the task has updated, or we haven't cached any information for the
-                        // task, so reload it
-                        tasksToForceLoad.add(task);
+                        // Either the task has changed since the last active time, or it was not
+                        // previously cached, so try and load the task anew.
+                        tasksToLoad.add(task);
                     }
                 }
 
                 // Load the thumbnail (if possible and not the foremost task, from the cache)
                 task.thumbnail = mThumbnailCache.getCheckLastActiveTime(task.key);
                 if (task.thumbnail == null) {
-                    if (Console.Enabled) {
-                        Console.log(Constants.Log.App.TaskDataLoader,
-                                "[RecentsTaskLoader|loadingTaskThumbnail]");
-                    }
                     if (isForemostTask) {
                         // We force loading the thumbnail icon for the foremost task
                         task.thumbnail = ssp.getTaskThumbnail(task.key.id);
@@ -473,37 +362,30 @@
                         } else {
                             task.thumbnail = mDefaultThumbnail;
                         }
+                        // Even if we can't load the thumbnail we set the default thumbnail into
+                        // the cache.  This will remain until the task's last active time is updated.
                         mThumbnailCache.put(task.key, task.thumbnail);
                     } else {
-                        // Either the task has updated, or we haven't cached any information for the
-                        // task, so reload it
-                        tasksToForceLoad.add(task);
+                        // Either the task has changed since the last active time, or it was not
+                        // previously cached, so try and load the task anew.
+                        tasksToLoad.add(task);
                     }
                 }
             }
 
             // Add the task to the stack
-            if (Console.Enabled) {
-                Console.log(Constants.Log.App.TaskDataLoader,
-                        "  [RecentsTaskLoader|task]", t.baseIntent.getComponent().getPackageName());
-            }
             stack.addTask(task);
         }
-        if (Console.Enabled) {
-            Console.log(Constants.Log.App.TimeSystemCalls,
-                    "[RecentsTaskLoader|getAllTaskTopThumbnail]",
-                    "" + (System.currentTimeMillis() - t1) + "ms");
-        }
 
         // Simulate the groupings that we describe
-        stack.createSimulatedAffiliatedGroupings();
+        stack.createAffiliatedGroupings();
 
         // Start the task loader
         mLoader.start(context);
 
-        // Add all the tasks that we are force/re-loading
-        for (Task t : tasksToForceLoad) {
-            mLoadQueue.addTask(t, true);
+        // Add all the tasks that we are reloading
+        for (Task t : tasksToLoad) {
+            mLoadQueue.addTask(t);
         }
 
         // Update the package monitor with the list of packages to listen for
@@ -526,7 +408,7 @@
             stack.addTask(new Task(t.persistentId, true, t.baseIntent, t.affiliatedTaskId, null,
                     null, 0, 0, t.firstActiveTime, t.lastActiveTime, (i == (taskCount - 1))));
         }
-        stack.createSimulatedAffiliatedGroupings();
+        stack.createAffiliatedGroupings();
         return stack;
     }
 
@@ -535,12 +417,6 @@
         Drawable applicationIcon = mApplicationIconCache.get(t.key);
         Bitmap thumbnail = mThumbnailCache.get(t.key);
 
-        if (Console.Enabled) {
-            Console.log(Constants.Log.App.TaskDataLoader, "[RecentsTaskLoader|loadTask]",
-                    t + " applicationIcon: " + applicationIcon + " thumbnail: " + thumbnail +
-                            " thumbnailCacheSize: " + mThumbnailCache.size());
-        }
-
         boolean requiresLoad = false;
         if (applicationIcon == null) {
             applicationIcon = mDefaultApplicationIcon;
@@ -551,30 +427,19 @@
             requiresLoad = true;
         }
         if (requiresLoad) {
-            mLoadQueue.addTask(t, false);
+            mLoadQueue.addTask(t);
         }
         t.notifyTaskDataLoaded(thumbnail, applicationIcon);
     }
 
     /** Releases the task resource data back into the pool. */
     public void unloadTaskData(Task t) {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.App.TaskDataLoader,
-                    "[RecentsTaskLoader|unloadTask]", t +
-                    " thumbnailCacheSize: " + mThumbnailCache.size());
-        }
-
         mLoadQueue.removeTask(t);
         t.notifyTaskDataUnloaded(mDefaultThumbnail, mDefaultApplicationIcon);
     }
 
     /** Completely removes the resource data from the pool. */
     public void deleteTaskData(Task t, boolean notifyTaskDataUnloaded) {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.App.TaskDataLoader,
-                    "[RecentsTaskLoader|deleteTask]", t);
-        }
-
         mLoadQueue.removeTask(t);
         mThumbnailCache.remove(t.key);
         mApplicationIconCache.remove(t.key);
@@ -585,9 +450,6 @@
 
     /** Stops the task loader and clears all pending tasks */
     void stopLoader() {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.App.TaskDataLoader, "[RecentsTaskLoader|stopLoader]");
-        }
         mLoader.stop();
         mLoadQueue.clearTasks();
     }
@@ -608,11 +470,6 @@
      * out of memory.
      */
     public void onTrimMemory(int level) {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.App.Memory, "[RecentsTaskLoader|onTrimMemory]",
-                    Console.trimMemoryLevelToString(level));
-        }
-
         switch (level) {
             case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
                 // Stop the loader immediately when the UI is no longer visible
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 88e9f40..1670735 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -18,6 +18,7 @@
 
 import android.content.Intent;
 import android.graphics.Bitmap;
+import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import com.android.systemui.recents.misc.Utilities;
 
@@ -84,7 +85,7 @@
     public Drawable activityIcon;
     public String activityLabel;
     public int colorPrimary;
-    public int colorPrimaryGreyscale;
+    public boolean useLightOnPrimaryColor;
     public Bitmap thumbnail;
     public boolean isActive;
     public boolean canLockToTask;
@@ -104,7 +105,8 @@
         this.activityLabel = activityTitle;
         this.activityIcon = activityIcon;
         this.colorPrimary = colorPrimary;
-        this.colorPrimaryGreyscale = Utilities.colorToGreyscale(colorPrimary);
+        this.useLightOnPrimaryColor = Utilities.computeContrastBetweenColors(colorPrimary,
+                Color.WHITE) > 3f;
         this.isActive = isActive;
         this.canLockToTask = canLockToTask;
         this.userId = userId;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 7dd15a6..e3bcff0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -320,7 +320,7 @@
     /**
      * Temporary: This method will simulate affiliation groups by
      */
-    public void createSimulatedAffiliatedGroupings() {
+    public void createAffiliatedGroupings() {
         if (Constants.DebugFlags.App.EnableSimulatedTaskGroups) {
             HashMap<Task.TaskKey, Task> taskMap = new HashMap<Task.TaskKey, Task>();
             // Sort all tasks by increasing firstActiveTime of the task
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FullscreenTransitionOverlayView.java b/packages/SystemUI/src/com/android/systemui/recents/views/FullscreenTransitionOverlayView.java
index 57b8ea4..63f59be 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/FullscreenTransitionOverlayView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FullscreenTransitionOverlayView.java
@@ -22,8 +22,6 @@
 import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
@@ -34,9 +32,7 @@
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import com.android.systemui.R;
-import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.misc.Console;
 
 
 /**
@@ -152,11 +148,6 @@
     public void prepareAnimateOnEnterRecents(Bitmap screenshot) {
         if (!mConfig.launchedFromAppWithScreenshot) return;
 
-        if (Console.Enabled) {
-            Console.logStartTracingTime(Constants.Log.App.TimeRecentsScreenshotTransition,
-                    Constants.Log.App.TimeRecentsScreenshotTransitionKey);
-        }
-
         setClipTop(0);
         setClipBottom(getMeasuredHeight());
         setDim(0);
@@ -180,11 +171,6 @@
     /** Animates this view as it enters recents */
     public void animateOnEnterRecents(ViewAnimation.TaskViewEnterContext ctx,
                                       final Runnable postAnimRunnable) {
-        if (Console.Enabled) {
-            Console.logTraceTime(Constants.Log.App.TimeRecentsScreenshotTransition,
-                    Constants.Log.App.TimeRecentsScreenshotTransitionKey, "Starting");
-        }
-
         // Cancel the current animation
         if (mEnterAnimation != null) {
             mEnterAnimation.removeAllListeners();
@@ -226,11 +212,6 @@
                         mCb.onEnterAnimationComplete();
                         // Run the given post-anim runnable
                         postAnimRunnable.run();
-
-                        if (Console.Enabled) {
-                            Console.logTraceTime(Constants.Log.App.TimeRecentsScreenshotTransition,
-                                    Constants.Log.App.TimeRecentsScreenshotTransitionKey, "Completed");
-                        }
                     }
                 });
             }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 99b012e..7bb6144 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -141,20 +141,12 @@
                     TaskView tv = (TaskView) stackView.getChildAt(j);
                     Task task = tv.getTask();
                     if (tv.isFocusedTask()) {
-                        if (Console.Enabled) {
-                            Console.log(Constants.Log.UI.Focus, "[RecentsView|launchFocusedTask]",
-                                    "Found focused Task");
-                        }
                         onTaskViewClicked(stackView, tv, stack, task, false);
                         return true;
                     }
                 }
             }
         }
-        if (Console.Enabled) {
-            Console.log(Constants.Log.UI.Focus, "[RecentsView|launchFocusedTask]",
-                    "No Tasks focused");
-        }
         return false;
     }
 
@@ -191,10 +183,6 @@
 
     /** Requests all task stacks to start their enter-recents animation */
     public void startEnterRecentsAnimation(ViewAnimation.TaskViewEnterContext ctx) {
-        // Handle the case when there are no views by incrementing and decrementing after all
-        // animations are started.
-        ctx.postAnimationTrigger.increment();
-
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
             View child = getChildAt(i);
@@ -203,18 +191,10 @@
                 stackView.startEnterRecentsAnimation(ctx);
             }
         }
-
-        // Handle the case when there are no views by incrementing and decrementing after all
-        // animations are started.
-        ctx.postAnimationTrigger.decrement();
     }
 
     /** Requests all task stacks to start their exit-recents animation */
     public void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
-        // Handle the case when there are no views by incrementing and decrementing after all
-        // animations are started.
-        ctx.postAnimationTrigger.increment();
-
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
             View child = getChildAt(i);
@@ -224,10 +204,6 @@
             }
         }
 
-        // Handle the case when there are no views by incrementing and decrementing after all
-        // animations are started.
-        ctx.postAnimationTrigger.decrement();
-
         // Notify of the exit animation
         mCb.onExitToHomeAnimationTriggered();
     }
@@ -244,12 +220,6 @@
             if (searchBar != null) {
                 mSearchBar = searchBar;
                 addView(mSearchBar);
-
-                if (Console.Enabled) {
-                    Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsView|setSearchBar]",
-                            "" + (mSearchBar.getVisibility() == View.VISIBLE),
-                            Console.AnsiBlue);
-                }
             }
         }
     }
@@ -276,13 +246,6 @@
         int height = MeasureSpec.getSize(heightMeasureSpec);
         int heightMode = MeasureSpec.getMode(heightMeasureSpec);
 
-        if (Console.Enabled) {
-            Console.log(Constants.Log.UI.MeasureAndLayout, "[RecentsView|measure]",
-                    "width: " + width + " height: " + height, Console.AnsiGreen);
-            Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
-                    Constants.Log.App.TimeRecentsStartupKey, "RecentsView.onMeasure");
-        }
-
         // Get the search bar bounds and measure the search bar layout
         if (mSearchBar != null) {
             Rect searchBarSpaceBounds = new Rect();
@@ -319,13 +282,6 @@
      */
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.UI.MeasureAndLayout, "[RecentsView|layout]",
-                    new Rect(left, top, right, bottom) + " changed: " + changed, Console.AnsiGreen);
-            Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
-                    Constants.Log.App.TimeRecentsStartupKey, "RecentsView.onLayout");
-        }
-
         // Get the search bar bounds so that we lay it out
         if (mSearchBar != null) {
             Rect searchBarSpaceBounds = new Rect();
@@ -367,11 +323,6 @@
 
     @Override
     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.UI.MeasureAndLayout,
-                    "[RecentsView|fitSystemWindows]", "insets: " + insets, Console.AnsiGreen);
-        }
-
         // Update the configuration with the latest system insets and trigger a relayout
         mConfig.updateSystemInsets(insets.getSystemWindowInsets());
         requestLayout();
@@ -498,11 +449,6 @@
         final Runnable launchRunnable = new Runnable() {
             @Override
             public void run() {
-                if (Console.Enabled) {
-                    Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
-                            Constants.Log.App.TimeRecentsLaunchKey, "preStartActivity");
-                }
-
                 if (task.isActive) {
                     // Bring an active task to the foreground
                     RecentsTaskLoader.getInstance().getSystemServicesProxy()
@@ -531,19 +477,9 @@
                     // And clean up the old task
                     onTaskViewDismissed(task);
                 }
-
-                if (Console.Enabled) {
-                    Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
-                            Constants.Log.App.TimeRecentsLaunchKey, "startActivity");
-                }
             }
         };
 
-        if (Console.Enabled) {
-            Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
-                    Constants.Log.App.TimeRecentsLaunchKey, "onTaskLaunched");
-        }
-
         // Launch the app right away if there is no task view, otherwise, animate the icon out first
         if (tv == null) {
             post(launchRunnable);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
index 8409227a..e0298ab 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java
@@ -28,8 +28,6 @@
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.animation.LinearInterpolator;
-import com.android.systemui.recents.misc.Console;
-import com.android.systemui.recents.Constants;
 
 /**
  * This class facilitates swipe to dismiss. It defines an interface to be implemented by the
@@ -178,11 +176,6 @@
     }
 
     public boolean onInterceptTouchEvent(MotionEvent ev) {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.UI.TouchEvents,
-                    "[SwipeHelper|interceptTouchEvent]",
-                    Console.motionEventActionToString(ev.getAction()), Console.AnsiBlue);
-        }
         final int action = ev.getAction();
 
         switch (action) {
@@ -294,12 +287,6 @@
     }
 
     public boolean onTouchEvent(MotionEvent ev) {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.UI.TouchEvents,
-                    "[SwipeHelper|touchEvent]",
-                    Console.motionEventActionToString(ev.getAction()), Console.AnsiBlue);
-        }
-
         if (!mDragging) {
             if (!onInterceptTouchEvent(ev)) {
                 return mCanCurrViewBeDimissed;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
index db84962..deb9df3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
@@ -162,11 +162,10 @@
         }
         // Try and apply the system ui tint
         setBackgroundColor(t.colorPrimary);
-        mActivityDescription.setTextColor(Utilities.getIdealColorForBackgroundColorGreyscale(
-                t.colorPrimaryGreyscale, mConfig.taskBarViewLightTextColor,
-                mConfig.taskBarViewDarkTextColor));
-        mDismissButton.setImageDrawable(Utilities.getIdealResourceForBackgroundColorGreyscale(
-                t.colorPrimaryGreyscale, mLightDismissDrawable, mDarkDismissDrawable));
+        mActivityDescription.setTextColor(t.useLightOnPrimaryColor ?
+                mConfig.taskBarViewLightTextColor : mConfig.taskBarViewDarkTextColor);
+        mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ?
+                mLightDismissDrawable : mDarkDismissDrawable);
     }
 
     /** Unbinds the bar view from the task */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 599c590..7b52163 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -167,10 +167,6 @@
         requestSynchronizeStackViewsWithModel(0);
     }
     void requestSynchronizeStackViewsWithModel(int duration) {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.TaskStack.SynchronizeViewsWithModel,
-                    "[TaskStackView|requestSynchronize]", "" + duration + "ms", Console.AnsiYellow);
-        }
         if (!mStackViewsDirty) {
             invalidate(mStackAlgorithm.mStackRect);
         }
@@ -266,11 +262,6 @@
         if (visibleRangeOut != null) {
             visibleRangeOut[0] = frontMostVisibleIndex;
             visibleRangeOut[1] = backMostVisibleIndex;
-            if (Console.Enabled) {
-                Console.log(Constants.Log.TaskStack.SynchronizeViewsWithModel,
-                        "[TaskStackView|updateStackTransforms]",
-                        "Back: " + backMostVisibleIndex + " Front: " + frontMostVisibleIndex);
-            }
         }
     }
 
@@ -290,11 +281,6 @@
 
     /** Synchronizes the views with the model */
     void synchronizeStackViewsWithModel() {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.TaskStack.SynchronizeViewsWithModel,
-                    "[TaskStackView|synchronizeViewsWithModel]",
-                    "mStackViewsDirty: " + mStackViewsDirty, Console.AnsiYellow);
-        }
         if (mStackViewsDirty) {
             // Get all the task transforms
             ArrayList<Task> tasks = mStack.getTasks();
@@ -302,7 +288,6 @@
             int[] visibleRange = mTmpVisibleRange;
             updateStackTransforms(mCurrentTaskTransforms, tasks, stackScroll, visibleRange, false);
             TaskViewTransform tmpTransform = new TaskViewTransform();
-            TaskStack.GroupTaskIndex gti = new TaskStack.GroupTaskIndex();
 
             // Return all the invisible children to the pool
             HashMap<Task, TaskView> taskChildViewMap = getTaskChildViewMap();
@@ -345,16 +330,52 @@
                         mStackViewsAnimationDuration);
             }
 
-            if (Console.Enabled) {
-                Console.log(Constants.Log.TaskStack.SynchronizeViewsWithModel,
-                        "  [TaskStackView|viewChildren]", "" + getChildCount());
-            }
-
             mStackViewsAnimationDuration = 0;
             mStackViewsDirty = false;
         }
     }
 
+    /** Updates the clip for each of the task views. */
+    void clipTaskViews() {
+        // Update the clip on each task child
+        if (Constants.DebugFlags.App.EnableTaskStackClipping) {
+            int childCount = getChildCount();
+            for (int i = 0; i < childCount - 1; i++) {
+                TaskView tv = (TaskView) getChildAt(i);
+                TaskView nextTv = null;
+                TaskView tmpTv = null;
+                int clipBottom = 0;
+                if (tv.shouldClipViewInStack()) {
+                    // Find the next view to clip against
+                    int nextIndex = i;
+                    while (nextIndex < getChildCount()) {
+                        tmpTv = (TaskView) getChildAt(++nextIndex);
+                        if (tmpTv != null && tmpTv.shouldClipViewInStack()) {
+                            nextTv = tmpTv;
+                            break;
+                        }
+                    }
+
+                    // Clip against the next view, this is just an approximation since we are
+                    // stacked and we can make assumptions about the visibility of the this
+                    // task relative to the ones in front of it.
+                    if (nextTv != null) {
+                        // XXX: Can hash the visible rects for this run
+                        tv.getHitRect(mTmpRect);
+                        nextTv.getHitRect(mTmpRect2);
+                        clipBottom = (mTmpRect.bottom - mTmpRect2.top);
+                    }
+                }
+                tv.setClipFromBottom(clipBottom);
+            }
+        }
+        if (getChildCount() > 0) {
+            // The front most task should never be clipped
+            TaskView tv = (TaskView) getChildAt(getChildCount() - 1);
+            tv.setClipFromBottom(0);
+        }
+    }
+
     /** Sets the current stack scroll */
     public void setStackScroll(int value) {
         mStackScroll = value;
@@ -497,11 +518,6 @@
         mMaxScroll = mStackAlgorithm.mMaxScroll;
 
         // Debug logging
-        if (Constants.Log.UI.MeasureAndLayout) {
-            Console.log("  [TaskStack|minScroll] " + mMinScroll);
-            Console.log("  [TaskStack|maxScroll] " + mMaxScroll);
-        }
-
         if (boundScrollToNewMinMax) {
             boundScroll();
         }
@@ -523,9 +539,6 @@
 
     /** Focuses the task at the specified index in the stack */
     void focusTask(int taskIndex, boolean scrollToNewPosition) {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.UI.Focus, "[TaskStackView|focusTask]", "" + taskIndex);
-        }
         if (0 <= taskIndex && taskIndex < mStack.getTaskCount()) {
             mFocusedTaskIndex = taskIndex;
 
@@ -535,9 +548,6 @@
             Runnable postScrollRunnable = null;
             if (tv != null) {
                 tv.setFocusedTask();
-                if (Console.Enabled) {
-                    Console.log(Constants.Log.UI.Focus, "[TaskStackView|focusTask]", "Requesting focus");
-                }
             } else {
                 postScrollRunnable = new Runnable() {
                     @Override
@@ -546,10 +556,6 @@
                         TaskView tv = getChildViewForTask(t);
                         if (tv != null) {
                             tv.setFocusedTask();
-                            if (Console.Enabled) {
-                                Console.log(Constants.Log.UI.Focus, "[TaskStackView|focusTask]",
-                                        "Requesting focus after scroll animation");
-                            }
                         }
                     }
                 };
@@ -571,11 +577,6 @@
 
     /** Focuses the next task in the stack */
     void focusNextTask(boolean forward) {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.UI.Focus, "[TaskStackView|focusNextTask]", "" +
-                    mFocusedTaskIndex);
-        }
-
         // Find the next index to focus
         int numTasks = mStack.getTaskCount();
         if (mFocusedTaskIndex < 0) {
@@ -590,24 +591,12 @@
 
     /** Enables the hw layers and increments the hw layer requirement ref count */
     void addHwLayersRefCount(String reason) {
-        if (Console.Enabled) {
-            int refCount = mHwLayersTrigger.getCount();
-            Console.log(Constants.Log.UI.HwLayers,
-                    "[TaskStackView|addHwLayersRefCount] refCount: " +
-                            refCount + "->" + (refCount + 1) + " " + reason);
-        }
         mHwLayersTrigger.increment();
     }
 
     /** Decrements the hw layer requirement ref count and disables the hw layers when we don't
         need them anymore. */
     void decHwLayersRefCount(String reason) {
-        if (Console.Enabled) {
-            int refCount = mHwLayersTrigger.getCount();
-            Console.log(Constants.Log.UI.HwLayers,
-                    "[TaskStackView|decHwLayersRefCount] refCount: " +
-                            refCount + "->" + (refCount - 1) + " " + reason);
-        }
         mHwLayersTrigger.decrement();
     }
 
@@ -636,55 +625,11 @@
 
     @Override
     public void dispatchDraw(Canvas canvas) {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.UI.Draw, "[TaskStackView|dispatchDraw]", "",
-                    Console.AnsiPurple);
-        }
         synchronizeStackViewsWithModel();
+        clipTaskViews();
         super.dispatchDraw(canvas);
     }
 
-    @Override
-    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
-        if (Constants.DebugFlags.App.EnableTaskStackClipping) {
-            TaskView tv = (TaskView) child;
-            TaskView nextTv = null;
-            TaskView tmpTv = null;
-            if (tv.shouldClipViewInStack()) {
-                int curIndex = indexOfChild(tv);
-
-                // Find the next view to clip against
-                while (nextTv == null && curIndex < getChildCount()) {
-                    tmpTv = (TaskView) getChildAt(++curIndex);
-                    if (tmpTv != null && tmpTv.shouldClipViewInStack()) {
-                        nextTv = tmpTv;
-                    }
-                }
-
-                // Clip against the next view (if we aren't animating its alpha)
-                if (nextTv != null) {
-                    Rect curRect = tv.getClippingRect(mTmpRect);
-                    Rect nextRect = nextTv.getClippingRect(mTmpRect2);
-                    // The hit rects are relative to the task view, which needs to be offset by
-                    // the system bar height
-                    curRect.offset(0, mConfig.systemInsets.top);
-                    nextRect.offset(0, mConfig.systemInsets.top);
-                    // Compute the clip region
-                    Region clipRegion = new Region();
-                    clipRegion.op(curRect, Region.Op.UNION);
-                    clipRegion.op(nextRect, Region.Op.DIFFERENCE);
-                    // Clip the canvas
-                    int saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
-                    canvas.clipRegion(clipRegion);
-                    boolean invalidate = super.drawChild(canvas, child, drawingTime);
-                    canvas.restoreToCount(saveCount);
-                    return invalidate;
-                }
-            }
-        }
-        return super.drawChild(canvas, child, drawingTime);
-    }
-
     /** Computes the stack and task rects */
     public void computeRects(int width, int height, int insetLeft, int insetBottom) {
         // Compute the rects in the stack algorithm
@@ -703,25 +648,12 @@
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         int width = MeasureSpec.getSize(widthMeasureSpec);
         int height = MeasureSpec.getSize(heightMeasureSpec);
-        if (Console.Enabled) {
-            Console.log(Constants.Log.UI.MeasureAndLayout, "[TaskStackView|measure]",
-                    "width: " + width + " height: " + height +
-                            " awaitingFirstLayout: " + mAwaitingFirstLayout, Console.AnsiGreen);
-        }
 
         // Compute our stack/task rects
         Rect taskStackBounds = new Rect();
         mConfig.getTaskStackBounds(width, height, taskStackBounds);
         computeRects(width, height, taskStackBounds.left, mConfig.systemInsets.bottom);
 
-        // Debug logging
-        if (Constants.Log.UI.MeasureAndLayout) {
-            Console.log("  [TaskStack|fullRect] " + mStackAlgorithm.mRect);
-            Console.log("  [TaskStack|stackRect] " + mStackAlgorithm.mStackRect);
-            Console.log("  [TaskStack|stackRectSansPeek] " + mStackAlgorithm.mStackRectSansPeek);
-            Console.log("  [TaskStack|taskRect] " + mStackAlgorithm.mTaskRect);
-        }
-
         // If this is the first layout, then scroll to the front of the stack and synchronize the
         // stack views immediately
         if (mAwaitingFirstLayout) {
@@ -749,19 +681,6 @@
      */
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.UI.MeasureAndLayout, "[TaskStackView|layout]",
-                    "" + new Rect(left, top, right, bottom), Console.AnsiGreen);
-        }
-
-        // Debug logging
-        if (Constants.Log.UI.MeasureAndLayout) {
-            Console.log("  [TaskStack|fullRect] " + mStackAlgorithm.mRect);
-            Console.log("  [TaskStack|stackRect] " + mStackAlgorithm.mStackRect);
-            Console.log("  [TaskStack|stackRectSansPeek] " + mStackAlgorithm.mStackRectSansPeek);
-            Console.log("  [TaskStack|taskRect] " + mStackAlgorithm.mTaskRect);
-        }
-
         // Layout each of the children
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
@@ -1021,19 +940,12 @@
 
     @Override
     public TaskView createView(Context context) {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.ViewPool.PoolCallbacks, "[TaskStackView|createPoolView]");
-        }
         return (TaskView) mInflater.inflate(R.layout.recents_task_view, this, false);
     }
 
     @Override
     public void prepareViewToEnterPool(TaskView tv) {
         Task task = tv.getTask();
-        if (Console.Enabled) {
-            Console.log(Constants.Log.ViewPool.PoolCallbacks, "[TaskStackView|returnToPool]",
-                    tv.getTask() + " tv: " + tv);
-        }
 
         // Report that this tasks's data is no longer being used
         RecentsTaskLoader.getInstance().unloadTaskData(task);
@@ -1050,11 +962,6 @@
 
     @Override
     public void prepareViewToLeavePool(TaskView tv, Task task, boolean isNewView) {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.ViewPool.PoolCallbacks, "[TaskStackView|leavePool]",
-                    "isNewView: " + isNewView);
-        }
-
         // Rebind the task and request that this task's data be filled into the TaskView
         tv.onTaskBound(task);
         RecentsTaskLoader.getInstance().loadTaskData(task);
@@ -1083,10 +990,6 @@
         }
 
         // Add/attach the view to the hierarchy
-        if (Console.Enabled) {
-            Console.log(Constants.Log.ViewPool.PoolCallbacks, "  [TaskStackView|insertIndex]",
-                    "" + insertIndex);
-        }
         if (isNewView) {
             addView(tv, insertIndex);
 
@@ -1112,11 +1015,6 @@
 
     @Override
     public void onTaskViewAppIconClicked(TaskView tv) {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.UI.ClickEvents, "[TaskStack|Clicked|Icon]",
-                    tv.getTask() + " is currently filtered: " + mStack.hasFilteredTasks(),
-                    Console.AnsiCyan);
-        }
         if (Constants.DebugFlags.App.EnableTaskFiltering) {
             if (mStack.hasFilteredTasks()) {
                 mStack.unfilterTasks();
@@ -1135,11 +1033,6 @@
 
     @Override
     public void onTaskViewClicked(TaskView tv, Task task, boolean lockToTask) {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.UI.ClickEvents, "[TaskStack|Clicked|Thumbnail]",
-                    task + " cb: " + mCb);
-        }
-
         // Cancel any doze triggers
         mUIDozeTrigger.stopDozing();
 
@@ -1155,6 +1048,11 @@
         mStack.removeTask(task);
     }
 
+    @Override
+    public void onTaskViewClipStateChanged(TaskView tv) {
+        invalidate(mStackAlgorithm.mStackRect);
+    }
+
     /**** RecentsPackageMonitor.PackageCallbacks Implementation ****/
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
index 908e063..9c48896 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
@@ -135,9 +135,9 @@
         // Set the y translation
         if (boundedT < 0f) {
             transformOut.translationY = (int) ((Math.max(-numPeekCards, boundedT) /
-                    numPeekCards) * peekHeight - scaleYOffset - scaleBarYOffset);
+                    numPeekCards) * peekHeight - scaleYOffset);
         } else {
-            transformOut.translationY = (int) (boundedT * overlapHeight - scaleYOffset - scaleBarYOffset);
+            transformOut.translationY = (int) (boundedT * overlapHeight - scaleYOffset);
         }
 
         // Set the z translation
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 15ace13..bd4ea90 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -22,7 +22,6 @@
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.ViewParent;
-import com.android.systemui.recents.misc.Console;
 import com.android.systemui.recents.Constants;
 
 /* Handles touch events for a TaskStackView. */
@@ -100,12 +99,6 @@
 
     /** Touch preprocessing for handling below */
     public boolean onInterceptTouchEvent(MotionEvent ev) {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.UI.TouchEvents,
-                    "[TaskStackViewTouchHandler|interceptTouchEvent]",
-                    Console.motionEventActionToString(ev.getAction()), Console.AnsiBlue);
-        }
-
         // Return early if we have no children
         boolean hasChildren = (mSv.getChildCount() > 0);
         if (!hasChildren) {
@@ -186,12 +179,6 @@
 
     /** Handles touch events once we have intercepted them */
     public boolean onTouchEvent(MotionEvent ev) {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.UI.TouchEvents,
-                    "[TaskStackViewTouchHandler|touchEvent]",
-                    Console.motionEventActionToString(ev.getAction()), Console.AnsiBlue);
-        }
-
         // Short circuit if we have no children
         boolean hasChildren = (mSv.getChildCount() > 0);
         if (!hasChildren) {
@@ -290,16 +277,6 @@
                     int overscrollRange = (int) (Math.min(1f,
                             Math.abs((float) velocity / mMaximumVelocity)) *
                             Constants.Values.TaskStackView.TaskStackOverscrollRange);
-
-                    if (Console.Enabled) {
-                        Console.log(Constants.Log.UI.TouchEvents,
-                                "[TaskStackViewTouchHandler|fling]",
-                                "scroll: " + mSv.getStackScroll() + " velocity: " + velocity +
-                                        " maxVelocity: " + mMaximumVelocity +
-                                        " overscrollRange: " + overscrollRange,
-                                Console.AnsiGreen);
-                    }
-
                     // Fling scroll
                     mSv.mScroller.fling(0, mSv.getStackScroll(),
                             0, -velocity,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 5524e15..7e30047 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -46,8 +46,9 @@
     interface TaskViewCallbacks {
         public void onTaskViewAppIconClicked(TaskView tv);
         public void onTaskViewAppInfoClicked(TaskView tv);
-        public void onTaskViewClicked(TaskView tv, Task t, boolean lockToTask);
+        public void onTaskViewClicked(TaskView tv, Task task, boolean lockToTask);
         public void onTaskViewDismissed(TaskView tv);
+        public void onTaskViewClipStateChanged(TaskView tv);
     }
 
     RecentsConfiguration mConfig;
@@ -65,7 +66,7 @@
     boolean mIsFocused;
     boolean mIsStub;
     boolean mClipViewInStack;
-    Rect mTmpRect = new Rect();
+    int mClipFromBottom;
     Paint mLayerPaint = new Paint();
 
     TaskThumbnailView mThumbnailView;
@@ -118,7 +119,9 @@
         setOutlineProvider(new ViewOutlineProvider() {
             @Override
             public boolean getOutline(View view, Outline outline) {
-                int height = getHeight() - mMaxFooterHeight + mFooterHeight;
+                // The current height is measured with the footer, so account for the footer height
+                // and the current clip (in the stack)
+                int height = getMeasuredHeight() - mClipFromBottom - mMaxFooterHeight + mFooterHeight;
                 outline.setRoundRect(0, 0, getWidth(), height,
                         mConfig.taskViewRoundedCornerRadiusPx);
                 return true;
@@ -172,11 +175,6 @@
 
     /** Synchronizes this view's properties with the task's transform */
     void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform, int duration) {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.UI.Draw, "[TaskView|updateViewPropertiesToTaskTransform]",
-                    "duration: " + duration, Console.AnsiPurple);
-        }
-
         // Update the bar view
         mBarView.updateViewPropertiesToTaskTransform(toTransform, duration);
 
@@ -483,15 +481,6 @@
         mBarView.setNoUserInteractionState();
     }
 
-    /** Returns the rect we want to clip (it may not be the full rect) */
-    Rect getClippingRect(Rect outRect) {
-        getHitRect(outRect);
-        // XXX: We should get the hit rect of the thumbnail view and intersect, but this is faster
-        outRect.right = outRect.left + mThumbnailView.getRight();
-        outRect.bottom = outRect.top + mThumbnailView.getBottom();
-        return outRect;
-    }
-
     /** Enable the hw layers on this task view */
     void enableHwLayers() {
         mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
@@ -506,7 +495,7 @@
         mLockToAppButtonView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
     }
 
-    /** Sets the stubbed state of this task view. */
+    /** Sets the stubbed state of this task view.
     void setStubState(boolean isStub) {
         if (!mIsStub && isStub) {
             // This is now a stub task view, so clip to the bar height, hide the thumbnail
@@ -519,7 +508,7 @@
             mThumbnailView.setVisibility(View.VISIBLE);
         }
         mIsStub = isStub;
-    }
+    } */
 
     /**
      * Returns whether this view should be clipped, or any views below should clip against this
@@ -533,19 +522,26 @@
     void setClipViewInStack(boolean clip) {
         if (clip != mClipViewInStack) {
             mClipViewInStack = clip;
-            if (getParent() instanceof View) {
-                getHitRect(mTmpRect);
-                ((View) getParent()).invalidate(mTmpRect);
-            }
+            mCb.onTaskViewClipStateChanged(this);
+        }
+    }
+
+    void setClipFromBottom(int clipFromBottom) {
+        clipFromBottom = Math.max(0, Math.min(getMeasuredHeight(), clipFromBottom));
+        if (mClipFromBottom != clipFromBottom) {
+            mClipFromBottom = clipFromBottom;
+            invalidateOutline();
         }
     }
 
     /** Sets the footer height. */
-    public void setFooterHeight(int height) {
-        mFooterHeight = height;
-        invalidateOutline();
-        invalidate(0, getMeasuredHeight() - mMaxFooterHeight, getMeasuredWidth(),
-                getMeasuredHeight());
+    public void setFooterHeight(int footerHeight) {
+        if (footerHeight != mFooterHeight) {
+            mFooterHeight = footerHeight;
+            invalidateOutline();
+            invalidate(0, getMeasuredHeight() - mMaxFooterHeight, getMeasuredWidth(),
+                    getMeasuredHeight());
+        }
     }
 
     /** Gets the footer height. */
@@ -677,6 +673,7 @@
         mTask = t;
         mTask.setCallbacks(this);
         if (getMeasuredWidth() == 0) {
+            // If we haven't yet measured, we should just set the footer height with any animation
             animateFooterVisibility(t.canLockToTask, 0, 0);
         } else {
             animateFooterVisibility(t.canLockToTask, mConfig.taskViewLockToAppLongAnimDuration, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataController.java
index f2dfe05..33d68bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataController.java
@@ -20,6 +20,8 @@
 import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
 import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
 import static android.telephony.TelephonyManager.SIM_STATE_READY;
+import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
+import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
 
 import android.content.Context;
 import android.net.ConnectivityManager;
@@ -33,19 +35,23 @@
 import android.os.ServiceManager;
 import android.telephony.TelephonyManager;
 import android.text.format.DateUtils;
+import android.text.format.Time;
 import android.util.Log;
 
 import com.android.systemui.statusbar.policy.NetworkController.DataUsageInfo;
 
-import java.text.SimpleDateFormat;
 import java.util.Date;
+import java.util.Locale;
 
 public class MobileDataController {
     private static final String TAG = "MobileDataController";
-    private static final boolean DEBUG = true;
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    private static final SimpleDateFormat MMM_D = new SimpleDateFormat("MMM d");
+    private static final long DEFAULT_WARNING_LEVEL = 2L * 1024 * 1024 * 1024;
     private static final int FIELDS = FIELD_RX_BYTES | FIELD_TX_BYTES;
+    private static final StringBuilder PERIOD_BUILDER = new StringBuilder(50);
+    private static final java.util.Formatter PERIOD_FORMATTER = new java.util.Formatter(
+            PERIOD_BUILDER, Locale.getDefault());
 
     private final Context mContext;
     private final TelephonyManager mTelephonyManager;
@@ -87,6 +93,13 @@
         return null;
     }
 
+    private static Time addMonth(Time t, int months) {
+        final Time rt = new Time(t);
+        rt.set(t.monthDay, t.month + months, t.year);
+        rt.normalize(false);
+        return rt;
+    }
+
     public DataUsageInfo getDataUsageInfo() {
         final String subscriberId = getActiveSubscriberId(mContext);
         if (subscriberId == null) {
@@ -101,9 +114,28 @@
         try {
             final NetworkStatsHistory history = mSession.getHistoryForNetwork(template, FIELDS);
             final long now = System.currentTimeMillis();
-            // period = last 4 wks for now
-            final long start = now - DateUtils.WEEK_IN_MILLIS * 4;
-            final long end = now;
+            final long start, end;
+            if (policy != null && policy.cycleDay > 0) {
+                // period = determined from cycleDay
+                if (DEBUG) Log.d(TAG, "Cycle day=" + policy.cycleDay + " tz="
+                        + policy.cycleTimezone);
+                final Time nowTime = new Time(policy.cycleTimezone);
+                nowTime.setToNow();
+                final Time policyTime = new Time(nowTime);
+                policyTime.set(policy.cycleDay, policyTime.month, policyTime.year);
+                policyTime.normalize(false);
+                if (nowTime.after(policyTime)) {
+                    start = policyTime.toMillis(false);
+                    end = addMonth(policyTime, 1).toMillis(false);
+                } else {
+                    start = addMonth(policyTime, -1).toMillis(false);
+                    end = policyTime.toMillis(false);
+                }
+            } else {
+                // period = last 4 wks
+                end = now;
+                start = now - DateUtils.WEEK_IN_MILLIS * 4;
+            }
             final long callStart = System.currentTimeMillis();
             final NetworkStatsHistory.Entry entry = history.getValues(start, end, now, null);
             final long callEnd = System.currentTimeMillis();
@@ -115,12 +147,13 @@
             }
             final long totalBytes = entry.rxBytes + entry.txBytes;
             final DataUsageInfo usage = new DataUsageInfo();
-            usage.maxLevel = (long) (totalBytes / .4);
             usage.usageLevel = totalBytes;
-            usage.period = MMM_D.format(new Date(start)) + " - " + MMM_D.format(new Date(end));
+            usage.period = formatDateRange(start, end);
             if (policy != null) {
                 usage.limitLevel = policy.limitBytes > 0 ? policy.limitBytes : 0;
                 usage.warningLevel = policy.warningBytes > 0 ? policy.warningBytes : 0;
+            } else {
+                usage.warningLevel = DEFAULT_WARNING_LEVEL;
             }
             return usage;
         } catch (RemoteException e) {
@@ -178,6 +211,15 @@
         return actualSubscriberId;
     }
 
+    private String formatDateRange(long start, long end) {
+        final int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
+        synchronized (PERIOD_BUILDER) {
+            PERIOD_BUILDER.setLength(0);
+            return DateUtils.formatDateRange(mContext, PERIOD_FORMATTER, start, end, flags, null)
+                    .toString();
+        }
+    }
+
     public interface Callback {
         void onMobileDataEnabled(boolean enabled);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index d058bd0..6d8b400 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -61,7 +61,6 @@
     public static class DataUsageInfo {
         public String carrier;
         public String period;
-        public long maxLevel;
         public long limitLevel;
         public long warningLevel;
         public long usageLevel;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
index ad2cf75..0c5d2a5 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
@@ -908,6 +908,9 @@
 
             if (mDialog != null) {
                 mDialog.show();
+                if (mCallback != null) {
+                    mCallback.onVisible(true);
+                }
             }
         }
 
@@ -1160,6 +1163,9 @@
                         mDialog.dismiss();
                         clearRemoteStreamController();
                         mActiveStreamType = -1;
+                        if (mCallback != null) {
+                            mCallback.onVisible(false);
+                        }
                     }
                 }
                 synchronized (sConfirmSafeVolumeLock) {
@@ -1262,5 +1268,6 @@
     public interface Callback {
         void onZenSettings();
         void onInteraction();
+        void onVisible(boolean visible);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index e4f5870..375f94c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -99,6 +99,13 @@
                     kvm.userActivity();
                 }
             }
+
+            @Override
+            public void onVisible(boolean visible) {
+                if (mAudioManager != null && mVolumeController != null) {
+                    mAudioManager.notifyVolumeControllerVisible(mVolumeController, visible);
+                }
+            }
         });
         mDialogPanel = mPanel;
     }
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index adfa1f2..f431fdb 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -49,6 +49,9 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.media.AudioManager;
+import android.media.session.MediaController;
+import android.media.session.MediaSession;
+import android.media.session.MediaSessionLegacyHelper;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
@@ -205,6 +208,8 @@
 
     private Drawable mBackgroundDrawable;
 
+    private float mElevation;
+
     private int mFrameResource = 0;
 
     private int mTextColor = 0;
@@ -224,6 +229,7 @@
     private boolean mClosingActionMenu;
 
     private int mVolumeControlStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE;
+    private MediaController mMediaController;
 
     private AudioManager mAudioManager;
     private KeyguardManager mKeyguardManager;
@@ -1688,15 +1694,42 @@
 
         switch (keyCode) {
             case KeyEvent.KEYCODE_VOLUME_UP:
-            case KeyEvent.KEYCODE_VOLUME_DOWN:
+            case KeyEvent.KEYCODE_VOLUME_DOWN: {
+                int direction = keyCode == KeyEvent.KEYCODE_VOLUME_UP ? AudioManager.ADJUST_RAISE
+                        : AudioManager.ADJUST_LOWER;
+                // If we have a session send it the volume command, otherwise
+                // use the suggested stream.
+                if (mMediaController != null) {
+                    mMediaController.adjustVolumeBy(direction, AudioManager.FLAG_SHOW_UI);
+                } else {
+                    MediaSessionLegacyHelper.getHelper(getContext()).sendAdjustVolumeBy(
+                            mVolumeControlStreamType, direction, AudioManager.FLAG_SHOW_UI);
+                }
+                return true;
+            }
             case KeyEvent.KEYCODE_VOLUME_MUTE: {
-                // Similar code is in PhoneFallbackEventHandler in case the window
-                // doesn't have one of these.  In this case, we execute it here and
-                // eat the event instead, because we have mVolumeControlStreamType
-                // and they don't.
                 getAudioManager().handleKeyDown(event, mVolumeControlStreamType);
                 return true;
             }
+            // These are all the recognized media key codes in
+            // KeyEvent.isMediaKey()
+            case KeyEvent.KEYCODE_MEDIA_PLAY:
+            case KeyEvent.KEYCODE_MEDIA_PAUSE:
+            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+            case KeyEvent.KEYCODE_MUTE:
+            case KeyEvent.KEYCODE_HEADSETHOOK:
+            case KeyEvent.KEYCODE_MEDIA_STOP:
+            case KeyEvent.KEYCODE_MEDIA_NEXT:
+            case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+            case KeyEvent.KEYCODE_MEDIA_REWIND:
+            case KeyEvent.KEYCODE_MEDIA_RECORD:
+            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
+                if (mMediaController != null) {
+                    if (mMediaController.dispatchMediaButtonEvent(event)) {
+                        return true;
+                    }
+                }
+            }
 
             case KeyEvent.KEYCODE_MENU: {
                 onKeyDownPanel((featureId < 0) ? FEATURE_OPTIONS_PANEL : featureId, event);
@@ -1750,7 +1783,19 @@
 
         switch (keyCode) {
             case KeyEvent.KEYCODE_VOLUME_UP:
-            case KeyEvent.KEYCODE_VOLUME_DOWN:
+            case KeyEvent.KEYCODE_VOLUME_DOWN: {
+                // If we have a session send it the volume command, otherwise
+                // use the suggested stream.
+                if (mMediaController != null) {
+                    mMediaController.adjustVolumeBy(0, AudioManager.FLAG_PLAY_SOUND
+                            | AudioManager.FLAG_VIBRATE);
+                } else {
+                    MediaSessionLegacyHelper.getHelper(getContext()).sendAdjustVolumeBy(
+                            mVolumeControlStreamType, 0,
+                            AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE);
+                }
+                return true;
+            }
             case KeyEvent.KEYCODE_VOLUME_MUTE: {
                 // Similar code is in PhoneFallbackEventHandler in case the window
                 // doesn't have one of these.  In this case, we execute it here and
@@ -1759,6 +1804,25 @@
                 getAudioManager().handleKeyUp(event, mVolumeControlStreamType);
                 return true;
             }
+            // These are all the recognized media key codes in
+            // KeyEvent.isMediaKey()
+            case KeyEvent.KEYCODE_MEDIA_PLAY:
+            case KeyEvent.KEYCODE_MEDIA_PAUSE:
+            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+            case KeyEvent.KEYCODE_MUTE:
+            case KeyEvent.KEYCODE_HEADSETHOOK:
+            case KeyEvent.KEYCODE_MEDIA_STOP:
+            case KeyEvent.KEYCODE_MEDIA_NEXT:
+            case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+            case KeyEvent.KEYCODE_MEDIA_REWIND:
+            case KeyEvent.KEYCODE_MEDIA_RECORD:
+            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
+                if (mMediaController != null) {
+                    if (mMediaController.dispatchMediaButtonEvent(event)) {
+                        return true;
+                    }
+                }
+            }
 
             case KeyEvent.KEYCODE_MENU: {
                 onKeyUpPanel(featureId < 0 ? FEATURE_OPTIONS_PANEL : featureId,
@@ -3189,6 +3253,7 @@
                             + Integer.toHexString(mFrameResource));
                 }
             }
+            mElevation = a.getDimension(com.android.internal.R.styleable.Window_windowElevation, 0);
             mTextColor = a.getColor(com.android.internal.R.styleable.Window_textColor, 0xFF000000);
         }
 
@@ -3278,28 +3343,31 @@
         // Remaining setup -- of background and title -- that only applies
         // to top-level windows.
         if (getContainer() == null) {
-            Drawable drawable = mBackgroundDrawable;
+            final Drawable background;
             if (mBackgroundResource != 0) {
-                drawable = getContext().getDrawable(mBackgroundResource);
+                background = getContext().getDrawable(mBackgroundResource);
+            } else {
+                background = mBackgroundDrawable;
             }
-            mDecor.setWindowBackground(drawable);
-            drawable = null;
+            mDecor.setWindowBackground(background);
+
+            final Drawable frame;
             if (mFrameResource != 0) {
-                drawable = getContext().getDrawable(mFrameResource);
+                frame = getContext().getDrawable(mFrameResource);
+            } else {
+                frame = null;
             }
-            mDecor.setWindowFrame(drawable);
+            mDecor.setWindowFrame(frame);
 
-            // System.out.println("Text=" + Integer.toHexString(mTextColor) +
-            // " Sel=" + Integer.toHexString(mTextSelectedColor) +
-            // " Title=" + Integer.toHexString(mTitleColor));
-
-            if (mTitleColor == 0) {
-                mTitleColor = mTextColor;
-            }
+            mDecor.setElevation(mElevation);
 
             if (mTitle != null) {
                 setTitle(mTitle);
             }
+
+            if (mTitleColor == 0) {
+                mTitleColor = mTextColor;
+            }
             setTitleColor(mTitleColor);
         }
 
@@ -3773,6 +3841,16 @@
         return mVolumeControlStreamType;
     }
 
+    @Override
+    public void setMediaController(MediaController controller) {
+        mMediaController = controller;
+    }
+
+    @Override
+    public MediaController getMediaController() {
+        return mMediaController;
+    }
+
     private boolean isTranslucent() {
         TypedArray a = getWindowStyle();
         return a.getBoolean(a.getResourceId(
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 5bfde4d..c3a9dbe 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -3922,6 +3922,11 @@
                                 break;
                         }
 
+                        // Is it a *file* we need to drop?
+                        if (!isRestorableFile(info)) {
+                            okay = false;
+                        }
+
                         // If the policy is satisfied, go ahead and set up to pipe the
                         // data to the agent.
                         if (DEBUG && okay && mAgent != null) {
@@ -4082,9 +4087,9 @@
                             }
                         }
 
-                        // Problems setting up the agent communication, or an already-
-                        // ignored package: skip to the next tar stream entry by
-                        // reading and discarding this file.
+                        // Problems setting up the agent communication, an explicitly
+                        // dropped file, or an already-ignored package: skip to the
+                        // next stream entry by reading and discarding this file.
                         if (!okay) {
                             if (DEBUG) Slog.d(TAG, "[discarding file content]");
                             long bytesToConsume = (info.size + 511) & ~511;
@@ -4691,6 +4696,31 @@
             return info;
         }
 
+        private boolean isRestorableFile(FileMetadata info) {
+            if (FullBackup.CACHE_TREE_TOKEN.equals(info.domain)) {
+                if (MORE_DEBUG) {
+                    Slog.i(TAG, "Dropping cache file path " + info.path);
+                }
+                return false;
+            }
+
+            if (FullBackup.ROOT_TREE_TOKEN.equals(info.domain)) {
+                // It's possible this is "no-backup" dir contents in an archive stream
+                // produced on a device running a version of the OS that predates that
+                // API.  Respect the no-backup intention and don't let the data get to
+                // the app.
+                if (info.path.startsWith("no_backup/")) {
+                    if (MORE_DEBUG) {
+                        Slog.i(TAG, "Dropping no_backup file path " + info.path);
+                    }
+                    return false;
+                }
+            }
+
+            // Otherwise we think this file is good to go
+            return true;
+        }
+
         private void HEXLOG(byte[] block) {
             int offset = 0;
             int todo = block.length;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index cdb3835..543384f 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1189,6 +1189,9 @@
      */
     private boolean mUserIsMonkey;
 
+    /** Flag whether the device has a recents UI */
+    final boolean mHasRecents;
+
     final ServiceThread mHandlerThread;
     final MainHandler mHandler;
 
@@ -2205,6 +2208,9 @@
         mConfigurationSeq = mConfiguration.seq = 1;
         mProcessCpuTracker.init();
 
+        mHasRecents = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_hasRecents);
+
         mCompatModePackages = new CompatModePackages(this, systemDir, mHandler);
         mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler);
         mStackSupervisor = new ActivityStackSupervisor(this);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 1f92bf9..260a5b5 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -825,7 +825,7 @@
         prev.task.touchActiveTime();
         clearLaunchTime(prev);
         final ActivityRecord next = mStackSupervisor.topRunningActivityLocked();
-        if (next == null || next.noDisplay || next.task != prev.task) {
+        if (mService.mHasRecents && (next == null || next.noDisplay || next.task != prev.task)) {
             prev.updateThumbnail(screenshotActivities(prev), null);
         }
         stopFullyDrawnTraceIfNeeded();
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 44b0f28eb..f75bdab 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -89,9 +89,15 @@
     // If true, TV going to standby mode puts other devices also to standby.
     private boolean mAutoDeviceOff;
 
+    // If true, TV wakes itself up when receiving <Text/Image View On>.
+    private boolean mAutoWakeup;
+
     HdmiCecLocalDeviceTv(HdmiControlService service) {
         super(service, HdmiCecDeviceInfo.DEVICE_TV);
         mPrevPortId = Constants.INVALID_PORT_ID;
+        mAutoDeviceOff = mService.readBooleanSetting(Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
+                true);
+        mAutoWakeup = mService.readBooleanSetting(Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED, true);
     }
 
     @Override
@@ -121,8 +127,7 @@
     @ServiceThreadOnly
     protected void setPreferredAddress(int addr) {
         assertRunOnServiceThread();
-        SystemProperties.set(Constants.PROPERTY_PREFERRED_ADDRESS_TV,
-                String.valueOf(addr));
+        SystemProperties.set(Constants.PROPERTY_PREFERRED_ADDRESS_TV, String.valueOf(addr));
     }
 
     private void registerAudioPortUpdateListener() {
@@ -491,7 +496,7 @@
     @ServiceThreadOnly
     protected boolean handleTextViewOn(HdmiCecMessage message) {
         assertRunOnServiceThread();
-        if (mService.isPowerStandbyOrTransient()) {
+        if (mService.isPowerStandbyOrTransient() && mAutoWakeup) {
             mService.wakeUp();
         }
         // TODO: Connect to Hardware input manager to invoke TV App with the appropriate channel
@@ -1115,6 +1120,14 @@
     void setAutoDeviceOff(boolean enabled) {
         assertRunOnServiceThread();
         mAutoDeviceOff = enabled;
+        mService.writeBooleanSetting(Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, enabled);
+    }
+
+    @ServiceThreadOnly
+    void setAutoWakeup(boolean enabled) {
+        assertRunOnServiceThread();
+        mAutoWakeup = enabled;
+        mService.writeBooleanSetting(Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED, enabled);
     }
 
     @Override
@@ -1176,7 +1189,7 @@
         if (!mService.isControlEnabled()) {
             return;
         }
-        if (!initiatedByCec) {
+        if (!initiatedByCec && mAutoDeviceOff) {
             mService.sendCecCommand(HdmiCecMessageBuilder.buildStandby(
                     mAddress, Constants.ADDR_BROADCAST));
         }
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index a9a391b..7db5f0c 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -44,7 +44,6 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.provider.Settings.Global;
-import android.provider.Settings.SettingNotFoundException;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java
index 8222155..b3419c1 100644
--- a/services/core/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/core/java/com/android/server/location/GpsLocationProvider.java
@@ -62,6 +62,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.WorkSource;
 import android.provider.Settings;
@@ -70,6 +71,7 @@
 import android.telephony.SmsMessage;
 import android.telephony.TelephonyManager;
 import android.telephony.gsm.GsmCellLocation;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.NtpTrustedTime;
 
@@ -85,6 +87,8 @@
 import java.util.Map.Entry;
 import java.util.Properties;
 
+import libcore.io.IoUtils;
+
 /**
  * A GPS implementation of LocationProvider used by LocationManager.
  *
@@ -201,7 +205,9 @@
     private static final int AGPS_SETID_TYPE_IMSI = 1;
     private static final int AGPS_SETID_TYPE_MSISDN = 2;
 
-    private static final String PROPERTIES_FILE = "/etc/gps.conf";
+    private static final String PROPERTIES_FILE_PREFIX = "/etc/gps";
+    private static final String PROPERTIES_FILE_SUFFIX = ".conf";
+    private static final String DEFAULT_PROPERTIES_FILE = PROPERTIES_FILE_PREFIX + PROPERTIES_FILE_SUFFIX;
 
     private static final int GPS_GEOFENCE_UNAVAILABLE = 1<<0L;
     private static final int GPS_GEOFENCE_AVAILABLE = 1<<1L;
@@ -441,6 +447,44 @@
         return native_is_supported();
     }
 
+    private boolean loadPropertiesFile(String filename) {
+        mProperties = new Properties();
+        try {
+            File file = new File(filename);
+            FileInputStream stream = null;
+            try {
+                stream = new FileInputStream(file);
+                mProperties.load(stream);
+            } finally {
+                IoUtils.closeQuietly(stream);
+            }
+
+            mSuplServerHost = mProperties.getProperty("SUPL_HOST");
+            String portString = mProperties.getProperty("SUPL_PORT");
+            if (mSuplServerHost != null && portString != null) {
+                try {
+                    mSuplServerPort = Integer.parseInt(portString);
+                } catch (NumberFormatException e) {
+                    Log.e(TAG, "unable to parse SUPL_PORT: " + portString);
+                }
+            }
+
+            mC2KServerHost = mProperties.getProperty("C2K_HOST");
+            portString = mProperties.getProperty("C2K_PORT");
+            if (mC2KServerHost != null && portString != null) {
+                try {
+                    mC2KServerPort = Integer.parseInt(portString);
+                } catch (NumberFormatException e) {
+                    Log.e(TAG, "unable to parse C2K_PORT: " + portString);
+                }
+            }
+        } catch (IOException e) {
+            Log.w(TAG, "Could not open GPS configuration file " + filename);
+            return false;
+        }
+        return true;
+    }
+
     public GpsLocationProvider(Context context, ILocationManager ilocationManager,
             Looper looper) {
         mContext = context;
@@ -469,34 +513,15 @@
         mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
                 BatteryStats.SERVICE_NAME));
 
-        mProperties = new Properties();
-        try {
-            File file = new File(PROPERTIES_FILE);
-            FileInputStream stream = new FileInputStream(file);
-            mProperties.load(stream);
-            stream.close();
+        boolean propertiesLoaded = false;
+        final String gpsHardware = SystemProperties.get("ro.hardware.gps");
+        if (!TextUtils.isEmpty(gpsHardware)) {
+            final String propFilename = PROPERTIES_FILE_PREFIX + "." + gpsHardware + PROPERTIES_FILE_SUFFIX;
+            propertiesLoaded = loadPropertiesFile(propFilename);
+        }
 
-            mSuplServerHost = mProperties.getProperty("SUPL_HOST");
-            String portString = mProperties.getProperty("SUPL_PORT");
-            if (mSuplServerHost != null && portString != null) {
-                try {
-                    mSuplServerPort = Integer.parseInt(portString);
-                } catch (NumberFormatException e) {
-                    Log.e(TAG, "unable to parse SUPL_PORT: " + portString);
-                }
-            }
-
-            mC2KServerHost = mProperties.getProperty("C2K_HOST");
-            portString = mProperties.getProperty("C2K_PORT");
-            if (mC2KServerHost != null && portString != null) {
-                try {
-                    mC2KServerPort = Integer.parseInt(portString);
-                } catch (NumberFormatException e) {
-                    Log.e(TAG, "unable to parse C2K_PORT: " + portString);
-                }
-            }
-        } catch (IOException e) {
-            Log.w(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE);
+        if (!propertiesLoaded) {
+            loadPropertiesFile(DEFAULT_PROPERTIES_FILE);
         }
 
         // construct handler, listen for events
diff --git a/services/core/java/com/android/server/media/MediaRouteProviderProxy.java b/services/core/java/com/android/server/media/MediaRouteProviderProxy.java
deleted file mode 100644
index b31153b..0000000
--- a/services/core/java/com/android/server/media/MediaRouteProviderProxy.java
+++ /dev/null
@@ -1,419 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.media;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.media.routeprovider.IRouteConnection;
-import android.media.routeprovider.IRouteProvider;
-import android.media.routeprovider.IRouteProviderCallback;
-import android.media.routeprovider.RouteProviderService;
-import android.media.routeprovider.RouteRequest;
-import android.media.session.RouteEvent;
-import android.media.session.RouteInfo;
-import android.media.session.MediaSession;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.os.UserHandle;
-import android.util.Log;
-import android.util.Slog;
-
-import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * System representation and interface to a MediaRouteProvider. This class is
- * not thread safe so all calls should be made on the main thread.
- */
-public class MediaRouteProviderProxy {
-    private static final String TAG = "MRPProxy";
-    private static final boolean DEBUG = true;
-
-    private static final int MAX_RETRIES = 3;
-
-    private final Object mLock = new Object();
-    private final Context mContext;
-    private final String mId;
-    private final ComponentName mComponentName;
-    private final int mUserId;
-    // Interfaces declared in the manifest
-    private final ArrayList<String> mInterfaces = new ArrayList<String>();
-    private final ArrayList<RouteConnectionRecord> mConnections
-            = new ArrayList<RouteConnectionRecord>();
-    // The sessions that have a route from this provider selected
-    private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>();
-    private final Handler mHandler = new Handler();
-
-    private Intent mBindIntent;
-    private IRouteProvider mBinder;
-    private boolean mRunning;
-    private boolean mPaused;
-    private boolean mInterested;
-    private boolean mBound;
-    private int mRetryCount;
-
-    private RoutesListener mRouteListener;
-
-    public MediaRouteProviderProxy(Context context, String id, ComponentName component, int uid,
-            ArrayList<String> interfaces) {
-        mContext = context;
-        mId = id;
-        mComponentName = component;
-        mUserId = uid;
-        if (interfaces != null) {
-            mInterfaces.addAll(interfaces);
-        }
-        mBindIntent = new Intent(RouteProviderService.SERVICE_INTERFACE);
-        mBindIntent.setComponent(mComponentName);
-    }
-
-    public void destroy() {
-        stop();
-        mSessions.clear();
-        updateBinding();
-    }
-
-    /**
-     * Send any cleanup messages and unbind from the media route provider
-     */
-    public void stop() {
-        if (mRunning) {
-            mRunning = false;
-            mRetryCount = 0;
-            updateBinding();
-        }
-    }
-
-    /**
-     * Bind to the media route provider and perform any setup needed
-     */
-    public void start() {
-        if (!mRunning) {
-            mRunning = true;
-            updateBinding();
-        }
-    }
-
-    /**
-     * Set whether or not this provider is currently interesting to the system.
-     * In the future this may take a list of interfaces instead.
-     *
-     * @param interested True if we want to connect to this provider
-     */
-    public void setInterested(boolean interested) {
-        mInterested = interested;
-        updateBinding();
-    }
-
-    /**
-     * Set a listener to get route updates on.
-     *
-     * @param listener The listener to receive updates on.
-     */
-    public void setRoutesListener(RoutesListener listener) {
-        mRouteListener = listener;
-    }
-
-    /**
-     * Send a request to the Provider to get all the routes that the session can
-     * use.
-     *
-     * @param record The session to get routes for.
-     * @param requestId An id to identify this request.
-     */
-    public void getRoutes(MediaSessionRecord record, final int requestId) {
-        // TODO change routes to have a system global id and maintain a mapping
-        // to the original route
-        if (mBinder == null) {
-            Log.wtf(TAG, "Attempted to call getRoutes without a binder connection");
-            return;
-        }
-        List<RouteRequest> requests = record.getRouteRequests();
-        final String sessionId = record.getSessionInfo().getId();
-        try {
-            mBinder.getAvailableRoutes(requests, new ResultReceiver(mHandler) {
-                @Override
-                protected void onReceiveResult(int resultCode, Bundle resultData) {
-                    if (resultCode != RouteProviderService.RESULT_SUCCESS) {
-                        // ignore failures, just means no routes were generated
-                        return;
-                    }
-                    ArrayList<RouteInfo> routes
-                            = resultData.getParcelableArrayList(RouteProviderService.KEY_ROUTES);
-                    ArrayList<RouteInfo> sysRoutes = new ArrayList<RouteInfo>();
-                    for (int i = 0; i < routes.size(); i++) {
-                        RouteInfo route = routes.get(i);
-                        RouteInfo.Builder bob = new RouteInfo.Builder(route);
-                        bob.setProviderId(mId);
-                        sysRoutes.add(bob.build());
-                    }
-                    if (mRouteListener != null) {
-                        mRouteListener.onRoutesUpdated(sessionId, sysRoutes, requestId);
-                    }
-                }
-            });
-        } catch (RemoteException e) {
-            Log.d(TAG, "Error in getRoutes", e);
-        }
-    }
-
-    /**
-     * Try connecting again if we've been disconnected.
-     */
-    public void rebindIfDisconnected() {
-        if (mBinder == null && shouldBind()) {
-            unbind();
-            bind();
-        }
-    }
-
-    /**
-     * Send a request to connect to a route.
-     *
-     * @param session The session that is trying to connect.
-     * @param route The route it is connecting to.
-     * @param request The request with the connection parameters.
-     * @return true if the request was sent, false otherwise.
-     */
-    public boolean connectToRoute(MediaSessionRecord session, final RouteInfo route,
-            final RouteRequest request) {
-        final String sessionId = session.getSessionInfo().getId();
-        try {
-            mBinder.connect(route, request, new ResultReceiver(mHandler) {
-                @Override
-                protected void onReceiveResult(int resultCode, Bundle resultData) {
-                    if (resultCode != RouteProviderService.RESULT_SUCCESS) {
-                        // TODO handle connection failure
-                        return;
-                    }
-                    IBinder binder = resultData.getBinder(RouteProviderService.KEY_CONNECTION);
-                    IRouteConnection connection = null;
-                    if (binder != null) {
-                        connection = IRouteConnection.Stub.asInterface(binder);
-                    }
-
-                    if (connection != null) {
-                        RouteConnectionRecord record = new RouteConnectionRecord(
-                                connection, mComponentName.getPackageName(), mUserId);
-                        mConnections.add(record);
-                        if (mRouteListener != null) {
-                            mRouteListener.onRouteConnected(sessionId, route, request, record);
-                        }
-                    }
-                }
-            });
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error connecting to route.", e);
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Check if this is the provider you're looking for.
-     */
-    public boolean hasComponentName(String packageName, String className) {
-        return mComponentName.getPackageName().equals(packageName)
-                && mComponentName.getClassName().equals(className);
-    }
-
-    /**
-     * Get the unique id for this provider.
-     *
-     * @return The provider's id.
-     */
-    public String getId() {
-        return mId;
-    }
-
-    public void addSession(MediaSessionRecord session) {
-        mSessions.add(session);
-    }
-
-    public void removeSession(MediaSessionRecord session) {
-        mSessions.remove(session);
-        updateBinding();
-    }
-
-    public int getSessionCount() {
-        return mSessions.size();
-    }
-
-    public void dump(PrintWriter pw, String prefix) {
-        pw.println(prefix + mId + " " + this);
-        String indent = prefix + "  ";
-
-        pw.println(indent + "component=" + mComponentName.toString());
-        pw.println(indent + "user id=" + mUserId);
-        pw.println(indent + "interfaces=" + mInterfaces.toString());
-        pw.println(indent + "connections=" + mConnections.toString());
-        pw.println(indent + "running=" + mRunning);
-        pw.println(indent + "interested=" + mInterested);
-        pw.println(indent + "bound=" + mBound);
-    }
-
-    private void updateBinding() {
-        if (shouldBind()) {
-            bind();
-        } else {
-            unbind();
-        }
-    }
-
-    // We want to bind as long as we're interested in this provider or there are
-    // sessions connected to it.
-    private boolean shouldBind() {
-        return (mRunning && mInterested) || (!mSessions.isEmpty());
-    }
-
-    private void bind() {
-        if (!mBound) {
-            if (DEBUG) {
-                Slog.d(TAG, this + ": Binding");
-            }
-
-            try {
-                mBound = mContext.bindServiceAsUser(mBindIntent, mServiceConn,
-                        Context.BIND_AUTO_CREATE, new UserHandle(mUserId));
-                if (!mBound && DEBUG) {
-                    Slog.d(TAG, this + ": Bind failed");
-                }
-            } catch (SecurityException ex) {
-                if (DEBUG) {
-                    Slog.d(TAG, this + ": Bind failed", ex);
-                }
-            }
-        }
-    }
-
-    private void unbind() {
-        if (mBound) {
-            if (DEBUG) {
-                Slog.d(TAG, this + ": Unbinding");
-            }
-
-            mBound = false;
-            mContext.unbindService(mServiceConn);
-        }
-    }
-
-    private RouteConnectionRecord getConnectionLocked(IBinder binder) {
-        for (int i = mConnections.size() - 1; i >= 0; i--) {
-            RouteConnectionRecord record = mConnections.get(i);
-            if (record.isConnection(binder)) {
-                return record;
-            }
-        }
-        return null;
-    }
-
-    private ServiceConnection mServiceConn = new ServiceConnection() {
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            mBinder = IRouteProvider.Stub.asInterface(service);
-            if (DEBUG) {
-                Slog.d(TAG, "Connected to route provider");
-            }
-            try {
-                mBinder.registerCallback(mCbStub);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Error registering callback on route provider. Retry count: "
-                        + mRetryCount, e);
-                if (mRetryCount < MAX_RETRIES) {
-                    mRetryCount++;
-                    rebindIfDisconnected();
-                }
-            }
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            mBinder = null;
-            if (DEBUG) {
-                Slog.d(TAG, "Disconnected from route provider");
-            }
-        }
-
-    };
-
-    private IRouteProviderCallback.Stub mCbStub = new IRouteProviderCallback.Stub() {
-        @Override
-        public void onConnectionStateChanged(IRouteConnection connection, int state)
-                throws RemoteException {
-            // TODO
-        }
-
-        @Override
-        public void onRouteEvent(RouteEvent event) throws RemoteException {
-            synchronized (mLock) {
-                RouteConnectionRecord record = getConnectionLocked(event.getConnection());
-                Log.d(TAG, "Received route event for record " + record);
-                if (record != null) {
-                    record.sendEvent(event);
-                }
-            }
-        }
-
-        @Override
-        public void onConnectionTerminated(IRouteConnection connection) throws RemoteException {
-            synchronized (mLock) {
-                RouteConnectionRecord record = getConnectionLocked(connection.asBinder());
-                if (record != null) {
-                    record.disconnect();
-                    mConnections.remove(record);
-                }
-            }
-        }
-
-        @Override
-        public void onRoutesChanged() throws RemoteException {
-            // TODO
-        }
-    };
-
-    /**
-     * Listener for receiving responses to route requests on the provider.
-     */
-    public interface RoutesListener {
-        /**
-         * Called when routes have been returned from a request to getRoutes.
-         *
-         * @param record The session that the routes were requested for.
-         * @param routes The matching routes returned by the provider.
-         * @param reqId The request id this is responding to.
-         */
-        public void onRoutesUpdated(String sessionId, ArrayList<RouteInfo> routes,
-                int reqId);
-
-        /**
-         * Called when a route has successfully connected.
-         *
-         * @param session The session that was connected.
-         * @param route The route it connected to.
-         * @param options The options that were used for the connection.
-         * @param connection The connection instance that was created.
-         */
-        public void onRouteConnected(String sessionId, RouteInfo route,
-                RouteRequest options, RouteConnectionRecord connection);
-    }
-}
diff --git a/services/core/java/com/android/server/media/MediaRouteProviderWatcher.java b/services/core/java/com/android/server/media/MediaRouteProviderWatcher.java
deleted file mode 100644
index 734eab9..0000000
--- a/services/core/java/com/android/server/media/MediaRouteProviderWatcher.java
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.media;
-
-import android.Manifest;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.media.routeprovider.RouteProviderService;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.text.TextUtils;
-import android.util.Slog;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.UUID;
-
-/**
- * Watches for media route provider services to be installed. Adds a provider to
- * the media session service for each registered service. For now just run all
- * providers. In the future define a policy for when to run providers.
- */
-public class MediaRouteProviderWatcher {
-    private static final String TAG = "MRPWatcher";
-    private static final boolean DEBUG = true; // Log.isLoggable(TAG,
-                                               // Log.DEBUG);
-
-    private final Context mContext;
-    private final Callback mCallback;
-    private final Handler mHandler;
-    private final int mUserId;
-    private final PackageManager mPackageManager;
-
-    private final ArrayList<MediaRouteProviderProxy> mProviders =
-            new ArrayList<MediaRouteProviderProxy>();
-    private boolean mRunning;
-
-    public MediaRouteProviderWatcher(Context context, Callback callback, Handler handler,
-            int userId) {
-        mContext = context;
-        mCallback = callback;
-        mHandler = handler;
-        mUserId = userId;
-        mPackageManager = context.getPackageManager();
-    }
-
-    public void dump(PrintWriter pw, String prefix) {
-        pw.println(prefix + " mUserId=" + mUserId);
-        pw.println(prefix + " mRunning=" + mRunning);
-        pw.println(prefix + " mProviders.size()=" + mProviders.size());
-    }
-
-    public void start() {
-        if (!mRunning) {
-            mRunning = true;
-
-            IntentFilter filter = new IntentFilter();
-            filter.addAction(Intent.ACTION_PACKAGE_ADDED);
-            filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-            filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-            filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
-            filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
-            filter.addDataScheme("package");
-            mContext.registerReceiverAsUser(mScanPackagesReceiver,
-                    new UserHandle(mUserId), filter, null, mHandler);
-
-            // Scan packages.
-            // Also has the side-effect of restarting providers if needed.
-            mHandler.post(mScanPackagesRunnable);
-        }
-    }
-
-    // Stop discovering providers and routes. Providers that still have an
-    // active session connected to them will not unbind.
-    public void stop() {
-        if (mRunning) {
-            mRunning = false;
-
-            mContext.unregisterReceiver(mScanPackagesReceiver);
-            mHandler.removeCallbacks(mScanPackagesRunnable);
-
-            // Stop all inactive providers.
-            for (int i = mProviders.size() - 1; i >= 0; i--) {
-                mProviders.get(i).stop();
-            }
-        }
-    }
-
-    // Clean up the providers forcibly unbinding if necessary
-    public void destroy() {
-        for (int i = mProviders.size() - 1; i >= 0; i--) {
-            mProviders.get(i).destroy();
-            mProviders.remove(i);
-        }
-    }
-
-    public ArrayList<MediaRouteProviderProxy> getProviders() {
-        return mProviders;
-    }
-
-    public MediaRouteProviderProxy getProvider(String id) {
-        int providerIndex = findProvider(id);
-        if (providerIndex != -1) {
-            return mProviders.get(providerIndex);
-        }
-        return null;
-    }
-
-    private void scanPackages() {
-        if (!mRunning) {
-            return;
-        }
-
-        // Add providers for all new services.
-        // Reorder the list so that providers left at the end will be the ones
-        // to remove.
-        int targetIndex = 0;
-        Intent intent = new Intent(RouteProviderService.SERVICE_INTERFACE);
-        for (ResolveInfo resolveInfo : mPackageManager.queryIntentServicesAsUser(
-                intent, 0, mUserId)) {
-            ServiceInfo serviceInfo = resolveInfo.serviceInfo;
-            if (DEBUG) {
-                Slog.d(TAG, "Checking service " + (serviceInfo == null ? null : serviceInfo.name));
-            }
-            if (serviceInfo != null && verifyServiceTrusted(serviceInfo)) {
-                int sourceIndex = findProvider(serviceInfo.packageName, serviceInfo.name);
-                if (sourceIndex < 0) {
-                    // TODO get declared interfaces from manifest
-                    if (DEBUG) {
-                        Slog.d(TAG, "Creating new provider proxy for service");
-                    }
-                    MediaRouteProviderProxy provider =
-                            new MediaRouteProviderProxy(mContext, UUID.randomUUID().toString(),
-                                    new ComponentName(serviceInfo.packageName, serviceInfo.name),
-                                    mUserId, null);
-                    provider.start();
-                    mProviders.add(targetIndex++, provider);
-                    mCallback.addProvider(provider);
-                } else if (sourceIndex >= targetIndex) {
-                    MediaRouteProviderProxy provider = mProviders.get(sourceIndex);
-                    provider.start(); // restart the provider if needed
-                    provider.rebindIfDisconnected();
-                    Collections.swap(mProviders, sourceIndex, targetIndex++);
-                }
-            }
-        }
-
-        // Remove providers for missing services.
-        if (targetIndex < mProviders.size()) {
-            for (int i = mProviders.size() - 1; i >= targetIndex; i--) {
-                MediaRouteProviderProxy provider = mProviders.get(i);
-                mCallback.removeProvider(provider);
-                mProviders.remove(provider);
-                provider.stop();
-            }
-        }
-    }
-
-    private boolean verifyServiceTrusted(ServiceInfo serviceInfo) {
-        if (serviceInfo.permission == null || !serviceInfo.permission.equals(
-                Manifest.permission.BIND_ROUTE_PROVIDER)) {
-            // If the service does not require this permission then any app
-            // could potentially bind to it and mess with their routes. So we
-            // only want to trust providers that require the
-            // correct permissions.
-            Slog.w(TAG, "Ignoring route provider service because it did not "
-                    + "require the BIND_ROUTE_PROVIDER permission in its manifest: "
-                    + serviceInfo.packageName + "/" + serviceInfo.name);
-            return false;
-        }
-        // Looks good.
-        return true;
-    }
-
-    private int findProvider(String id) {
-        int count = mProviders.size();
-        for (int i = 0; i < count; i++) {
-            MediaRouteProviderProxy provider = mProviders.get(i);
-            if (TextUtils.equals(id, provider.getId())) {
-                return i;
-            }
-        }
-        return -1;
-    }
-
-    private int findProvider(String packageName, String className) {
-        int count = mProviders.size();
-        for (int i = 0; i < count; i++) {
-            MediaRouteProviderProxy provider = mProviders.get(i);
-            if (provider.hasComponentName(packageName, className)) {
-                return i;
-            }
-        }
-        return -1;
-    }
-
-    private final BroadcastReceiver mScanPackagesReceiver = new BroadcastReceiver() {
-            @Override
-        public void onReceive(Context context, Intent intent) {
-            if (DEBUG) {
-                Slog.d(TAG, "Received package manager broadcast: " + intent);
-            }
-            scanPackages();
-        }
-    };
-
-    private final Runnable mScanPackagesRunnable = new Runnable() {
-            @Override
-        public void run() {
-            scanPackages();
-        }
-    };
-
-    public interface Callback {
-        void addProvider(MediaRouteProviderProxy provider);
-
-        void removeProvider(MediaRouteProviderProxy provider);
-    }
-}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 0fbcd7e..01a21f4 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -19,19 +19,16 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.media.routeprovider.RouteRequest;
+import android.media.routing.IMediaRouter;
+import android.media.routing.IMediaRouterDelegate;
+import android.media.routing.IMediaRouterStateCallback;
 import android.media.session.ISessionController;
 import android.media.session.ISessionControllerCallback;
 import android.media.session.ISession;
 import android.media.session.ISessionCallback;
 import android.media.session.MediaController;
-import android.media.session.RouteCommand;
-import android.media.session.RouteInfo;
-import android.media.session.RouteOptions;
-import android.media.session.RouteEvent;
 import android.media.session.MediaSession;
 import android.media.session.MediaSessionInfo;
-import android.media.session.RouteInterface;
 import android.media.session.PlaybackState;
 import android.media.session.ParcelableVolumeInfo;
 import android.media.AudioManager;
@@ -94,14 +91,9 @@
     private final Object mLock = new Object();
     private final ArrayList<ISessionControllerCallback> mControllerCallbacks =
             new ArrayList<ISessionControllerCallback>();
-    private final ArrayList<RouteRequest> mRequests = new ArrayList<RouteRequest>();
 
-    private RouteInfo mRoute;
-    private RouteOptions mRequest;
-    private RouteConnectionRecord mConnection;
-    // TODO define a RouteState class with relevant info
-    private int mRouteState;
     private long mFlags;
+    private IMediaRouter mMediaRouter;
     private ComponentName mMediaButtonReceiver;
 
     // TransportPerformer fields
@@ -160,24 +152,6 @@
     }
 
     /**
-     * Get the set of route requests this session is interested in.
-     *
-     * @return The list of RouteRequests
-     */
-    public List<RouteRequest> getRouteRequests() {
-        return mRequests;
-    }
-
-    /**
-     * Get the route this session is currently on.
-     *
-     * @return The route the session is on.
-     */
-    public RouteInfo getRoute() {
-        return mRoute;
-    }
-
-    /**
      * Get the info for this session.
      *
      * @return Info that identifies this session.
@@ -229,41 +203,6 @@
     }
 
     /**
-     * Set the selected route. This does not connect to the route, just notifies
-     * the app that a new route has been selected.
-     *
-     * @param route The route that was selected.
-     */
-    public void selectRoute(RouteInfo route) {
-        synchronized (mLock) {
-            if (route != mRoute) {
-                disconnect(MediaSession.DISCONNECT_REASON_ROUTE_CHANGED);
-            }
-            mRoute = route;
-        }
-        mSessionCb.sendRouteChange(route);
-    }
-
-    /**
-     * Update the state of the route this session is using and notify the
-     * session.
-     *
-     * @param state The new state of the route.
-     */
-    public void setRouteState(int state) {
-        mSessionCb.sendRouteStateChange(state);
-    }
-
-    /**
-     * Send an event to this session from the route it is using.
-     *
-     * @param event The event to send.
-     */
-    public void sendRouteEvent(RouteEvent event) {
-        mSessionCb.sendRouteEvent(event);
-    }
-
-    /**
      * Send a volume adjustment to the session owner.
      *
      * @param delta The amount to adjust the volume by.
@@ -338,40 +277,6 @@
     }
 
     /**
-     * Set the connection to use for the selected route and notify the app it is
-     * now connected.
-     *
-     * @param route The route the connection is to.
-     * @param request The request that was used to connect.
-     * @param connection The connection to the route.
-     * @return True if this connection is still valid, false if it is stale.
-     */
-    public boolean setRouteConnected(RouteInfo route, RouteOptions request,
-            RouteConnectionRecord connection) {
-        synchronized (mLock) {
-            if (mDestroyed) {
-                Log.i(TAG, "setRouteConnected: session has been destroyed");
-                connection.disconnect();
-                return false;
-            }
-            if (mRoute == null || !TextUtils.equals(route.getId(), mRoute.getId())) {
-                Log.w(TAG, "setRouteConnected: connected route is stale");
-                connection.disconnect();
-                return false;
-            }
-            if (request != mRequest) {
-                Log.w(TAG, "setRouteConnected: connection request is stale");
-                connection.disconnect();
-                return false;
-            }
-            mConnection = connection;
-            mConnection.setListener(mConnectionListener);
-            mSessionCb.sendRouteConnected();
-        }
-        return true;
-    }
-
-    /**
      * Check if this session has been set to active by the app.
      *
      * @return True if the session is active, false otherwise.
@@ -460,30 +365,6 @@
         return mOptimisticVolume;
     }
 
-    /**
-     * @return True if this session is currently connected to a route.
-     */
-    public boolean isConnected() {
-        return mConnection != null;
-    }
-
-    public void disconnect(int reason) {
-        synchronized (mLock) {
-            if (!mDestroyed) {
-                disconnectLocked(reason);
-            }
-        }
-    }
-
-    private void disconnectLocked(int reason) {
-        if (mConnection != null) {
-            mConnection.setListener(null);
-            mConnection.disconnect();
-            mConnection = null;
-            pushDisconnected(reason);
-        }
-    }
-
     public boolean isTransportControlEnabled() {
         return hasFlag(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
     }
@@ -502,11 +383,6 @@
             if (mDestroyed) {
                 return;
             }
-            if (isConnected()) {
-                disconnectLocked(MediaSession.DISCONNECT_REASON_SESSION_DESTROYED);
-            }
-            mRoute = null;
-            mRequest = null;
             mDestroyed = true;
         }
     }
@@ -532,15 +408,6 @@
         pw.println(indent + "controllers: " + mControllerCallbacks.size());
         pw.println(indent + "state=" + (mPlaybackState == null ? null : mPlaybackState.toString()));
         pw.println(indent + "metadata:" + getShortMetadataString());
-        pw.println(indent + "route requests {");
-        int size = mRequests.size();
-        for (int i = 0; i < size; i++) {
-            pw.println(indent + "  " + mRequests.get(i).toString());
-        }
-        pw.println(indent + "}");
-        pw.println(indent + "route=" + (mRoute == null ? null : mRoute.toString()));
-        pw.println(indent + "connection=" + (mConnection == null ? null : mConnection.toString()));
-        pw.println(indent + "params=" + (mRequest == null ? null : mRequest.toString()));
     }
 
     private String getShortMetadataString() {
@@ -550,12 +417,6 @@
         return "size=" + fields + ", title=" + title;
     }
 
-    private void pushDisconnected(int reason) {
-        synchronized (mLock) {
-            mSessionCb.sendRouteDisconnected(reason);
-        }
-    }
-
     private void pushPlaybackStateUpdate() {
         synchronized (mLock) {
             if (mDestroyed) {
@@ -613,25 +474,6 @@
         }
     }
 
-    private void pushRouteUpdate() {
-        synchronized (mLock) {
-            if (mDestroyed) {
-                return;
-            }
-            for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
-                ISessionControllerCallback cb = mControllerCallbacks.get(i);
-                try {
-                    cb.onRouteChanged(mRoute);
-                } catch (DeadObjectException e) {
-                    Log.w(TAG, "Removing dead callback in pushRouteUpdate.", e);
-                    mControllerCallbacks.remove(i);
-                } catch (RemoteException e) {
-                    Log.w(TAG, "unexpected exception in pushRouteUpdate.", e);
-                }
-            }
-        }
-    }
-
     private void pushEvent(String event, Bundle data) {
         synchronized (mLock) {
             if (mDestroyed) {
@@ -651,25 +493,6 @@
         }
     }
 
-    private void pushRouteCommand(RouteCommand command, ResultReceiver cb) {
-        synchronized (mLock) {
-            if (mDestroyed) {
-                return;
-            }
-            if (mRoute == null || !TextUtils.equals(command.getRouteInfo(), mRoute.getId())) {
-                if (cb != null) {
-                    cb.send(RouteInterface.RESULT_ROUTE_IS_STALE, null);
-                    return;
-                }
-            }
-            if (mConnection != null) {
-                mConnection.sendCommand(command, cb);
-            } else if (cb != null) {
-                cb.send(RouteInterface.RESULT_NOT_CONNECTED, null);
-            }
-        }
-    }
-
     private PlaybackState getStateWithUpdatedPosition() {
         PlaybackState state = mPlaybackState;
         long duration = -1;
@@ -682,16 +505,19 @@
                     || state.getState() == PlaybackState.STATE_FAST_FORWARDING
                     || state.getState() == PlaybackState.STATE_REWINDING) {
                 long updateTime = state.getLastPositionUpdateTime();
+                long currentTime = SystemClock.elapsedRealtime();
                 if (updateTime > 0) {
-                    long position = (long) (state.getPlaybackRate()
-                            * (SystemClock.elapsedRealtime() - updateTime)) + state.getPosition();
+                    long position = (long) (state.getPlaybackSpeed()
+                            * (currentTime - updateTime)) + state.getPosition();
                     if (duration >= 0 && position > duration) {
                         position = duration;
                     } else if (position < 0) {
                         position = 0;
                     }
-                    result = new PlaybackState(state);
-                    result.setState(state.getState(), position, state.getPlaybackRate());
+                    PlaybackState.Builder builder = new PlaybackState.Builder(state);
+                    builder.setState(state.getState(), position, state.getPlaybackSpeed(),
+                            currentTime);
+                    result = builder.build();
                 }
             }
         }
@@ -708,21 +534,6 @@
         return -1;
     }
 
-    private final RouteConnectionRecord.Listener mConnectionListener
-            = new RouteConnectionRecord.Listener() {
-        @Override
-        public void onEvent(RouteEvent event) {
-            RouteEvent eventForSession = new RouteEvent(null, event.getIface(),
-                    event.getEvent(), event.getExtras());
-            mSessionCb.sendRouteEvent(eventForSession);
-        }
-
-        @Override
-        public void disconnect() {
-            MediaSessionRecord.this.disconnect(MediaSession.DISCONNECT_REASON_PROVIDER_DISCONNECTED);
-        }
-    };
-
     private final Runnable mClearOptimisticVolumeRunnable = new Runnable() {
         @Override
         public void run() {
@@ -769,6 +580,12 @@
         }
 
         @Override
+        public void setMediaRouter(IMediaRouter router) {
+            mMediaRouter = router;
+            mHandler.post(MessageHandler.MSG_UPDATE_SESSION_STATE);
+        }
+
+        @Override
         public void setMediaButtonReceiver(ComponentName mbr) {
             mMediaButtonReceiver = mbr;
         }
@@ -797,46 +614,6 @@
         }
 
         @Override
-        public void sendRouteCommand(RouteCommand command, ResultReceiver cb) {
-            mHandler.post(MessageHandler.MSG_SEND_COMMAND,
-                    new Pair<RouteCommand, ResultReceiver>(command, cb));
-        }
-
-        @Override
-        public boolean setRoute(RouteInfo route) throws RemoteException {
-            // TODO decide if allowed to set route and if the route exists
-            return false;
-        }
-
-        @Override
-        public void connectToRoute(RouteInfo route, RouteOptions request)
-                throws RemoteException {
-            if (mRoute == null || !TextUtils.equals(route.getId(), mRoute.getId())) {
-                throw new RemoteException("RouteInfo does not match current route");
-            }
-            mService.connectToRoute(MediaSessionRecord.this, route, request);
-            mRequest = request;
-        }
-
-        @Override
-        public void disconnectFromRoute(RouteInfo route) {
-            if (route != null && mRoute != null
-                    && TextUtils.equals(route.getId(), mRoute.getId())) {
-                disconnect(MediaSession.DISCONNECT_REASON_SESSION_DISCONNECTED);
-            }
-        }
-
-        @Override
-        public void setRouteOptions(List<RouteOptions> options) throws RemoteException {
-            mRequests.clear();
-            for (int i = options.size() - 1; i >= 0; i--) {
-                RouteRequest request = new RouteRequest(mSessionInfo, options.get(i),
-                        false);
-                mRequests.add(request);
-            }
-        }
-
-        @Override
         public void setCurrentVolume(int volume) {
             mCurrentVolume = volume;
             mHandler.post(MessageHandler.MSG_UPDATE_VOLUME);
@@ -903,46 +680,6 @@
             }
         }
 
-        public void sendRouteChange(RouteInfo route) {
-            try {
-                mCb.onRequestRouteChange(route);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Remote failure in sendRouteChange.", e);
-            }
-        }
-
-        public void sendRouteStateChange(int state) {
-            try {
-                mCb.onRouteStateChange(state);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Remote failure in sendRouteStateChange.", e);
-            }
-        }
-
-        public void sendRouteEvent(RouteEvent event) {
-            try {
-                mCb.onRouteEvent(event);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Remote failure in sendRouteEvent.", e);
-            }
-        }
-
-        public void sendRouteConnected() {
-            try {
-                mCb.onRouteConnected(mRoute, mRequest);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Remote failure in sendRouteStateChange.", e);
-            }
-        }
-
-        public void sendRouteDisconnected(int reason) {
-            try {
-                mCb.onRouteDisconnected(mRoute, reason);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Remote failure in sendRouteDisconnected");
-            }
-        }
-
         public void play() {
             try {
                 mCb.onPlay();
@@ -1187,20 +924,19 @@
         }
 
         @Override
-        public void showRoutePicker() {
-            mService.showRoutePickerForSession(MediaSessionRecord.this);
+        public IMediaRouterDelegate createMediaRouterDelegate(
+                IMediaRouterStateCallback callback) {
+            // todo
+            return null;
         }
     }
 
     private class MessageHandler extends Handler {
         private static final int MSG_UPDATE_METADATA = 1;
         private static final int MSG_UPDATE_PLAYBACK_STATE = 2;
-        private static final int MSG_UPDATE_ROUTE = 3;
-        private static final int MSG_SEND_EVENT = 4;
-        private static final int MSG_UPDATE_ROUTE_FILTERS = 5;
-        private static final int MSG_SEND_COMMAND = 6;
-        private static final int MSG_UPDATE_SESSION_STATE = 7;
-        private static final int MSG_UPDATE_VOLUME = 8;
+        private static final int MSG_SEND_EVENT = 3;
+        private static final int MSG_UPDATE_SESSION_STATE = 4;
+        private static final int MSG_UPDATE_VOLUME = 5;
 
         public MessageHandler(Looper looper) {
             super(looper);
@@ -1214,17 +950,9 @@
                 case MSG_UPDATE_PLAYBACK_STATE:
                     pushPlaybackStateUpdate();
                     break;
-                case MSG_UPDATE_ROUTE:
-                    pushRouteUpdate();
-                    break;
                 case MSG_SEND_EVENT:
                     pushEvent((String) msg.obj, msg.getData());
                     break;
-                case MSG_SEND_COMMAND:
-                    Pair<RouteCommand, ResultReceiver> cmd =
-                            (Pair<RouteCommand, ResultReceiver>) msg.obj;
-                    pushRouteCommand(cmd.first, cmd.second);
-                    break;
                 case MSG_UPDATE_SESSION_STATE:
                     // TODO add session state
                     break;
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 310f3e9..4c475d9 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -30,14 +30,11 @@
 import android.media.AudioManager;
 import android.media.IAudioService;
 import android.media.IRemoteVolumeController;
-import android.media.routeprovider.RouteRequest;
 import android.media.session.IActiveSessionsListener;
 import android.media.session.ISession;
 import android.media.session.ISessionCallback;
 import android.media.session.ISessionManager;
 import android.media.session.MediaSession;
-import android.media.session.RouteInfo;
-import android.media.session.RouteOptions;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
@@ -74,15 +71,12 @@
     private static final int WAKELOCK_TIMEOUT = 5000;
 
     private final SessionManagerImpl mSessionManagerImpl;
-    // private final MediaRouteProviderWatcher mRouteProviderWatcher;
     private final MediaSessionStack mPriorityStack;
 
     private final ArrayList<MediaSessionRecord> mAllSessions = new ArrayList<MediaSessionRecord>();
     private final SparseArray<UserRecord> mUserRecords = new SparseArray<UserRecord>();
     private final ArrayList<SessionsListenerRecord> mSessionsListeners
             = new ArrayList<SessionsListenerRecord>();
-    // private final ArrayList<MediaRouteProviderProxy> mProviders
-    // = new ArrayList<MediaRouteProviderProxy>();
     private final Object mLock = new Object();
     private final MessageHandler mHandler = new MessageHandler();
     private final PowerManager.WakeLock mMediaEventWakeLock;
@@ -94,10 +88,6 @@
     private MediaSessionRecord mPrioritySession;
     private int mCurrentUserId = -1;
 
-    // Used to keep track of the current request to show routes for a specific
-    // session so we drop late callbacks properly.
-    private int mShowRoutesRequestId = 0;
-
     // Used to notify system UI when remote volume was changed. TODO find a
     // better way to handle this.
     private IRemoteVolumeController mRvc;
@@ -126,69 +116,6 @@
         return IAudioService.Stub.asInterface(b);
     }
 
-    /**
-     * Should trigger showing the Media route picker dialog. Right now it just
-     * kicks off a query to all the providers to get routes.
-     *
-     * @param record The session to show the picker for.
-     */
-    public void showRoutePickerForSession(MediaSessionRecord record) {
-        // TODO for now just toggle the route to test (we will only have one
-        // match for now)
-        synchronized (mLock) {
-            if (!mAllSessions.contains(record)) {
-                Log.d(TAG, "Unknown session tried to show route picker. Ignoring.");
-                return;
-            }
-            RouteInfo current = record.getRoute();
-            UserRecord user = mUserRecords.get(record.getUserId());
-            if (current != null) {
-                // For now send null to mean the local route
-                MediaRouteProviderProxy proxy = user.getProviderLocked(current.getProvider());
-                if (proxy != null) {
-                    proxy.removeSession(record);
-                }
-                record.selectRoute(null);
-                return;
-            }
-            ArrayList<MediaRouteProviderProxy> providers = user.getProvidersLocked();
-            mShowRoutesRequestId++;
-            for (int i = providers.size() - 1; i >= 0; i--) {
-                MediaRouteProviderProxy provider = providers.get(i);
-                provider.getRoutes(record, mShowRoutesRequestId);
-            }
-        }
-    }
-
-    /**
-     * Connect a session to the given route.
-     *
-     * @param session The session to connect.
-     * @param route The route to connect to.
-     * @param options The options to use for the connection.
-     */
-    public void connectToRoute(MediaSessionRecord session, RouteInfo route,
-            RouteOptions options) {
-        synchronized (mLock) {
-            if (!mAllSessions.contains(session)) {
-                Log.d(TAG, "Unknown session attempting to connect to route. Ignoring");
-                return;
-            }
-            UserRecord user = mUserRecords.get(session.getUserId());
-            if (user == null) {
-                Log.wtf(TAG, "connectToRoute: User " + session.getUserId() + " does not exist.");
-                return;
-            }
-            MediaRouteProviderProxy proxy = user.getProviderLocked(route.getProvider());
-            if (proxy == null) {
-                Log.w(TAG, "Provider for route " + route.getName() + " does not exist.");
-                return;
-            }
-            RouteRequest request = new RouteRequest(session.getSessionInfo(), options, true);
-            proxy.connectToRoute(session, route, request);
-        }
-    }
-
     public void updateSession(MediaSessionRecord record) {
         synchronized (mLock) {
             if (!mAllSessions.contains(record)) {
@@ -552,110 +479,49 @@
         }
     }
 
-    private MediaRouteProviderProxy.RoutesListener mRoutesCallback
-            = new MediaRouteProviderProxy.RoutesListener() {
-        @Override
-        public void onRoutesUpdated(String sessionId, ArrayList<RouteInfo> routes,
-                int reqId) {
-            // TODO for now select the first route to test, eventually add the
-            // new routes to the dialog if it is still open
-            synchronized (mLock) {
-                int index = findIndexOfSessionForIdLocked(sessionId);
-                if (index != -1 && routes != null && routes.size() > 0) {
-                    MediaSessionRecord record = mAllSessions.get(index);
-                    RouteInfo route = routes.get(0);
-                    record.selectRoute(route);
-                    UserRecord user = mUserRecords.get(record.getUserId());
-                    MediaRouteProviderProxy provider = user.getProviderLocked(route.getProvider());
-                    provider.addSession(record);
-                }
-            }
-        }
-
-        @Override
-        public void onRouteConnected(String sessionId, RouteInfo route,
-                RouteRequest options, RouteConnectionRecord connection) {
-            synchronized (mLock) {
-                int index = findIndexOfSessionForIdLocked(sessionId);
-                if (index != -1) {
-                    MediaSessionRecord session = mAllSessions.get(index);
-                    session.setRouteConnected(route, options.getConnectionOptions(), connection);
-                }
-            }
-        }
-    };
-
     /**
      * Information about a particular user. The contents of this object is
      * guarded by mLock.
      */
     final class UserRecord {
         private final int mUserId;
-        private final MediaRouteProviderWatcher mRouteProviderWatcher;
-        private final ArrayList<MediaRouteProviderProxy> mProviders
-                = new ArrayList<MediaRouteProviderProxy>();
         private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>();
 
         public UserRecord(Context context, int userId) {
             mUserId = userId;
-            mRouteProviderWatcher = new MediaRouteProviderWatcher(context,
-                    mProviderWatcherCallback, mHandler, userId);
         }
 
         public void startLocked() {
-            mRouteProviderWatcher.start();
+            // nothing for now
         }
 
         public void stopLocked() {
-            mRouteProviderWatcher.stop();
-            updateInterestLocked();
+            // nothing for now
         }
 
         public void destroyLocked() {
             for (int i = mSessions.size() - 1; i >= 0; i--) {
                 MediaSessionRecord session = mSessions.get(i);
                 MediaSessionService.this.destroySessionLocked(session);
-                if (session.isConnected()) {
-                    session.disconnect(MediaSession.DISCONNECT_REASON_USER_STOPPING);
-                }
             }
         }
 
-        public ArrayList<MediaRouteProviderProxy> getProvidersLocked() {
-            return mProviders;
-        }
-
         public ArrayList<MediaSessionRecord> getSessionsLocked() {
             return mSessions;
         }
 
         public void addSessionLocked(MediaSessionRecord session) {
             mSessions.add(session);
-            updateInterestLocked();
         }
 
         public void removeSessionLocked(MediaSessionRecord session) {
             mSessions.remove(session);
-            RouteInfo route = session.getRoute();
-            if (route != null) {
-                MediaRouteProviderProxy provider = getProviderLocked(route.getProvider());
-                if (provider != null) {
-                    provider.removeSession(session);
-                }
-            }
-            updateInterestLocked();
         }
 
         public void dumpLocked(PrintWriter pw, String prefix) {
             pw.println(prefix + "Record for user " + mUserId);
             String indent = prefix + "  ";
-            int size = mProviders.size();
-            pw.println(indent + size + " Providers:");
-            for (int i = 0; i < size; i++) {
-                mProviders.get(i).dump(pw, indent);
-            }
-            pw.println();
-            size = mSessions.size();
+            int size = mSessions.size();
             pw.println(indent + size + " Sessions:");
             for (int i = 0; i < size; i++) {
                 // Just print the session info, the full session dump will
@@ -663,48 +529,6 @@
                 pw.println(indent + mSessions.get(i).getSessionInfo());
             }
         }
-
-        public void updateInterestLocked() {
-            // TODO go through the sessions and build up the set of interfaces
-            // we're interested in. Update the provider watcher.
-            // For now, just express interest in all providers for the current
-            // user
-            boolean interested = mUserId == mCurrentUserId;
-            for (int i = mProviders.size() - 1; i >= 0; i--) {
-                mProviders.get(i).setInterested(interested);
-            }
-        }
-
-        private MediaRouteProviderProxy getProviderLocked(String providerId) {
-            for (int i = mProviders.size() - 1; i >= 0; i--) {
-                MediaRouteProviderProxy provider = mProviders.get(i);
-                if (TextUtils.equals(providerId, provider.getId())) {
-                    return provider;
-                }
-            }
-            return null;
-        }
-
-        private MediaRouteProviderWatcher.Callback mProviderWatcherCallback
-                = new MediaRouteProviderWatcher.Callback() {
-            @Override
-            public void removeProvider(MediaRouteProviderProxy provider) {
-                synchronized (mLock) {
-                    mProviders.remove(provider);
-                    provider.setRoutesListener(null);
-                    provider.setInterested(false);
-                }
-            }
-
-            @Override
-            public void addProvider(MediaRouteProviderProxy provider) {
-                synchronized (mLock) {
-                    mProviders.add(provider);
-                    provider.setRoutesListener(mRoutesCallback);
-                    provider.setInterested(true);
-                }
-            }
-        };
     }
 
     final class SessionsListenerRecord implements IBinder.DeathRecipient {
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index e26a2eb..3d1ecb3 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -25,7 +25,7 @@
 
 /**
  * Keeps track of media sessions and their priority for notifications, media
- * button routing, etc.
+ * button dispatch, etc.
  */
 public class MediaSessionStack {
     /**
@@ -277,8 +277,8 @@
                 lastActiveIndex++;
                 lastPublishedIndex++;
             } else if (session.isPlaybackActive(true)) {
-                // TODO replace getRoute() == null with real local route check
-                if(session.getRoute() == null) {
+                // TODO this with real local route check
+                if (true) {
                     // Active local sessions get top priority
                     result.add(lastLocalIndex, session);
                     lastLocalIndex++;
diff --git a/services/core/java/com/android/server/media/RouteConnectionRecord.java b/services/core/java/com/android/server/media/RouteConnectionRecord.java
deleted file mode 100644
index 90ddf29..0000000
--- a/services/core/java/com/android/server/media/RouteConnectionRecord.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.media;
-
-import android.media.routeprovider.IRouteConnection;
-import android.media.session.RouteCommand;
-import android.media.session.RouteEvent;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.util.Log;
-
-/**
- * A connection between a Session and a Route.
- */
-public class RouteConnectionRecord {
-    private static final String TAG = "RouteConnRecord";
-    private final IRouteConnection mBinder;
-    private final String mPackageName;
-    private final int mUid;
-    private Listener mListener;
-
-    public RouteConnectionRecord(IRouteConnection binder, String packageName, int uid) {
-        mBinder = binder;
-        mPackageName = packageName;
-        mUid = uid;
-    }
-
-    /**
-     * Add a listener to get route events on.
-     *
-     * @param listener The listener to get events on.
-     */
-    public void setListener(Listener listener) {
-        mListener = listener;
-    }
-
-    /**
-     * Check if this connection matches the token given.
-     *
-     * @param binder The token to check
-     * @return True if this is the connection you're looking for, false
-     *         otherwise.
-     */
-    public boolean isConnection(IBinder binder) {
-        return binder != null && binder.equals(mBinder.asBinder());
-    }
-
-    /**
-     * Send an event from this connection.
-     *
-     * @param event The event to send.
-     */
-    public void sendEvent(RouteEvent event) {
-        if (mListener != null) {
-            mListener.onEvent(event);
-        }
-    }
-
-    /**
-     * Send a command to this connection.
-     *
-     * @param command The command to send.
-     * @param cb The receiver to get a result on.
-     */
-    public void sendCommand(RouteCommand command, ResultReceiver cb) {
-        try {
-            mBinder.onCommand(command, cb);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error in sendCommand", e);
-        }
-    }
-
-    /**
-     * Tell the session that the provider has disconnected it.
-     */
-    public void disconnect() {
-        if (mListener != null) {
-            mListener.disconnect();
-        }
-    }
-
-    @Override
-    public String toString() {
-        return "RouteConnection { binder=" + mBinder.toString() + ", package=" + mPackageName
-                + ", uid=" + mUid + "}";
-    }
-
-    /**
-     * Listener to receive updates from the provider for this connection.
-     */
-    public static interface Listener {
-        /**
-         * Called when an event is sent on this connection.
-         *
-         * @param event The event that was sent.
-         */
-        public void onEvent(RouteEvent event);
-
-        /**
-         * Called when the provider has disconnected the route.
-         */
-        public void disconnect();
-    }
-}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/pm/KeySetHandle.java b/services/core/java/com/android/server/pm/KeySetHandle.java
new file mode 100644
index 0000000..640feb3
--- /dev/null
+++ b/services/core/java/com/android/server/pm/KeySetHandle.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+    * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+     *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+     * Unless required by applicable law or agreed to in writing, software
+     * distributed under the License is distributed on an "AS IS" BASIS,
+     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.os.Binder;
+
+public class KeySetHandle extends Binder {
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java
index c19951f..37bedf3 100644
--- a/services/core/java/com/android/server/pm/KeySetManagerService.java
+++ b/services/core/java/com/android/server/pm/KeySetManagerService.java
@@ -16,7 +16,6 @@
 
 package com.android.server.pm;
 
-import android.content.pm.KeySet;
 import android.content.pm.PackageParser;
 import android.os.Binder;
 import android.util.ArraySet;
@@ -52,7 +51,7 @@
     /** Sentinel value returned when public key is not found. */
     protected static final long PUBLIC_KEY_NOT_FOUND = -1;
 
-    private final LongSparseArray<KeySet> mKeySets;
+    private final LongSparseArray<KeySetHandle> mKeySets;
 
     private final LongSparseArray<PublicKey> mPublicKeys;
 
@@ -65,7 +64,7 @@
     private static long lastIssuedKeyId = 0;
 
     public KeySetManagerService(Map<String, PackageSetting> packages) {
-        mKeySets = new LongSparseArray<KeySet>();
+        mKeySets = new LongSparseArray<KeySetHandle>();
         mPublicKeys = new LongSparseArray<PublicKey>();
         mKeySetMapping = new LongSparseArray<ArraySet<Long>>();
         mPackages = packages;
@@ -82,7 +81,7 @@
      *
      * Note that this can return true for multiple KeySets.
      */
-    public boolean packageIsSignedByLPr(String packageName, KeySet ks) {
+    public boolean packageIsSignedByLPr(String packageName, KeySetHandle ks) {
         PackageSetting pkg = mPackages.get(packageName);
         if (pkg == null) {
             throw new NullPointerException("Invalid package name");
@@ -91,16 +90,42 @@
             throw new NullPointerException("Package has no KeySet data");
         }
         long id = getIdByKeySetLPr(ks);
+        if (id == KEYSET_NOT_FOUND) {
+                return false;
+        }
         return pkg.keySetData.packageIsSignedBy(id);
     }
 
     /**
+     * Determine if a package is signed by the given KeySet.
+     *
+     * Returns false if the package was not signed by all the
+     * keys in the KeySet, or if the package was signed by keys
+     * not in the KeySet.
+     *
+     * Note that this can return only for one KeySet.
+     */
+    public boolean packageIsSignedByExactlyLPr(String packageName, KeySetHandle ks) {
+        PackageSetting pkg = mPackages.get(packageName);
+        if (pkg == null) {
+            throw new NullPointerException("Invalid package name");
+        }
+        if (pkg.keySetData == null
+            || pkg.keySetData.getProperSigningKeySet()
+            == PackageKeySetData.KEYSET_UNASSIGNED) {
+            throw new NullPointerException("Package has no KeySet data");
+        }
+        long id = getIdByKeySetLPr(ks);
+        return pkg.keySetData.getProperSigningKeySet() == id;
+    }
+
+    /**
      * This informs the system that the given package has defined a KeySet
      * in its manifest that a) contains the given keys and b) is named
      * alias by that package.
      */
     public void addDefinedKeySetToPackageLPw(String packageName,
-            Set<PublicKey> keys, String alias) {
+            ArraySet<PublicKey> keys, String alias) {
         if ((packageName == null) || (keys == null) || (alias == null)) {
             Slog.w(TAG, "Got null argument for a defined keyset, ignoring!");
             return;
@@ -110,7 +135,7 @@
             throw new NullPointerException("Unknown package");
         }
         // Add to KeySets, then to package
-        KeySet ks = addKeySetLPw(keys);
+        KeySetHandle ks = addKeySetLPw(keys);
         long id = getIdByKeySetLPr(ks);
         pkg.keySetData.addDefinedKeySet(id, alias);
     }
@@ -137,19 +162,18 @@
      * was signed by the provided KeySet.
      */
     public void addSigningKeySetToPackageLPw(String packageName,
-            Set<PublicKey> signingKeys) {
+            ArraySet<PublicKey> signingKeys) {
         if ((packageName == null) || (signingKeys == null)) {
             Slog.w(TAG, "Got null argument for a signing keyset, ignoring!");
             return;
         }
         // add the signing KeySet
-        KeySet ks = addKeySetLPw(signingKeys);
+        KeySetHandle ks = addKeySetLPw(signingKeys);
         long id = getIdByKeySetLPr(ks);
-        Set<Long> publicKeyIds = mKeySetMapping.get(id);
+        ArraySet<Long> publicKeyIds = mKeySetMapping.get(id);
         if (publicKeyIds == null) {
             throw new NullPointerException("Got invalid KeySet id");
         }
-
         // attach it to the package
         PackageSetting pkg = mPackages.get(packageName);
         if (pkg == null) {
@@ -160,7 +184,7 @@
         // KeySet id to the package's signing KeySets
         for (int keySetIndex = 0; keySetIndex < mKeySets.size(); keySetIndex++) {
             long keySetID = mKeySets.keyAt(keySetIndex);
-            Set<Long> definedKeys = mKeySetMapping.get(keySetID);
+            ArraySet<Long> definedKeys = mKeySetMapping.get(keySetID);
             if (publicKeyIds.containsAll(definedKeys)) {
                 pkg.keySetData.addSigningKeySet(keySetID);
             }
@@ -171,9 +195,9 @@
      * Fetches the stable identifier associated with the given KeySet. Returns
      * {@link #KEYSET_NOT_FOUND} if the KeySet... wasn't found.
      */
-    private long getIdByKeySetLPr(KeySet ks) {
+    private long getIdByKeySetLPr(KeySetHandle ks) {
         for (int keySetIndex = 0; keySetIndex < mKeySets.size(); keySetIndex++) {
-            KeySet value = mKeySets.valueAt(keySetIndex);
+            KeySetHandle value = mKeySets.valueAt(keySetIndex);
             if (ks.equals(value)) {
                 return mKeySets.keyAt(keySetIndex);
             }
@@ -187,25 +211,24 @@
      * Returns {@link #KEYSET_NOT_FOUND} if the identifier doesn't
      * identify a {@link KeySet}.
      */
-    public KeySet getKeySetByIdLPr(long id) {
+    public KeySetHandle getKeySetByIdLPr(long id) {
         return mKeySets.get(id);
     }
 
     /**
-     * Fetches the {@link KeySet} that a given package refers to by the provided alias.
-     *
-     * @throws IllegalArgumentException if the package has no keyset data.
-     * @throws NullPointerException if the package is unknown.
+     * Fetches the {@link KeySetHandle} that a given package refers to by the
+     * provided alias. Returns null if the package is unknown or does not have a
+     * KeySet corresponding to that alias.
      */
-    public KeySet getKeySetByAliasAndPackageNameLPr(String packageName, String alias) {
+    public KeySetHandle getKeySetByAliasAndPackageNameLPr(String packageName, String alias) {
         PackageSetting p = mPackages.get(packageName);
-        if (p == null) {
-            throw new NullPointerException("Unknown package");
+        if (p == null || p.keySetData == null) {
+                return null;
         }
-        if (p.keySetData == null) {
-            throw new IllegalArgumentException("Package has no keySet data");
+        Long keySetId = p.keySetData.getAliases().get(alias);
+        if (keySetId == null) {
+            throw new IllegalArgumentException("Unknown KeySet alias: " + alias);
         }
-        long keySetId = p.keySetData.getAliases().get(alias);
         return mKeySets.get(keySetId);
     }
 
@@ -214,7 +237,7 @@
      * KeySet id.
      *
      * Returns {@code null} if the identifier doesn't
-     * identify a {@link KeySet}.
+     * identify a {@link KeySetHandle}.
      */
     public ArraySet<PublicKey> getPublicKeysFromKeySetLPr(long id) {
         if(mKeySetMapping.get(id) == null) {
@@ -228,36 +251,32 @@
     }
 
     /**
-     * Fetches all the known {@link KeySet KeySets} that signed the given
+     * Fetches the proper {@link KeySetHandle KeySet} that signed the given
      * package.
      *
      * @throws IllegalArgumentException if the package has no keyset data.
      * @throws NullPointerException if the package is unknown.
      */
-    public Set<KeySet> getSigningKeySetsByPackageNameLPr(String packageName) {
-        Set<KeySet> signingKeySets = new ArraySet<KeySet>();
+    public KeySetHandle  getSigningKeySetByPackageNameLPr(String packageName) {
         PackageSetting p = mPackages.get(packageName);
-        if (p == null) {
-            throw new NullPointerException("Unknown package");
+        if (p == null
+            || p.keySetData == null
+            || p.keySetData.getProperSigningKeySet()
+            == PackageKeySetData.KEYSET_UNASSIGNED) {
+            return null;
         }
-        if (p.keySetData == null || p.keySetData.getSigningKeySets() == null) {
-            throw new IllegalArgumentException("Package has no keySet data");
-        }
-        for (long l : p.keySetData.getSigningKeySets()) {
-            signingKeySets.add(mKeySets.get(l));
-        }
-        return signingKeySets;
+        return mKeySets.get(p.keySetData.getProperSigningKeySet());
     }
 
     /**
-     * Fetches all the known {@link KeySet KeySets} that may upgrade the given
+     * Fetches all the known {@link KeySetHandle KeySets} that may upgrade the given
      * package.
      *
      * @throws IllegalArgumentException if the package has no keyset data.
      * @throws NullPointerException if the package is unknown.
      */
-    public ArraySet<KeySet> getUpgradeKeySetsByPackageNameLPr(String packageName) {
-        ArraySet<KeySet> upgradeKeySets = new ArraySet<KeySet>();
+    public ArraySet<KeySetHandle> getUpgradeKeySetsByPackageNameLPr(String packageName) {
+        ArraySet<KeySetHandle> upgradeKeySets = new ArraySet<KeySetHandle>();
         PackageSetting p = mPackages.get(packageName);
         if (p == null) {
             throw new NullPointerException("Unknown package");
@@ -287,7 +306,7 @@
      *
      * Throws if the provided set is {@code null}.
      */
-    private KeySet addKeySetLPw(Set<PublicKey> keys) {
+    private KeySetHandle addKeySetLPw(ArraySet<PublicKey> keys) {
         if (keys == null) {
             throw new NullPointerException("Provided keys cannot be null");
         }
@@ -305,7 +324,7 @@
         }
 
         // create the KeySet object
-        KeySet ks = new KeySet(new Binder());
+        KeySetHandle ks = new KeySetHandle();
         // get the first unoccupied slot in mKeySets
         long id = getFreeKeySetIDLPw();
         // add the KeySet object to it
@@ -318,7 +337,7 @@
             if (p.keySetData != null) {
                 long pProperSigning = p.keySetData.getProperSigningKeySet();
                 if (pProperSigning != PackageKeySetData.KEYSET_UNASSIGNED) {
-                    Set<Long> pSigningKeys = mKeySetMapping.get(pProperSigning);
+                    ArraySet<Long> pSigningKeys = mKeySetMapping.get(pProperSigning);
                     if (pSigningKeys.containsAll(addedKeyIds)) {
                         p.keySetData.addSigningKeySet(id);
                     }
@@ -353,7 +372,7 @@
      */
     private long getIdFromKeyIdsLPr(Set<Long> publicKeyIds) {
         for (int keyMapIndex = 0; keyMapIndex < mKeySetMapping.size(); keyMapIndex++) {
-            Set<Long> value = mKeySetMapping.valueAt(keyMapIndex);
+            ArraySet<Long> value = mKeySetMapping.valueAt(keyMapIndex);
             if (value.equals(publicKeyIds)) {
                 return mKeySetMapping.keyAt(keyMapIndex);
             }
@@ -582,7 +601,7 @@
         serializer.startTag(null, "keysets");
         for (int keySetIndex = 0; keySetIndex < mKeySetMapping.size(); keySetIndex++) {
             long id = mKeySetMapping.keyAt(keySetIndex);
-            Set<Long> keys = mKeySetMapping.valueAt(keySetIndex);
+            ArraySet<Long> keys = mKeySetMapping.valueAt(keySetIndex);
             serializer.startTag(null, "keyset");
             serializer.attribute(null, "identifier", Long.toString(id));
             for (long keyId : keys) {
@@ -662,7 +681,7 @@
             final String tagName = parser.getName();
             if (tagName.equals("keyset")) {
                 currentKeySetId = readIdentifierLPw(parser);
-                mKeySets.put(currentKeySetId, new KeySet(new Binder()));
+                mKeySets.put(currentKeySetId, new KeySetHandle());
                 mKeySetMapping.put(currentKeySetId, new ArraySet<Long>());
             } else if (tagName.equals("key-id")) {
                 long id = readIdentifierLPw(parser);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 0eb922d..190e87c 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -60,6 +60,7 @@
 
     // TODO: destroy sessions with old timestamps
     // TODO: remove outstanding sessions when installer package goes away
+    // TODO: notify listeners in other users when package has been installed there
 
     private final Context mContext;
     private final PackageManagerService mPm;
@@ -167,6 +168,10 @@
             params.installFlags |= INSTALL_REPLACE_EXISTING;
         }
 
+        if (params.mode == InstallSessionParams.MODE_INVALID) {
+            throw new IllegalArgumentException("Params must have valid mode set");
+        }
+
         // Sanity check that install could fit
         if (params.deltaSize > 0) {
             try {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index c399fa2..31d9704 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -166,7 +166,7 @@
         info.installerPackageName = installerPackageName;
         info.progress = mProgress;
 
-        info.fullInstall = params.fullInstall;
+        info.mode = params.mode;
         info.packageName = params.packageName;
         info.icon = params.icon;
         info.title = params.title;
@@ -177,7 +177,8 @@
     @Override
     public void setClientProgress(int progress) {
         mClientProgress = progress;
-        mProgress = MathUtils.constrain((mClientProgress * 8 * 100) / (params.progressMax * 10), 0, 80);
+        mProgress = MathUtils.constrain(
+                (int) (((float) mClientProgress) / ((float) params.progressMax)) * 80, 0, 80);
         mCallback.onSessionProgress(this, mProgress);
     }
 
@@ -288,7 +289,7 @@
 
         // Inherit any packages and native libraries from existing install that
         // haven't been overridden.
-        if (!params.fullInstall) {
+        if (params.mode == InstallSessionParams.MODE_INHERIT_EXISTING) {
             spliceExistingFilesIntoStage();
         }
 
@@ -382,7 +383,7 @@
         // currently relying on PMS to do this.
         // TODO: teach about compatible upgrade keysets.
 
-        if (params.fullInstall) {
+        if (params.mode == InstallSessionParams.MODE_FULL_INSTALL) {
             // Full installs must include a base package
             if (!seenSplits.contains(null)) {
                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 101ef92..cfba19c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -9331,14 +9331,18 @@
                 return false;
             } else {
                 final File beforeCodeFile = codeFile;
-                final File afterCodeFile = new File(mAppInstallDir,
-                        getNextCodePath(oldCodePath, pkg.packageName, null));
+                final File afterCodeFile = getNextCodePath(pkg.packageName);
 
                 Slog.d(TAG, "Renaming " + beforeCodeFile + " to " + afterCodeFile);
-                if (!beforeCodeFile.renameTo(afterCodeFile)) {
+                try {
+                    Os.rename(beforeCodeFile.getAbsolutePath(), afterCodeFile.getAbsolutePath());
+                } catch (ErrnoException e) {
+                    Slog.d(TAG, "Failed to rename", e);
                     return false;
                 }
+
                 if (!SELinux.restoreconRecursive(afterCodeFile)) {
+                    Slog.d(TAG, "Failed to restorecon");
                     return false;
                 }
 
@@ -9811,6 +9815,16 @@
         return prefix + idxStr;
     }
 
+    private File getNextCodePath(String packageName) {
+        int suffix = 1;
+        File result;
+        do {
+            result = new File(mAppInstallDir, packageName + "-" + suffix);
+            suffix++;
+        } while (result.exists());
+        return result;
+    }
+
     // Utility method used to ignore ADD/REMOVE events
     // by directory observer.
     private static boolean ignoreCodePath(String fullPathStr) {
@@ -13255,4 +13269,83 @@
         }
         return mUserNeedsBadging.valueAt(index);
     }
+
+    @Override
+    public KeySetHandle getKeySetByAlias(String packageName, String alias) {
+        if (packageName == null || alias == null) {
+            return null;
+        }
+        synchronized(mPackages) {
+            final PackageParser.Package pkg = mPackages.get(packageName);
+            if (pkg == null) {
+                Slog.w(TAG, "KeySet requested for unknown package:" + packageName);
+                throw new IllegalArgumentException("Unknown package: " + packageName);
+            }
+            if (pkg.applicationInfo.uid != Binder.getCallingUid()
+                    && Process.SYSTEM_UID != Binder.getCallingUid()) {
+                throw new SecurityException("May not access KeySets defined by"
+                        + " aliases in other applications.");
+            }
+            KeySetManagerService ksms = mSettings.mKeySetManagerService;
+            return ksms.getKeySetByAliasAndPackageNameLPr(packageName, alias);
+        }
+    }
+
+    @Override
+    public KeySetHandle getSigningKeySet(String packageName) {
+        if (packageName == null) {
+            return null;
+        }
+        synchronized(mPackages) {
+            final PackageParser.Package pkg = mPackages.get(packageName);
+            if (pkg == null) {
+                Slog.w(TAG, "KeySet requested for unknown package:" + packageName);
+                throw new IllegalArgumentException("Unknown package: " + packageName);
+            }
+            if (pkg.applicationInfo.uid != Binder.getCallingUid()
+                    && Process.SYSTEM_UID != Binder.getCallingUid()) {
+                throw new SecurityException("May not access signing KeySet of other apps.");
+            }
+            KeySetManagerService ksms = mSettings.mKeySetManagerService;
+            return ksms.getSigningKeySetByPackageNameLPr(packageName);
+        }
+    }
+
+    @Override
+    public boolean isPackageSignedByKeySet(String packageName, IBinder ks) {
+        if (packageName == null || ks == null) {
+            return false;
+        }
+        synchronized(mPackages) {
+            final PackageParser.Package pkg = mPackages.get(packageName);
+            if (pkg == null) {
+                Slog.w(TAG, "KeySet requested for unknown package:" + packageName);
+                throw new IllegalArgumentException("Unknown package: " + packageName);
+            }
+            if (ks instanceof KeySetHandle) {
+                KeySetManagerService ksms = mSettings.mKeySetManagerService;
+                return ksms.packageIsSignedByLPr(packageName, (KeySetHandle) ks);
+            }
+            return false;
+        }
+    }
+
+    @Override
+    public boolean isPackageSignedByKeySetExactly(String packageName, IBinder ks) {
+        if (packageName == null || ks == null) {
+            return false;
+        }
+        synchronized(mPackages) {
+            final PackageParser.Package pkg = mPackages.get(packageName);
+            if (pkg == null) {
+                Slog.w(TAG, "KeySet requested for unknown package:" + packageName);
+                throw new IllegalArgumentException("Unknown package: " + packageName);
+            }
+            if (ks instanceof KeySetHandle) {
+                KeySetManagerService ksms = mSettings.mKeySetManagerService;
+                return ksms.packageIsSignedByExactlyLPr(packageName, (KeySetHandle) ks);
+            }
+            return false;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index a69dd30..ebad422 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -58,6 +58,8 @@
 import android.os.SystemService;
 import android.os.UserHandle;
 import android.os.WorkSource;
+import android.os.Parcel;
+import android.os.ServiceManager;
 import android.provider.Settings;
 import android.service.dreams.DreamManagerInternal;
 import android.util.EventLog;
@@ -659,6 +661,7 @@
         if (mLowPowerModeEnabled != lowPowerModeEnabled) {
             mLowPowerModeEnabled = lowPowerModeEnabled;
             powerHintInternal(POWER_HINT_LOW_POWER_MODE, lowPowerModeEnabled ? 1 : 0);
+            setSurfaceFlingerLowPowerMode(lowPowerModeEnabled ? 1 : 0);
             mLowPowerModeEnabled = lowPowerModeEnabled;
             BackgroundThread.getHandler().post(new Runnable() {
                 @Override
@@ -2094,6 +2097,21 @@
         nativeSendPowerHint(hintId, data);
     }
 
+    private static void setSurfaceFlingerLowPowerMode(int enabled) {
+        try {
+            final IBinder flinger = ServiceManager.getService("SurfaceFlinger");
+            if (flinger != null) {
+                final Parcel data = Parcel.obtain();
+                data.writeInterfaceToken("android.ui.ISurfaceComposer");
+                data.writeInt(enabled);
+                flinger.transact(1016, data, null, 0);
+                data.recycle();
+            }
+        } catch (RemoteException ex) {
+            Slog.e(TAG, "Failed to reduce refresh rate", ex);
+        }
+    }
+
     /**
      * Low-level function turn the device off immediately, without trying
      * to be clean.  Most people should use {@link ShutdownThread} for a clean shutdown.
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index efe543b..8f237db 100644
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -16,7 +16,12 @@
 
 package com.android.server.tv;
 
+import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED;
+import static android.media.tv.TvInputManager.INPUT_STATE_DISCONNECTED;
+
 import android.content.Context;
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiHotplugEvent;
 import android.media.AudioDevicePort;
 import android.media.AudioManager;
 import android.media.AudioPatch;
@@ -25,14 +30,22 @@
 import android.media.tv.ITvInputHardware;
 import android.media.tv.ITvInputHardwareCallback;
 import android.media.tv.TvInputHardwareInfo;
+import android.media.tv.TvInputInfo;
 import android.media.tv.TvStreamConfig;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
 import android.os.RemoteException;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 import android.view.KeyEvent;
 import android.view.Surface;
 
+import com.android.server.SystemService;
+
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
@@ -46,23 +59,42 @@
  *
  * @hide
  */
-class TvInputHardwareManager implements TvInputHal.Callback {
+class TvInputHardwareManager
+        implements TvInputHal.Callback, HdmiControlManager.HotplugEventListener {
     private static final String TAG = TvInputHardwareManager.class.getSimpleName();
     private final TvInputHal mHal = new TvInputHal(this);
     private final SparseArray<Connection> mConnections = new SparseArray<Connection>();
     private final List<TvInputHardwareInfo> mInfoList = new ArrayList<TvInputHardwareInfo>();
     private final Context mContext;
+    private final TvInputManagerService.Client mClient;
     private final Set<Integer> mActiveHdmiSources = new HashSet<Integer>();
     private final AudioManager mAudioManager;
+    private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray();
+    // TODO: Should handle INACTIVE case.
+    private final SparseArray<TvInputInfo> mTvInputInfoMap = new SparseArray<TvInputInfo>();
+
+    // Calls to mClient should happen here.
+    private final HandlerThread mHandlerThread = new HandlerThread(TAG);
+    private final Handler mHandler;
 
     private final Object mLock = new Object();
 
-    public TvInputHardwareManager(Context context) {
+    public TvInputHardwareManager(Context context, TvInputManagerService.Client client) {
         mContext = context;
+        mClient = client;
         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
-        // TODO(hdmi): mHdmiManager = mContext.getSystemService(...);
-        // TODO(hdmi): mHdmiClient = mHdmiManager.getTvClient();
         mHal.init();
+
+        mHandlerThread.start();
+        mHandler = new ClientHandler(mHandlerThread.getLooper());
+    }
+
+    public void onBootPhase(int phase) {
+        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+            HdmiControlManager hdmiControlManager =
+                    (HdmiControlManager) mContext.getSystemService(Context.HDMI_CONTROL_SERVICE);
+            hdmiControlManager.addHotplugEventListener(this);
+        }
     }
 
     @Override
@@ -80,7 +112,7 @@
     private void buildInfoListLocked() {
         mInfoList.clear();
         for (int i = 0; i < mConnections.size(); ++i) {
-            mInfoList.add(mConnections.valueAt(i).getInfoLocked());
+            mInfoList.add(mConnections.valueAt(i).getHardwareInfoLocked());
         }
     }
 
@@ -92,7 +124,7 @@
                 Slog.e(TAG, "onDeviceUnavailable: Cannot find a connection with " + deviceId);
                 return;
             }
-            connection.resetLocked(null, null, null, null);
+            connection.resetLocked(null, null, null, null, null);
             mConnections.remove(deviceId);
             buildInfoListLocked();
             // TODO: notify if necessary
@@ -136,6 +168,37 @@
         return false;
     }
 
+    private int convertConnectedToState(boolean connected) {
+        if (connected) {
+            return INPUT_STATE_CONNECTED;
+        } else {
+            return INPUT_STATE_DISCONNECTED;
+        }
+    }
+
+    public void registerTvInputInfo(TvInputInfo info, int deviceId) {
+        if (info.getType() == TvInputInfo.TYPE_VIRTUAL) {
+            throw new IllegalArgumentException("info (" + info + ") has virtual type.");
+        }
+        synchronized (mLock) {
+            if (mTvInputInfoMap.indexOfKey(deviceId) >= 0) {
+                Slog.w(TAG, "Trying to override previous registration: old = "
+                        + mTvInputInfoMap.get(deviceId) + ":" + deviceId + ", new = "
+                        + info + ":" + deviceId);
+            }
+            mTvInputInfoMap.put(deviceId, info);
+
+            for (int i = 0; i < mHdmiStateMap.size(); ++i) {
+                String inputId = findInputIdForHdmiPortLocked(mHdmiStateMap.keyAt(i));
+                if (inputId != null && inputId.equals(info.getId())) {
+                    mHandler.obtainMessage(ClientHandler.DO_SET_AVAILABLE,
+                            convertConnectedToState(mHdmiStateMap.valueAt(i)), 0,
+                            inputId).sendToTarget();
+                }
+            }
+        }
+    }
+
     /**
      * Create a TvInputHardware object with a specific deviceId. One service at a time can access
      * the object, and if more than one process attempts to create hardware with the same deviceId,
@@ -143,7 +206,7 @@
      * release is notified via ITvInputHardwareCallback.onReleased().
      */
     public ITvInputHardware acquireHardware(int deviceId, ITvInputHardwareCallback callback,
-            int callingUid, int resolvedUserId) {
+            TvInputInfo info, int callingUid, int resolvedUserId) {
         if (callback == null) {
             throw new NullPointerException();
         }
@@ -154,14 +217,15 @@
                 return null;
             }
             if (checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
-                TvInputHardwareImpl hardware = new TvInputHardwareImpl(connection.getInfoLocked());
+                TvInputHardwareImpl hardware =
+                        new TvInputHardwareImpl(connection.getHardwareInfoLocked());
                 try {
                     callback.asBinder().linkToDeath(connection, 0);
                 } catch (RemoteException e) {
                     hardware.release();
                     return null;
                 }
-                connection.resetLocked(hardware, callback, callingUid, resolvedUserId);
+                connection.resetLocked(hardware, callback, info, callingUid, resolvedUserId);
             }
             return connection.getHardwareLocked();
         }
@@ -182,26 +246,55 @@
                     || checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
                 return;
             }
-            connection.resetLocked(null, null, null, null);
+            connection.resetLocked(null, null, null, null, null);
+        }
+    }
+
+    private String findInputIdForHdmiPortLocked(int port) {
+        for (TvInputHardwareInfo hardwareInfo : mInfoList) {
+            if (hardwareInfo.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI
+                    && hardwareInfo.getHdmiPortId() == port) {
+                TvInputInfo info = mTvInputInfoMap.get(hardwareInfo.getDeviceId());
+                return (info == null) ? null : info.getId();
+            }
+        }
+        return null;
+    }
+
+    // HdmiControlManager.HotplugEventListener implementation.
+
+    @Override
+    public void onReceived(HdmiHotplugEvent event) {
+        String inputId = null;
+
+        synchronized (mLock) {
+            mHdmiStateMap.put(event.getPort(), event.isConnected());
+            inputId = findInputIdForHdmiPortLocked(event.getPort());
+            if (inputId == null) {
+                return;
+            }
+            mHandler.obtainMessage(ClientHandler.DO_SET_AVAILABLE,
+                    convertConnectedToState(event.isConnected()), 0, inputId).sendToTarget();
         }
     }
 
     private class Connection implements IBinder.DeathRecipient {
-        private final TvInputHardwareInfo mInfo;
+        private final TvInputHardwareInfo mHardwareInfo;
+        private TvInputInfo mInfo;
         private TvInputHardwareImpl mHardware = null;
         private ITvInputHardwareCallback mCallback;
         private TvStreamConfig[] mConfigs = null;
         private Integer mCallingUid = null;
         private Integer mResolvedUserId = null;
 
-        public Connection(TvInputHardwareInfo info) {
-            mInfo = info;
+        public Connection(TvInputHardwareInfo hardwareInfo) {
+            mHardwareInfo = hardwareInfo;
         }
 
         // *Locked methods assume TvInputHardwareManager.mLock is held.
 
-        public void resetLocked(TvInputHardwareImpl hardware,
-                ITvInputHardwareCallback callback, Integer callingUid, Integer resolvedUserId) {
+        public void resetLocked(TvInputHardwareImpl hardware, ITvInputHardwareCallback callback,
+                TvInputInfo info, Integer callingUid, Integer resolvedUserId) {
             if (mHardware != null) {
                 try {
                     mCallback.onReleased();
@@ -212,6 +305,7 @@
             }
             mHardware = hardware;
             mCallback = callback;
+            mInfo = info;
             mCallingUid = callingUid;
             mResolvedUserId = resolvedUserId;
 
@@ -228,7 +322,11 @@
             mConfigs = configs;
         }
 
-        public TvInputHardwareInfo getInfoLocked() {
+        public TvInputHardwareInfo getHardwareInfoLocked() {
+            return mHardwareInfo;
+        }
+
+        public TvInputInfo getInfoLocked() {
             return mInfo;
         }
 
@@ -255,7 +353,7 @@
         @Override
         public void binderDied() {
             synchronized (mLock) {
-                resetLocked(null, null, null, null);
+                resetLocked(null, null, null, null, null);
             }
         }
     }
@@ -403,4 +501,28 @@
             return false;
         }
     }
+
+    private class ClientHandler extends Handler {
+        private static final int DO_SET_AVAILABLE = 1;
+
+        ClientHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public final void handleMessage(Message msg) {
+            switch (msg.what) {
+                case DO_SET_AVAILABLE: {
+                    String inputId = (String) msg.obj;
+                    int state = msg.arg1;
+                    mClient.setState(inputId, state);
+                    break;
+                }
+                default: {
+                    Slog.w(TAG, "Unhandled message: " + msg);
+                    break;
+                }
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 5e95af4..20fdefa 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -16,6 +16,9 @@
 
 package com.android.server.tv;
 
+import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED;
+import static android.media.tv.TvInputManager.INPUT_STATE_DISCONNECTED;
+
 import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -38,6 +41,7 @@
 import android.media.tv.ITvInputHardware;
 import android.media.tv.ITvInputHardwareCallback;
 import android.media.tv.ITvInputManager;
+import android.media.tv.ITvInputManagerCallback;
 import android.media.tv.ITvInputService;
 import android.media.tv.ITvInputServiceCallback;
 import android.media.tv.ITvInputSession;
@@ -109,7 +113,7 @@
         mContentResolver = context.getContentResolver();
         mLogHandler = new LogHandler(IoThread.get().getLooper());
 
-        mTvInputHardwareManager = new TvInputHardwareManager(context);
+        mTvInputHardwareManager = new TvInputHardwareManager(context, new Client());
 
         synchronized (mLock) {
             mUserStates.put(mCurrentUserId, new UserState());
@@ -129,6 +133,7 @@
                 buildTvInputListLocked(mCurrentUserId);
             }
         }
+        mTvInputHardwareManager.onBootPhase(phase);
     }
 
     private void registerBroadcastReceivers() {
@@ -144,7 +149,7 @@
             public void onPackageRemoved(String packageName, int uid) {
                 synchronized (mLock) {
                     UserState userState = getUserStateLocked(mCurrentUserId);
-                    if (!userState.packageList.contains(packageName)) {
+                    if (!userState.packageSet.contains(packageName)) {
                         // Not a TV input package.
                         return;
                     }
@@ -198,8 +203,11 @@
 
     private void buildTvInputListLocked(int userId) {
         UserState userState = getUserStateLocked(userId);
-        userState.inputMap.clear();
-        userState.packageList.clear();
+
+        Map<String, TvInputState> oldInputMap = userState.inputMap;
+        userState.inputMap = new HashMap<String, TvInputState>();
+
+        userState.packageSet.clear();
 
         if (DEBUG) Slog.d(TAG, "buildTvInputList");
         PackageManager pm = mContext.getPackageManager();
@@ -216,8 +224,13 @@
             try {
                 TvInputInfo info = TvInputInfo.createTvInputInfo(mContext, ri);
                 if (DEBUG) Slog.d(TAG, "add " + info.getId());
-                userState.inputMap.put(info.getId(), info);
-                userState.packageList.add(si.packageName);
+                TvInputState state = oldInputMap.get(info.getId());
+                if (state == null) {
+                    state = new TvInputState();
+                }
+                userState.inputMap.put(info.getId(), state);
+                state.mInfo = info;
+                userState.packageSet.add(si.packageName);
 
                 // Reconnect the service if existing input is updated.
                 updateServiceConnectionLocked(info.getId(), userId);
@@ -225,6 +238,7 @@
                 Slog.e(TAG, "Can't load TV input " + si.name, e);
             }
         }
+        oldInputMap.clear();
     }
 
     private void switchUser(int userId) {
@@ -359,7 +373,7 @@
             }
 
             Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(
-                    userState.inputMap.get(inputId).getComponent());
+                    userState.inputMap.get(inputId).mInfo.getComponent());
             // Binding service may fail if the service is updating.
             // In that case, the connection will be revived in buildTvInputListLocked called by
             // onSomePackagesChanged.
@@ -588,7 +602,7 @@
         updateServiceConnectionLocked(sessionState.mInputId, userId);
     }
 
-    private void unregisterCallbackInternalLocked(IBinder clientToken, String inputId,
+    private void unregisterClientInternalLocked(IBinder clientToken, String inputId,
             int userId) {
         UserState userState = getUserStateLocked(userId);
         ClientState clientState = userState.clientStateMap.get(clientToken);
@@ -623,14 +637,43 @@
         }
     }
 
-    private void broadcastServiceAvailabilityChangedLocked(ServiceState serviceState) {
-        for (IBinder clientToken : serviceState.mClientTokens) {
-            try {
-                ITvInputClient.Stub.asInterface(clientToken).onAvailabilityChanged(
-                        serviceState.mTvInputInfo.getId(), serviceState.mAvailable);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "error in onAvailabilityChanged", e);
+    private void notifyStateChangedLocked(UserState userState, String inputId,
+            int state, ITvInputManagerCallback targetCallback) {
+        if (DEBUG) {
+            Slog.d(TAG, "notifyStateChangedLocked: inputId = " + inputId
+                    + "; state = " + state);
+        }
+        if (targetCallback == null) {
+            for (ITvInputManagerCallback callback : userState.callbackSet) {
+                try {
+                    callback.onInputStateChanged(inputId, state);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Failed to report state change to callback.");
+                }
             }
+        } else {
+            try {
+                targetCallback.onInputStateChanged(inputId, state);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to report state change to callback.");
+            }
+        }
+    }
+
+    private void setStateLocked(String inputId, int state, int userId) {
+        UserState userState = getUserStateLocked(userId);
+        TvInputState inputState = userState.inputMap.get(inputId);
+        ServiceState serviceState = userState.serviceStateMap.get(inputId);
+        int oldState = inputState.mState;
+        inputState.mState = state;
+        boolean isStateEmpty = serviceState.mClientTokens.isEmpty()
+                && serviceState.mSessionTokens.isEmpty();
+        if (serviceState != null && serviceState.mService == null && !isStateEmpty) {
+            // We don't notify state change while reconnecting. It should remain disconnected.
+            return;
+        }
+        if (oldState != state) {
+            notifyStateChangedLocked(userState, inputId, state, null);
         }
     }
 
@@ -643,80 +686,29 @@
             try {
                 synchronized (mLock) {
                     UserState userState = getUserStateLocked(resolvedUserId);
-                    return new ArrayList<TvInputInfo>(userState.inputMap.values());
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-
-        @Override
-        public boolean getAvailability(final ITvInputClient client, final String inputId,
-                int userId) {
-            final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
-                    Binder.getCallingUid(), userId, "getAvailability");
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                synchronized (mLock) {
-                    UserState userState = getUserStateLocked(resolvedUserId);
-                    ServiceState serviceState = userState.serviceStateMap.get(inputId);
-                    if (serviceState != null) {
-                        // We already know the status of this input service. Return the cached
-                        // status.
-                        return serviceState.mAvailable;
+                    List<TvInputInfo> inputList = new ArrayList<TvInputInfo>();
+                    for (TvInputState state : userState.inputMap.values()) {
+                        inputList.add(state.mInfo);
                     }
+                    return inputList;
                 }
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
-            // STOPSHIP: Redesign the API around the availability change. For now, the service
-            // will be always available.
-            return true;
         }
 
         @Override
-        public void registerCallback(final ITvInputClient client, final String inputId,
-                int userId) {
+        public void registerCallback(final ITvInputManagerCallback callback, int userId) {
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
                     Binder.getCallingUid(), userId, "registerCallback");
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
-                    // Create a new service callback and add it to the callback map of the current
-                    // service.
                     UserState userState = getUserStateLocked(resolvedUserId);
-                    ServiceState serviceState = userState.serviceStateMap.get(inputId);
-                    if (serviceState == null) {
-                        serviceState = new ServiceState(
-                                userState.inputMap.get(inputId), resolvedUserId);
-                        userState.serviceStateMap.put(inputId, serviceState);
-                    }
-                    IBinder clientToken = client.asBinder();
-                    if (!serviceState.mClientTokens.contains(clientToken)) {
-                        serviceState.mClientTokens.add(clientToken);
-                    }
-
-                    ClientState clientState = userState.clientStateMap.get(clientToken);
-                    if (clientState == null) {
-                        clientState = createClientStateLocked(clientToken, resolvedUserId);
-                    }
-                    if (!clientState.mInputIds.contains(inputId)) {
-                        clientState.mInputIds.add(inputId);
-                    }
-
-                    if (serviceState.mService != null) {
-                        if (serviceState.mCallback != null) {
-                            // We already handled.
-                            return;
-                        }
-                        serviceState.mCallback = new ServiceCallback(resolvedUserId);
-                        try {
-                            serviceState.mService.registerCallback(serviceState.mCallback);
-                        } catch (RemoteException e) {
-                            Slog.e(TAG, "error in registerCallback", e);
-                        }
-                    } else {
-                        updateServiceConnectionLocked(inputId, resolvedUserId);
+                    userState.callbackSet.add(callback);
+                    for (TvInputState state : userState.inputMap.values()) {
+                        notifyStateChangedLocked(userState, state.mInfo.getId(),
+                                state.mState, callback);
                     }
                 }
             } finally {
@@ -725,13 +717,14 @@
         }
 
         @Override
-        public void unregisterCallback(ITvInputClient client, String inputId, int userId) {
+        public void unregisterCallback(ITvInputManagerCallback callback, int userId) {
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
                     Binder.getCallingUid(), userId, "unregisterCallback");
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
-                    unregisterCallbackInternalLocked(client.asBinder(), inputId, resolvedUserId);
+                    UserState userState = getUserStateLocked(resolvedUserId);
+                    userState.callbackSet.remove(callback);
                 }
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -751,7 +744,7 @@
                     ServiceState serviceState = userState.serviceStateMap.get(inputId);
                     if (serviceState == null) {
                         serviceState = new ServiceState(
-                                userState.inputMap.get(inputId), resolvedUserId);
+                                userState.inputMap.get(inputId).mInfo, resolvedUserId);
                         userState.serviceStateMap.put(inputId, serviceState);
                     }
                     // Send a null token immediately while reconnecting.
@@ -868,7 +861,7 @@
                         }
 
                         // Create a log entry and fill it later.
-                        String packageName = userState.inputMap.get(sessionState.mInputId)
+                        String packageName = userState.inputMap.get(sessionState.mInputId).mInfo
                                 .getServiceInfo().packageName;
                         ContentValues values = new ContentValues();
                         values.put(TvContract.WatchedPrograms.COLUMN_PACKAGE_NAME, packageName);
@@ -1017,8 +1010,7 @@
 
         @Override
         public List<TvInputHardwareInfo> getHardwareList() throws RemoteException {
-            if (mContext.checkCallingPermission(
-                    android.Manifest.permission.TV_INPUT_HARDWARE)
+            if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
                     != PackageManager.PERMISSION_GRANTED) {
                 return null;
             }
@@ -1032,10 +1024,25 @@
         }
 
         @Override
+        public void registerTvInputInfo(TvInputInfo info, int deviceId) {
+            if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
+                    != PackageManager.PERMISSION_GRANTED) {
+                return;
+            }
+
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                mTvInputHardwareManager.registerTvInputInfo(info, deviceId);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
         public ITvInputHardware acquireTvInputHardware(int deviceId,
-                ITvInputHardwareCallback callback, int userId) throws RemoteException {
-            if (mContext.checkCallingPermission(
-                    android.Manifest.permission.TV_INPUT_HARDWARE)
+                ITvInputHardwareCallback callback, TvInputInfo info, int userId)
+                throws RemoteException {
+            if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
                     != PackageManager.PERMISSION_GRANTED) {
                 return null;
             }
@@ -1046,7 +1053,7 @@
                     userId, "acquireTvInputHardware");
             try {
                 return mTvInputHardwareManager.acquireHardware(
-                        deviceId, callback, callingUid, resolvedUserId);
+                        deviceId, callback, info, callingUid, resolvedUserId);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -1055,8 +1062,7 @@
         @Override
         public void releaseTvInputHardware(int deviceId, ITvInputHardware hardware, int userId)
                 throws RemoteException {
-            if (mContext.checkCallingPermission(
-                    android.Manifest.permission.TV_INPUT_HARDWARE)
+            if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
                     != PackageManager.PERMISSION_GRANTED) {
                 return;
             }
@@ -1099,16 +1105,16 @@
                     pw.println("UserState (" + userId + "):");
                     pw.increaseIndent();
 
-                    pw.println("inputMap: inputId -> TvInputInfo");
+                    pw.println("inputMap: inputId -> TvInputState");
                     pw.increaseIndent();
-                    for (TvInputInfo info : userState.inputMap.values()) {
-                        pw.println(info.toString());
+                    for (TvInputState state : userState.inputMap.values()) {
+                        pw.println(state.toString());
                     }
                     pw.decreaseIndent();
 
-                    pw.println("packageList:");
+                    pw.println("packageSet:");
                     pw.increaseIndent();
-                    for (String packageName : userState.packageList) {
+                    for (String packageName : userState.packageSet) {
                         pw.println(packageName);
                     }
                     pw.decreaseIndent();
@@ -1169,7 +1175,6 @@
                         pw.println("mService: " + service.mService);
                         pw.println("mCallback: " + service.mCallback);
                         pw.println("mBound: " + service.mBound);
-                        pw.println("mAvailable: " + service.mAvailable);
                         pw.println("mReconnecting: " + service.mReconnecting);
 
                         pw.decreaseIndent();
@@ -1196,18 +1201,38 @@
                     }
                     pw.decreaseIndent();
 
+                    pw.println("callbackSet:");
+                    pw.increaseIndent();
+                    for (ITvInputManagerCallback callback : userState.callbackSet) {
+                        pw.println(callback.toString());
+                    }
+                    pw.decreaseIndent();
+
                     pw.decreaseIndent();
                 }
             }
         }
     }
 
-    private static final class UserState {
-        // A mapping from the TV input id to its TvInputInfo.
-        private final Map<String, TvInputInfo> inputMap = new HashMap<String,TvInputInfo>();
+    private static final class TvInputState {
+        // A TvInputInfo object which represents the TV input.
+        private TvInputInfo mInfo;
 
-        // A list of all TV input packages.
-        private final Set<String> packageList = new HashSet<String>();
+        // The state of TV input. Connected by default.
+        private int mState = INPUT_STATE_CONNECTED;
+
+        @Override
+        public String toString() {
+            return "mInfo: " + mInfo + "; mState: " + mState;
+        }
+    }
+
+    private static final class UserState {
+        // A mapping from the TV input id to its TvInputState.
+        private Map<String, TvInputState> inputMap = new HashMap<String, TvInputState>();
+
+        // A set of all TV input packages.
+        private final Set<String> packageSet = new HashSet<String>();
 
         // A mapping from the token of a client to its state.
         private final Map<IBinder, ClientState> clientStateMap =
@@ -1220,6 +1245,10 @@
         // A mapping from the token of a TV input session to its state.
         private final Map<IBinder, SessionState> sessionStateMap =
                 new HashMap<IBinder, SessionState>();
+
+        // A set of callbacks.
+        private final Set<ITvInputManagerCallback> callbackSet =
+                new HashSet<ITvInputManagerCallback>();
     }
 
     private final class ClientState implements IBinder.DeathRecipient {
@@ -1243,7 +1272,7 @@
             synchronized (mLock) {
                 UserState userState = getUserStateLocked(mUserId);
                 // DO NOT remove the client state of clientStateMap in this method. It will be
-                // removed in releaseSessionLocked() or unregisterCallbackInternalLocked().
+                // removed in releaseSessionLocked() or unregisterClientInternalLocked().
                 ClientState clientState = userState.clientStateMap.get(mClientToken);
                 if (clientState != null) {
                     while (clientState.mSessionTokens.size() > 0) {
@@ -1251,7 +1280,7 @@
                                 clientState.mSessionTokens.get(0), Process.SYSTEM_UID, mUserId);
                     }
                     while (clientState.mInputIds.size() > 0) {
-                        unregisterCallbackInternalLocked(
+                        unregisterClientInternalLocked(
                                 mClientToken, clientState.mInputIds.get(0), mUserId);
                     }
                 }
@@ -1269,7 +1298,6 @@
         private ITvInputService mService;
         private ServiceCallback mCallback;
         private boolean mBound;
-        private boolean mAvailable;
         private boolean mReconnecting;
 
         private ServiceState(TvInputInfo inputInfo, int userId) {
@@ -1325,16 +1353,18 @@
 
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
+            String inputId = mTvInputInfo.getId();
             if (DEBUG) {
-                Slog.d(TAG, "onServiceConnected(inputId=" + mTvInputInfo.getId() + ")");
+                Slog.d(TAG, "onServiceConnected(inputId=" + inputId + ")");
             }
             synchronized (mLock) {
-                ServiceState serviceState = getServiceStateLocked(mTvInputInfo.getId(), mUserId);
+                UserState userState = getUserStateLocked(mUserId);
+                ServiceState serviceState = userState.serviceStateMap.get(inputId);
                 serviceState.mService = ITvInputService.Stub.asInterface(service);
 
                 // Register a callback, if we need to.
                 if (!serviceState.mClientTokens.isEmpty() && serviceState.mCallback == null) {
-                    serviceState.mCallback = new ServiceCallback(mUserId);
+                    serviceState.mCallback = new ServiceCallback(mTvInputInfo.getId(), mUserId);
                     try {
                         serviceState.mService.registerCallback(serviceState.mCallback);
                     } catch (RemoteException e) {
@@ -1346,6 +1376,12 @@
                 for (IBinder sessionToken : serviceState.mSessionTokens) {
                     createSessionInternalLocked(serviceState.mService, sessionToken, mUserId);
                 }
+
+                TvInputState inputState = userState.inputMap.get(inputId);
+                if (inputState != null && inputState.mState != INPUT_STATE_DISCONNECTED) {
+                    notifyStateChangedLocked(userState, mTvInputInfo.getId(),
+                            inputState.mState, null);
+                }
             }
         }
 
@@ -1377,10 +1413,8 @@
                         }
                     }
 
-                    if (serviceState.mAvailable) {
-                        serviceState.mAvailable = false;
-                        broadcastServiceAvailabilityChangedLocked(serviceState);
-                    }
+                    notifyStateChangedLocked(userState, mTvInputInfo.getId(),
+                            INPUT_STATE_DISCONNECTED, null);
                     updateServiceConnectionLocked(mTvInputInfo.getId(), mUserId);
                 }
             }
@@ -1388,24 +1422,22 @@
     }
 
     private final class ServiceCallback extends ITvInputServiceCallback.Stub {
+        private final String mInputId;
         private final int mUserId;
 
-        ServiceCallback(int userId) {
+        ServiceCallback(String inputId, int userId) {
+            mInputId = inputId;
             mUserId = userId;
         }
 
         @Override
-        public void onAvailabilityChanged(String inputId, boolean isAvailable) {
+        public void onInputStateChanged(int state) {
             if (DEBUG) {
-                Slog.d(TAG, "onAvailabilityChanged(inputId=" + inputId + ", isAvailable="
-                        + isAvailable + ")");
+                Slog.d(TAG, "onInputStateChanged(inputId=" + mInputId + ", state="
+                        + state + ")");
             }
             synchronized (mLock) {
-                ServiceState serviceState = getServiceStateLocked(inputId, mUserId);
-                if (serviceState.mAvailable != isAvailable) {
-                    serviceState.mAvailable = isAvailable;
-                    broadcastServiceAvailabilityChangedLocked(serviceState);
-                }
+                setStateLocked(mInputId, state, mUserId);
             }
         }
     }
@@ -1553,4 +1585,12 @@
             mContentResolver.update(uri, values, null, null);
         }
     }
+
+    final class Client {
+        public void setState(String inputId, int state) {
+            synchronized (mLock) {
+                setStateLocked(inputId, state, mCurrentUserId);
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 8387b65..b24072f 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -717,10 +717,10 @@
             float top = w.mFrame.top + w.mYOffset;
 
             // Adjust for surface insets.
-            width += attrs.shadowInsets.left + attrs.shadowInsets.right;
-            height += attrs.shadowInsets.top + attrs.shadowInsets.bottom;
-            left -= attrs.shadowInsets.left;
-            top -= attrs.shadowInsets.top;
+            width += attrs.surfaceInsets.left + attrs.surfaceInsets.right;
+            height += attrs.surfaceInsets.top + attrs.surfaceInsets.bottom;
+            left -= attrs.surfaceInsets.left;
+            top -= attrs.surfaceInsets.top;
 
             if (DEBUG_VISIBILITY) {
                 Slog.v(TAG, "Creating surface in session "
@@ -1140,19 +1140,12 @@
 
     void applyDecorRect(final Rect decorRect) {
         final WindowState w = mWin;
-        int width = w.mFrame.width();
-        int height = w.mFrame.height();
+        final int width = w.mFrame.width();
+        final int height = w.mFrame.height();
 
         // Compute the offset of the window in relation to the decor rect.
-        int left = w.mXOffset + w.mFrame.left;
-        int top = w.mYOffset + w.mFrame.top;
-
-        // Adjust for surface insets.
-        final WindowManager.LayoutParams attrs = w.mAttrs;
-        width += attrs.shadowInsets.left + attrs.shadowInsets.right;
-        height += attrs.shadowInsets.top + attrs.shadowInsets.bottom;
-        left -= attrs.shadowInsets.left;
-        top -= attrs.shadowInsets.top;
+        final int left = w.mXOffset + w.mFrame.left;
+        final int top = w.mYOffset + w.mFrame.top;
 
         // Initialize the decor rect to the entire frame.
         w.mSystemDecorRect.set(0, 0, width, height);
@@ -1182,7 +1175,6 @@
         if (displayContent == null) {
             return;
         }
-        DisplayInfo displayInfo = displayContent.getDisplayInfo();
 
         // Need to recompute a new system decor rect each time.
         if ((w.mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) {
@@ -1192,6 +1184,7 @@
         } else if (!w.isDefaultDisplay()) {
             // On a different display there is no system decor.  Crop the window
             // by the screen boundaries.
+            final DisplayInfo displayInfo = displayContent.getDisplayInfo();
             w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(), w.mCompatFrame.height());
             w.mSystemDecorRect.intersect(-w.mCompatFrame.left, -w.mCompatFrame.top,
                     displayInfo.logicalWidth - w.mCompatFrame.left,
@@ -1202,44 +1195,52 @@
             // windows need to be cropped by the screen, so they don't cover
             // the universe background.
             if (mAnimator.mUniverseBackground == null) {
-                w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(),
-                        w.mCompatFrame.height());
+                w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(), w.mCompatFrame.height());
             } else {
                 applyDecorRect(mService.mScreenRect);
             }
         } else if (w.mAttrs.type == WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND
                 || w.mDecorFrame.isEmpty()) {
             // The universe background isn't cropped, nor windows without policy decor.
-            w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(),
-                    w.mCompatFrame.height());
+            w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(), w.mCompatFrame.height());
         } else {
             // Crop to the system decor specified by policy.
             applyDecorRect(w.mDecorFrame);
         }
 
-        // By default, the clip rect is the system decor rect
-        Rect clipRect = w.mSystemDecorRect;
-        if (mHasClipRect) {
+        // By default, the clip rect is the system decor.
+        final Rect clipRect = mTmpClipRect;
+        clipRect.set(w.mSystemDecorRect);
 
-            // If we have an animated clip rect, intersect it with the system decor rect
-            // NOTE: We are adding a temporary workaround due to the status bar not always reporting
-            // the correct system decor rect.  In such cases, we take into account the specified
-            // content insets as well.
-            int offsetTop = Math.max(w.mSystemDecorRect.top, w.mContentInsets.top);
-            mTmpClipRect.set(w.mSystemDecorRect);
-            // Don't apply the workaround to apps explicitly requesting fullscreen layout.
+        // Expand the clip rect for surface insets.
+        final WindowManager.LayoutParams attrs = w.mAttrs;
+        clipRect.left -= attrs.surfaceInsets.left;
+        clipRect.top -= attrs.surfaceInsets.top;
+        clipRect.right += attrs.surfaceInsets.right;
+        clipRect.bottom += attrs.surfaceInsets.bottom;
+
+        // If we have an animated clip rect, intersect it with the clip rect.
+        if (mHasClipRect) {
+            // NOTE: We are adding a temporary workaround due to the status bar
+            // not always reporting the correct system decor rect. In such
+            // cases, we take into account the specified content insets as well.
             if ((w.mSystemUiVisibility & SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN)
                     == SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN) {
-                mTmpClipRect.intersect(mClipRect);
+                // Don't apply the workaround to apps explicitly requesting
+                // fullscreen layout.
+                clipRect.intersect(mClipRect);
             } else {
-                mTmpClipRect.offset(0, -offsetTop);
-                mTmpClipRect.intersect(mClipRect);
-                mTmpClipRect.offset(0, offsetTop);
+                final int offsetTop = Math.max(clipRect.top, w.mContentInsets.top);
+                clipRect.offset(0, -offsetTop);
+                clipRect.intersect(mClipRect);
+                clipRect.offset(0, offsetTop);
             }
-            clipRect = mTmpClipRect;
-
         }
 
+        // The clip rect was generated assuming (0,0) as the window origin,
+        // so we need to translate to match the actual surface coordinates.
+        clipRect.offset(attrs.surfaceInsets.left, attrs.surfaceInsets.top);
+
         if (!clipRect.equals(mLastClipRect)) {
             mLastClipRect.set(clipRect);
             try {
@@ -1285,10 +1286,10 @@
 
         // Adjust for surface insets.
         final LayoutParams attrs = w.getAttrs();
-        width += attrs.shadowInsets.left + attrs.shadowInsets.right;
-        height += attrs.shadowInsets.top + attrs.shadowInsets.bottom;
-        left -= attrs.shadowInsets.left;
-        top -= attrs.shadowInsets.top;
+        width += attrs.surfaceInsets.left + attrs.surfaceInsets.right;
+        height += attrs.surfaceInsets.top + attrs.surfaceInsets.bottom;
+        left -= attrs.surfaceInsets.left;
+        top -= attrs.surfaceInsets.top;
 
         final boolean surfaceMoved = mSurfaceX != left || mSurfaceY != top;
         if (surfaceMoved) {
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 5233297..c0923ca 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -656,14 +656,13 @@
                     }
                     break;
                 case MSG_USER_SWITCHED: {
-                    mCurrentUser = msg.arg1;
-
                     UserManager userManager =
                             (UserManager) mContext.getSystemService(Context.USER_SERVICE);
                     if (userManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER)) {
                         Slog.v(TAG, "Switched to user with DISALLOW_USB_FILE_TRANSFER restriction;"
                                 + " disabling USB.");
                         setUsbConfig("none");
+                        mCurrentUser = msg.arg1;
                         break;
                     }
 
@@ -675,6 +674,7 @@
                         setUsbConfig("none");
                         setUsbConfig(mCurrentFunctions);
                     }
+                    mCurrentUser = msg.arg1;
                     break;
                 }
             }
diff --git a/telecomm/java/android/telecomm/Call.java b/telecomm/java/android/telecomm/Call.java
new file mode 100644
index 0000000..ba7e253
--- /dev/null
+++ b/telecomm/java/android/telecomm/Call.java
@@ -0,0 +1,710 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecomm;
+
+import android.net.Uri;
+import android.os.RemoteException;
+import android.telephony.DisconnectCause;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Represents an ongoing phone call that the in-call app should present to the user.
+ */
+public final class Call {
+    /**
+     * The state of a {@code Call} when newly created.
+     */
+    public static final int STATE_NEW = 0;
+
+    /**
+     * The state of an outgoing {@code Call} when dialing the remote number, but not yet connected.
+     */
+    public static final int STATE_DIALING = 1;
+
+    /**
+     * The state of an incoming {@code Call} when ringing locally, but not yet connected.
+     */
+    public static final int STATE_RINGING = 2;
+
+    /**
+     * The state of a {@code Call} when in a holding state.
+     */
+    public static final int STATE_HOLDING = 3;
+
+    /**
+     * The state of a {@code Call} when actively supporting conversation.
+     */
+    public static final int STATE_ACTIVE = 4;
+
+    /**
+     * The state of a {@code Call} when no further voice or other communication is being
+     * transmitted, the remote side has been or will inevitably be informed that the {@code Call}
+     * is no longer active, and the local data transport has or inevitably will release resources
+     * associated with this {@code Call}.
+     */
+    public static final int STATE_DISCONNECTED = 7;
+
+    public static class Details {
+        private final Uri mHandle;
+        private final int mHandlePresentation;
+        private final String mCallerDisplayName;
+        private final int mCallerDisplayNamePresentation;
+        private final PhoneAccount mAccount;
+        private final int mCapabilities;
+        private final int mDisconnectCauseCode;
+        private final String mDisconnectCauseMsg;
+        private final long mConnectTimeMillis;
+        private final GatewayInfo mGatewayInfo;
+
+        /**
+         * @return The handle (e.g., phone number) to which the {@code Call} is currently
+         * connected.
+         */
+        public Uri getHandle() {
+            return mHandle;
+        }
+
+        /**
+         * @return The presentation requirements for the handle. See
+         * {@link android.telecomm.CallPropertyPresentation} for valid values.
+         */
+        public int getHandlePresentation() {
+            return mHandlePresentation;
+        }
+
+        /**
+         * @return The display name for the caller.
+         */
+        public String getCallerDisplayName() {
+            return mCallerDisplayName;
+        }
+
+        /**
+         * @return The presentation requirements for the caller display name. See
+         * {@link android.telecomm.CallPropertyPresentation} for valid values.
+         */
+        public int getCallerDisplayNamePresentation() {
+            return mCallerDisplayNamePresentation;
+        }
+
+        /**
+         * @return The {@code PhoneAccount} whereby the {@code Call} is currently being routed.
+         */
+        public PhoneAccount getAccount() {
+            return mAccount;
+        }
+
+        /**
+         * @return A bitmask of the capabilities of the {@code Call}, as defined in
+         *         {@link CallCapabilities}.
+         */
+        public int getCapabilities() {
+            return mCapabilities;
+        }
+
+        /**
+         * @return For a {@link #STATE_DISCONNECTED} {@code Call}, the disconnect cause expressed
+         * as a code chosen from among those declared in {@link DisconnectCause}.
+         */
+        public int getDisconnectCauseCode() {
+            return mDisconnectCauseCode;
+        }
+
+        /**
+         * @return For a {@link #STATE_DISCONNECTED} {@code Call}, an optional reason for
+         * disconnection expressed as a free text message.
+         */
+        public String getDisconnectCauseMsg() {
+            return mDisconnectCauseMsg;
+        }
+
+        /**
+         * @return The time the {@code Call} has been connected. This information is updated
+         * periodically, but user interfaces should not rely on this to display any "call time
+         * clock".
+         */
+        public long getConnectTimeMillis() {
+            return mConnectTimeMillis;
+        }
+
+        /**
+         * @return Information about any calling gateway the {@code Call} may be using.
+         */
+        public GatewayInfo getGatewayInfo() {
+            return mGatewayInfo;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o instanceof Details) {
+                Details d = (Details) o;
+                return
+                        Objects.equals(mHandle, d.mHandle) &&
+                        Objects.equals(mHandlePresentation, d.mHandlePresentation) &&
+                        Objects.equals(mCallerDisplayName, d.mCallerDisplayName) &&
+                        Objects.equals(mCallerDisplayNamePresentation,
+                                d.mCallerDisplayNamePresentation) &&
+                        Objects.equals(mAccount, d.mAccount) &&
+                        Objects.equals(mCapabilities, d.mCapabilities) &&
+                        Objects.equals(mDisconnectCauseCode, d.mDisconnectCauseCode) &&
+                        Objects.equals(mDisconnectCauseMsg, d.mDisconnectCauseMsg) &&
+                        Objects.equals(mConnectTimeMillis, d.mConnectTimeMillis) &&
+                        Objects.equals(mGatewayInfo, d.mGatewayInfo);
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return
+                    Objects.hashCode(mHandle) +
+                    Objects.hashCode(mHandlePresentation) +
+                    Objects.hashCode(mCallerDisplayName) +
+                    Objects.hashCode(mCallerDisplayNamePresentation) +
+                    Objects.hashCode(mAccount) +
+                    Objects.hashCode(mCapabilities) +
+                    Objects.hashCode(mDisconnectCauseCode) +
+                    Objects.hashCode(mDisconnectCauseMsg) +
+                    Objects.hashCode(mConnectTimeMillis) +
+                    Objects.hashCode(mGatewayInfo);
+        }
+
+        /** {@hide} */
+        public Details(
+                Uri handle,
+                int handlePresentation,
+                String callerDisplayName,
+                int callerDisplayNamePresentation,
+                PhoneAccount account,
+                int capabilities,
+                int disconnectCauseCode,
+                String disconnectCauseMsg,
+                long connectTimeMillis,
+                GatewayInfo gatewayInfo) {
+            mHandle = handle;
+            mHandlePresentation = handlePresentation;
+            mCallerDisplayName = callerDisplayName;
+            mCallerDisplayNamePresentation = callerDisplayNamePresentation;
+            mAccount = account;
+            mCapabilities = capabilities;
+            mDisconnectCauseCode = disconnectCauseCode;
+            mDisconnectCauseMsg = disconnectCauseMsg;
+            mConnectTimeMillis = connectTimeMillis;
+            mGatewayInfo = gatewayInfo;
+        }
+    }
+
+    public static abstract class Listener {
+        /**
+         * Invoked when the state of this {@code Call} has changed. See {@link #getState()}.
+         *
+         * TODO(ihab): Provide previous state also?
+         *
+         * @param call The {@code Call} invoking this method.
+         * @param state The new state of the {@code Call}.
+         */
+        public void onStateChanged(Call call, int state) {}
+
+        /**
+         * Invoked when the parent of this {@code Call} has changed. See {@link #getParent()}.
+         *
+         * @param call The {@code Call} invoking this method.
+         * @param parent The new parent of the {@code Call}.
+         */
+        public void onParentChanged(Call call, Call parent) {}
+
+        /**
+         * Invoked when the children of this {@code Call} have changed. See {@link #getChildren()}.
+         *
+         * @param call The {@code Call} invoking this method.
+         * @param children The new children of the {@code Call}.
+         */
+        public void onChildrenChanged(Call call, List<Call> children) {}
+
+        /**
+         * Invoked when the details of this {@code Call} have changed. See {@link #getDetails()}.
+         *
+         * @param call The {@code Call} invoking this method.
+         * @param details A {@code Details} object describing the {@code Call}.
+         */
+        public void onDetailsChanged(Call call, Details details) {}
+
+        /**
+         * Invoked when the text messages that can be used as responses to the incoming
+         * {@code Call} are loaded from the relevant database.
+         * See {@link #getCannedTextResponses()}.
+         *
+         * @param call The {@code Call} invoking this method.
+         * @param cannedTextResponses The text messages useable as responses.
+         */
+        public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) {}
+
+        /**
+         * Invoked when the outgoing {@code Call} has finished dialing but is sending DTMF signals
+         * that were embedded into the outgoing number.
+         *
+         * @param call The {@code Call} invoking this method.
+         * @param remainingPostDialSequence The post-dial characters that remain to be sent.
+         */
+        public void onPostDial(Call call, String remainingPostDialSequence) {}
+
+        /**
+         * Invoked when the post-dial sequence in the outgoing {@code Call} has reached a pause
+         * character. This causes the post-dial signals to stop pending user confirmation. An
+         * implementation should present this choice to the user and invoke
+         * {@link #postDialContinue(boolean)} when the user makes the choice.
+         *
+         * @param call The {@code Call} invoking this method.
+         * @param remainingPostDialSequence The post-dial characters that remain to be sent.
+         */
+        public void onPostDialWait(Call call, String remainingPostDialSequence) {}
+
+        /**
+         * Invoked when the {@code RemoteCallVideoProvider} of the {@code Call} has changed.
+         *
+         * @param call The {@code Call} invoking this method.
+         * @param callVideoProvider The {@code RemoteCallVideoProvider} associated with the
+         * {@code Call}.
+         */
+
+        public void onCallVideoProviderChanged(Call call,
+                RemoteCallVideoProvider callVideoProvider) {}
+
+        /**
+         * Invoked when the {@code Call} is destroyed. Clients should refrain from cleaning
+         * up their UI for the {@code Call} in response to state transitions. Specifically,
+         * clients should not assume that a {@link #onStateChanged(Call, int)} with a state of
+         * {@link #STATE_DISCONNECTED} is the final notification the {@code Call} will send. Rather,
+         * clients should wait for this method to be invoked.
+         *
+         * @param call The {@code Call} being destroyed.
+         */
+        public void onCallDestroyed(Call call) {}
+    }
+
+    private final Phone mPhone;
+    private final String mTelecommCallId;
+    private final InCallAdapter mInCallAdapter;
+    private Call mParent = null;
+    private int mState;
+    private final List<Call> mChildren = new ArrayList<>();
+    private final List<Call> mUnmodifiableChildren = Collections.unmodifiableList(mChildren);
+    private List<String> mCannedTextResponses = null;
+    private String mRemainingPostDialSequence;
+    private RemoteCallVideoProvider mCallVideoProvider;
+    private Details mDetails;
+    private final List<Listener> mListeners = new ArrayList<>();
+
+    /**
+     * Obtains the post-dial sequence remaining to be emitted by this {@code Call}, if any.
+     *
+     * @return The remaining post-dial sequence, or {@code null} if there is no post-dial sequence
+     * remaining or this {@code Call} is not in a post-dial state.
+     */
+    public String getRemainingPostDialSequence() {
+        return mRemainingPostDialSequence;
+    }
+
+    /**
+     * Instructs this {@link #STATE_RINGING} {@code Call} to answer.
+     */
+    public void answer() {
+        mInCallAdapter.answerCall(mTelecommCallId);
+    }
+
+    /**
+     * Instructs this {@link #STATE_RINGING} {@code Call} to reject.
+     *
+     * @param rejectWithMessage Whether to reject with a text message.
+     * @param textMessage An optional text message with which to respond.
+     */
+    public void reject(boolean rejectWithMessage, String textMessage) {
+        mInCallAdapter.rejectCall(mTelecommCallId, rejectWithMessage, textMessage);
+    }
+
+    /**
+     * Instructs this {@code Call} to disconnect.
+     */
+    public void disconnect() {
+        mInCallAdapter.disconnectCall(mTelecommCallId);
+    }
+
+    /**
+     * Instructs this {@code Call} to go on hold.
+     */
+    public void hold() {
+        mInCallAdapter.holdCall(mTelecommCallId);
+    }
+
+    /**
+     * Instructs this {@link #STATE_HOLDING} call to release from hold.
+     */
+    public void unhold() {
+        mInCallAdapter.unholdCall(mTelecommCallId);
+    }
+
+    /**
+     * Instructs this {@code Call} to play a dual-tone multi-frequency signaling (DTMF) tone.
+     *
+     * Any other currently playing DTMF tone in the specified call is immediately stopped.
+     *
+     * @param digit A character representing the DTMF digit for which to play the tone. This
+     *         value must be one of {@code '0'} through {@code '9'}, {@code '*'} or {@code '#'}.
+     */
+    public void playDtmfTone(char digit) {
+        mInCallAdapter.playDtmfTone(mTelecommCallId, digit);
+    }
+
+    /**
+     * Instructs this {@code Call} to stop any dual-tone multi-frequency signaling (DTMF) tone
+     * currently playing.
+     *
+     * DTMF tones are played by calling {@link #playDtmfTone(char)}. If no DTMF tone is
+     * currently playing, this method will do nothing.
+     */
+    public void stopDtmfTone() {
+        mInCallAdapter.stopDtmfTone(mTelecommCallId);
+    }
+
+    /**
+     * Instructs this {@code Call} to continue playing a post-dial DTMF string.
+     *
+     * A post-dial DTMF string is a string of digits entered after a phone number, when dialed,
+     * that are immediately sent as DTMF tones to the recipient as soon as the connection is made.
+     * While these tones are playing, this {@code Call} will notify listeners via
+     * {@link Listener#onPostDial(Call, String)}.
+     *
+     * If the DTMF string contains a {@link TelecommConstants#DTMF_CHARACTER_PAUSE} symbol, this
+     * {@code Call} will temporarily pause playing the tones for a pre-defined period of time.
+     *
+     * If the DTMF string contains a {@link TelecommConstants#DTMF_CHARACTER_WAIT} symbol, this
+     * {@code Call} will pause playing the tones and notify listeners via
+     * {@link Listener#onPostDialWait(Call, String)}. At this point, the in-call app
+     * should display to the user an indication of this state and an affordance to continue
+     * the postdial sequence. When the user decides to continue the postdial sequence, the in-call
+     * app should invoke the {@link #postDialContinue(boolean)} method.
+     *
+     * @param proceed Whether or not to continue with the post-dial sequence.
+     */
+    public void postDialContinue(boolean proceed) {
+        mInCallAdapter.postDialContinue(mTelecommCallId, proceed);
+    }
+
+    /**
+     * Notifies this {@code Call} that the phone account user interface element was touched.
+     *
+     * TODO(ihab): Figure out if and how we can generalize this
+     */
+    public void phoneAccountClicked() {
+        mInCallAdapter.phoneAccountClicked(mTelecommCallId);
+    }
+
+    /**
+     * Instructs this {@code Call} to enter a conference.
+     */
+    public void conference() {
+        mInCallAdapter.conference(mTelecommCallId);
+    }
+
+    /**
+     * Instructs this {@code Call} to split from any conference call with which it may be
+     * connected.
+     */
+    public void splitFromConference() {
+        mInCallAdapter.splitFromConference(mTelecommCallId);
+    }
+
+    /**
+     * Instructs this {@code Call} to swap itself with an existing background call, if one
+     * such call exists.
+     */
+    public void swapWithBackgroundCall() {
+        mInCallAdapter.swapWithBackgroundCall(mTelecommCallId);
+    }
+
+    /**
+     * Obtains the parent of this {@code Call} in a conference, if any.
+     *
+     * @return The parent {@code Call}, or {@code null} if this {@code Call} is not a
+     * child of any conference {@code Call}s.
+     */
+    public Call getParent() {
+        return mParent;
+    }
+
+    /**
+     * Obtains the children of this conference {@code Call}, if any.
+     *
+     * @return The children of this {@code Call} if this {@code Call} is a conference, or an empty
+     * {@code List} otherwise.
+     */
+    public List<Call> getChildren() {
+        return mUnmodifiableChildren;
+    }
+
+    /**
+     * Obtains the state of this {@code Call}.
+     *
+     * @return A state value, chosen from the {@code STATE_*} constants.
+     */
+    public int getState() {
+        return mState;
+    }
+
+    /**
+     * Obtains a list of canned, pre-configured message responses to present to the user as
+     * ways of rejecting this {@code Call} using via a text message.
+     *
+     * @see #reject(boolean, String)
+     *
+     * @return A list of canned text message responses.
+     */
+    public List<String> getCannedTextResponses() {
+        return mCannedTextResponses;
+    }
+
+    /**
+     * Obtains an object that can be used to display video from this {@code Call}.
+     *
+     * @return An {@code ICallVideoProvider}.
+     */
+    public RemoteCallVideoProvider getCallVideoProvider() {
+        return mCallVideoProvider;
+    }
+
+    /**
+     * Obtains an object containing call details.
+     *
+     * @return A {@link Details} object. Depending on the state of the {@code Call}, the
+     * result may be {@code null}.
+     */
+    public Details getDetails() {
+        return mDetails;
+    }
+
+    /**
+     * Adds a listener to this {@code Call}.
+     *
+     * @param listener A {@code Listener}.
+     */
+    public void addListener(Listener listener) {
+        mListeners.add(listener);
+    }
+
+    /**
+     * Removes a listener from this {@code Call}.
+     *
+     * @param listener A {@code Listener}.
+     */
+    public void removeListener(Listener listener) {
+        mListeners.remove(listener);
+    }
+
+    /** {@hide} */
+    Call(Phone phone, String telecommCallId, InCallAdapter inCallAdapter) {
+        mPhone = phone;
+        mTelecommCallId = telecommCallId;
+        mInCallAdapter = inCallAdapter;
+        mState = STATE_NEW;
+    }
+
+    /** {@hide} */
+    final String internalGetCallId() {
+        return mTelecommCallId;
+    }
+
+    /** {@hide} */
+    final void internalUpdate(InCallCall inCallCall) {
+        // First, we update the internal state as far as possible before firing any updates.
+
+        Details details = new Details(
+                inCallCall.getHandle(),
+                inCallCall.getHandlePresentation(),
+                inCallCall.getCallerDisplayName(),
+                inCallCall.getCallerDisplayNamePresentation(),
+                inCallCall.getAccount(),
+                inCallCall.getCapabilities(),
+                inCallCall.getDisconnectCauseCode(),
+                inCallCall.getDisconnectCauseMsg(),
+                inCallCall.getConnectTimeMillis(),
+                inCallCall.getGatewayInfo());
+        boolean detailsChanged = !Objects.equals(mDetails, details);
+        if (detailsChanged) {
+            mDetails = details;
+        }
+
+        boolean cannedTextResponsesChanged = false;
+        if (mCannedTextResponses == null && inCallCall.getCannedSmsResponses() != null
+                && !inCallCall.getCannedSmsResponses().isEmpty()) {
+            mCannedTextResponses = Collections.unmodifiableList(inCallCall.getCannedSmsResponses());
+        }
+
+        boolean callVideoProviderChanged = false;
+        try {
+            callVideoProviderChanged =
+                    !Objects.equals(mCallVideoProvider, inCallCall.getCallVideoProvider());
+            if (callVideoProviderChanged) {
+                mCallVideoProvider = inCallCall.getCallVideoProvider();
+            }
+        } catch (RemoteException e) {
+        }
+
+        int state = stateFromInCallCallState(inCallCall.getState());
+        boolean stateChanged = mState != state;
+        if (stateChanged) {
+            mState = state;
+        }
+
+        if (inCallCall.getParentCallId() != null) {
+            mParent = mPhone.internalGetCallByTelecommId(inCallCall.getParentCallId());
+        }
+
+        mChildren.clear();
+        if (inCallCall.getChildCallIds() != null) {
+            for (int i = 0; i < inCallCall.getChildCallIds().size(); i++) {
+                mChildren.add(mPhone.internalGetCallByTelecommId(
+                        inCallCall.getChildCallIds().get(i)));
+            }
+        }
+
+        // Now we fire updates, ensuring that any client who listens to any of these notifications
+        // gets the most up-to-date state.
+
+        if (stateChanged) {
+            fireStateChanged(mState);
+        }
+        if (detailsChanged) {
+            fireDetailsChanged(mDetails);
+        }
+        if (cannedTextResponsesChanged) {
+            fireCannedTextResponsesLoaded(mCannedTextResponses);
+        }
+        if (callVideoProviderChanged) {
+            fireCallVideoProviderChanged(mCallVideoProvider);
+        }
+
+        // If we have transitioned to DISCONNECTED, that means we need to notify clients and
+        // remove ourselves from the Phone. Note that we do this after completing all state updates
+        // so a client can cleanly transition all their UI to the state appropriate for a
+        // DISCONNECTED Call while still relying on the existence of that Call in the Phone's list.
+        if (mState == STATE_DISCONNECTED) {
+            fireCallDestroyed();
+            mPhone.internalRemoveCall(this);
+        }
+    }
+
+    /** {@hide} */
+    final void internalSetPostDial(String remaining) {
+        mRemainingPostDialSequence = remaining;
+        firePostDial(mRemainingPostDialSequence);
+    }
+
+    /** {@hide} */
+    final void internalSetPostDialWait(String remaining) {
+        mRemainingPostDialSequence = remaining;
+        firePostDialWait(mRemainingPostDialSequence);
+    }
+
+    private void fireStateChanged(int newState) {
+        Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+        for (int i = 0; i < listeners.length; i++) {
+            listeners[i].onStateChanged(this, newState);
+        }
+    }
+
+    private void fireParentChanged(Call newParent) {
+        Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+        for (int i = 0; i < listeners.length; i++) {
+            listeners[i].onParentChanged(this, newParent);
+        }
+    }
+
+    private void fireChildrenChanged(List<Call> children) {
+        Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+        for (int i = 0; i < listeners.length; i++) {
+            listeners[i].onChildrenChanged(this, children);
+        }
+    }
+
+    private void fireDetailsChanged(Details details) {
+        Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+        for (int i = 0; i < listeners.length; i++) {
+            listeners[i].onDetailsChanged(this, details);
+        }
+    }
+
+    private void fireCannedTextResponsesLoaded(List<String> cannedTextResponses) {
+        Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+        for (int i = 0; i < listeners.length; i++) {
+            listeners[i].onCannedTextResponsesLoaded(this, cannedTextResponses);
+        }
+    }
+
+    private void fireCallVideoProviderChanged(RemoteCallVideoProvider callVideoProvider) {
+        Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+        for (int i = 0; i < listeners.length; i++) {
+            listeners[i].onCallVideoProviderChanged(this, callVideoProvider);
+        }
+    }
+
+    private void firePostDial(String remainingPostDialSequence) {
+        Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+        for (int i = 0; i < listeners.length; i++) {
+            listeners[i].onPostDial(this, remainingPostDialSequence);
+        }
+    }
+
+    private void firePostDialWait(String remainingPostDialSequence) {
+        Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+        for (int i = 0; i < listeners.length; i++) {
+            listeners[i].onPostDialWait(this, remainingPostDialSequence);
+        }
+    }
+
+    private void fireCallDestroyed() {
+        Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+        for (int i = 0; i < listeners.length; i++) {
+            listeners[i].onCallDestroyed(this);
+        }
+    }
+
+    private int stateFromInCallCallState(CallState inCallCallState) {
+        switch (inCallCallState) {
+            case NEW:
+                return STATE_NEW;
+            case DIALING:
+                return STATE_DIALING;
+            case RINGING:
+                return STATE_RINGING;
+            case ACTIVE:
+                return STATE_ACTIVE;
+            case ON_HOLD:
+                return STATE_HOLDING;
+            case DISCONNECTED:
+                return STATE_DISCONNECTED;
+            case ABORTED:
+                return STATE_DISCONNECTED;
+            default:
+                Log.wtf(this, "Unrecognized CallState %s", inCallCallState);
+                return STATE_NEW;
+        }
+    }
+}
diff --git a/telecomm/java/android/telecomm/CallPropertyPresentation.java b/telecomm/java/android/telecomm/CallPropertyPresentation.java
index 350980c..319e565 100644
--- a/telecomm/java/android/telecomm/CallPropertyPresentation.java
+++ b/telecomm/java/android/telecomm/CallPropertyPresentation.java
@@ -19,14 +19,14 @@
 /** Defines how numbers and names are displayed in caller id. */
 public class CallPropertyPresentation {
     /** Property is displayed normally. */
-    public static final int ALLOWED = 0;
+    public static final int ALLOWED = 1;
 
     /** Property was blocked. */
-    public static final int RESTRICTED = 1;
+    public static final int RESTRICTED = 2;
 
     /** Presentation was not specified or is unknown. */
-    public static final int UNKNOWN = 2;
+    public static final int UNKNOWN = 3;
 
     /** Property should be displayed as a pay phone. */
-    public static final int PAYPHONE = 3;
+    public static final int PAYPHONE = 4;
 }
diff --git a/telecomm/java/android/telecomm/CallServiceDescriptor.java b/telecomm/java/android/telecomm/CallServiceDescriptor.java
deleted file mode 100644
index 5ae07d3..0000000
--- a/telecomm/java/android/telecomm/CallServiceDescriptor.java
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telecomm;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Log;
-
-import java.util.Locale;
-import java.util.UUID;
-
-/**
- * An immutable object containing information about a given {@link ConnectionService}. Instances are
- * created using the enclosed {@link Builder}.
- */
-public final class CallServiceDescriptor implements Parcelable {
-    private static final String TAG = CallServiceDescriptor.class.getSimpleName();
-
-    /**
-     * A placeholder value indicating an invalid network type.
-     * @hide
-     */
-    private static final int FLAG_INVALID = 0;
-
-    /**
-     * Indicates that the device must be connected to a Wi-Fi network in order for the backing
-     * {@link ConnectionService} to be used.
-     */
-    public static final int FLAG_WIFI = 0x01;
-
-    /**
-     * Indicates that the device must be connected to a cellular PSTN network in order for the
-     * backing {@link ConnectionService} to be used.
-     */
-    public static final int FLAG_PSTN = 0x02;
-
-    /**
-     * Indicates that the device must be connected to a cellular data network in order for the
-     * backing {@link ConnectionService} to be used.
-     */
-    public static final int FLAG_MOBILE = 0x04;
-
-    /**
-     * Represents all of the defined FLAG_ constants so validity can be easily checked.
-     * @hide
-     */
-    public static final int FLAG_ALL = FLAG_WIFI | FLAG_PSTN | FLAG_MOBILE;
-
-    /**
-     * A unique ID used to identify a given instance.
-     */
-    private final String mConnectionServiceId;
-
-    /**
-     * The {@link ComponentName} of the {@link ConnectionService} implementation which this is
-     * describing.
-     */
-    private final ComponentName mComponentName;
-
-    /**
-     * The type of connection that the {@link ConnectionService} requires; will be one of the FLAG_*
-     * constants defined in this class.
-     */
-    private final int mNetworkType;
-
-    private CallServiceDescriptor(
-            String connectionServiceId,
-            ComponentName componentName,
-            int networkType) {
-
-        mConnectionServiceId = connectionServiceId;
-        mComponentName = componentName;
-        mNetworkType = networkType;
-    }
-
-    /**
-     * @return The ID used to identify this {@link ConnectionService}.
-     */
-    public String getConnectionServiceId() {
-        return mConnectionServiceId;
-    }
-
-    /**
-     * @return The {@link ComponentName} of the {@link ConnectionService}.
-     */
-    public ComponentName getServiceComponent() {
-        return mComponentName;
-    }
-
-    /**
-     * @return The network type required by the {@link ConnectionService} to place a call.
-     */
-    public int getNetworkType() {
-        return mNetworkType;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null) {
-            return false;
-        }
-        if (!(obj instanceof CallServiceDescriptor)) {
-            return false;
-        }
-        CallServiceDescriptor descriptor = (CallServiceDescriptor) obj;
-        return mConnectionServiceId.equals(descriptor.mConnectionServiceId) &&
-                mComponentName.equals(descriptor.mComponentName) &&
-                mNetworkType == descriptor.mNetworkType;
-    }
-
-    @Override
-    public String toString() {
-        return String.format(Locale.US, "[%s, component: %s]",
-                CallServiceDescriptor.class.getSimpleName(),
-                mComponentName == null ? "null" : mComponentName.flattenToShortString());
-    }
-
-    /**
-     * @param context {@link Context} to use for the construction of the {@link Builder}.
-     * @return A new {@link Builder} instance.
-     */
-    public static Builder newBuilder(Context context) {
-        return new Builder(context);
-    }
-
-    /**
-     * Creates {@link CallServiceDescriptor} instances. Builders should be created with the
-     * {@link CallServiceDescriptor#newBuilder(Context)} method.
-     */
-    public static class Builder {
-        /** The {@link Context} to use to verify {@link ComponentName} ownership. */
-        private Context mContext;
-
-        /** The {@link ComponentName} pointing to the backing {@link ConnectionService}. */
-        private ComponentName mComponentName;
-
-        /** The required network type that the {@link ConnectionService} needs. */
-        private int mNetworkType = FLAG_INVALID;
-
-        private Builder(Context context) {
-            mContext = context;
-        }
-
-        /**
-         * Set which {@link ConnectionService} this {@link CallServiceDescriptor} is describing.
-         *
-         * @param serviceClass The {@link ConnectionService} class
-         * @return This {@link Builder} for method chaining.
-         */
-        public Builder setConnectionService(Class<? extends ConnectionService> serviceClass) {
-            mComponentName = new ComponentName(mContext, serviceClass);
-            return this;
-        }
-
-        /**
-         * Which network type the backing {@link ConnectionService} requires. This must be one of
-         * the {@link CallServiceDescriptor}.TYPE_* fields.
-         *
-         * @param networkType Which network type the backing {@link ConnectionService} requires.
-         * @return This {@link Builder} for method chaining.
-         */
-        public Builder setNetworkType(int networkType) {
-            mNetworkType = networkType;
-            return this;
-        }
-
-        /**
-         * @return A constructed {@link CallServiceDescriptor} object.
-         */
-        public CallServiceDescriptor build() {
-            // STOPSHIP: Verify validity of ComponentName (permissions, intents, etc)
-
-            // Make sure that they passed in a valid network flag combination
-            if (mNetworkType == FLAG_INVALID || ((mNetworkType & FLAG_ALL) == 0)) {
-
-                Log.wtf(TAG, "Invalid network type for " + mComponentName);
-                // Revert them back to TYPE_INVALID so it won't be considered.
-                mNetworkType = FLAG_INVALID;
-            }
-
-            // TODO: Should we use a sha1 of the ComponentName? Would prevent duplicates.
-            return new CallServiceDescriptor(
-                UUID.randomUUID().toString(), mComponentName, mNetworkType);
-        }
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeString(mConnectionServiceId);
-        dest.writeParcelable(mComponentName, 0);
-        dest.writeInt(mNetworkType);
-    }
-
-    public static final Creator<CallServiceDescriptor> CREATOR =
-            new Creator<CallServiceDescriptor>() {
-        @Override
-        public CallServiceDescriptor createFromParcel(Parcel source) {
-            String id = source.readString();
-            ComponentName componentName = source.readParcelable(
-                    CallServiceDescriptor.class.getClassLoader());
-            int networkType = source.readInt();
-
-            return new CallServiceDescriptor(id, componentName, networkType);
-        }
-
-        @Override
-        public CallServiceDescriptor[] newArray(int size) {
-            return new CallServiceDescriptor[size];
-        }
-    };
-}
diff --git a/telecomm/java/android/telecomm/CallServiceLookupResponse.java b/telecomm/java/android/telecomm/CallServiceLookupResponse.java
deleted file mode 100644
index dd35a24..0000000
--- a/telecomm/java/android/telecomm/CallServiceLookupResponse.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telecomm;
-
-import android.os.RemoteException;
-
-import com.android.internal.telecomm.ICallServiceLookupResponse;
-
-import java.util.List;
-
-/**
- * Used by {@link CallServiceProvider} to return a list of {@link CallServiceDescriptor}s.
- */
-public final class CallServiceLookupResponse {
-    private final ICallServiceLookupResponse mResponse;
-
-    /**
-     * {@hide}
-     */
-    public CallServiceLookupResponse(ICallServiceLookupResponse response) {
-        mResponse = response;
-    }
-
-    /**
-     * Passes the sorted list of preferred {@link CallServiceDescriptor}s back to Telecomm.  Used
-     * in the context of attempting to place a pending outgoing call.
-     *
-     * @param callServiceDescriptors The set of call-service descriptors from
-     * {@link CallServiceProvider}.
-     */
-    public void setCallServiceDescriptors(List<CallServiceDescriptor> callServiceDescriptors) {
-        try {
-            mResponse.setCallServiceDescriptors(callServiceDescriptors);
-        } catch (RemoteException e) {
-        }
-    }
-}
diff --git a/telecomm/java/android/telecomm/CallServiceProvider.java b/telecomm/java/android/telecomm/CallServiceProvider.java
deleted file mode 100644
index c50334a..0000000
--- a/telecomm/java/android/telecomm/CallServiceProvider.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2013 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.telecomm;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-
-import com.android.internal.telecomm.ICallServiceLookupResponse;
-import com.android.internal.telecomm.ICallServiceProvider;
-
-/**
- * Base implementation of a call service provider which extends {@link Service}. This class
- * should be extended by an app that wants to supply phone calls to be handled and managed by
- * the device's in-call interface. All method-calls from the framework to the call service provider
- * are passed through to the main thread for before executing the overriden methods of
- * CallServiceProvider.
- *
- * TODO(santoscordon): Improve paragraph above once the final design is in place. Needs more
- * about how this can be used.
- */
-public abstract class CallServiceProvider extends Service {
-
-    /**
-     * Default Handler used to consolidate binder method calls onto a single thread.
-     */
-    private final class CallServiceProviderMessageHandler extends Handler {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_LOOKUP_CALL_SERVICES:
-                    CallServiceLookupResponse response =
-                            new CallServiceLookupResponse((ICallServiceLookupResponse) msg.obj);
-                    lookupCallServices(response);
-                    break;
-            }
-        }
-    }
-
-    /**
-     * Default ICallServiceProvider implementation provided to CallsManager via {@link #onBind}.
-     */
-    private final class CallServiceProviderWrapper extends ICallServiceProvider.Stub {
-        /** {@inheritDoc} */
-        @Override
-        public void lookupCallServices(ICallServiceLookupResponse callServiceLookupResponse) {
-            Message message = mMessageHandler.obtainMessage(
-                    MSG_LOOKUP_CALL_SERVICES, callServiceLookupResponse);
-            message.sendToTarget();
-        }
-    }
-
-    // Only used internally by this class.
-    // Binder method calls on this service can occur on multiple threads. These messages are used
-    // in conjunction with {@link #mMessageHandler} to ensure that all callbacks are handled on a
-    // single thread.  Keeping it on a single thread allows CallService implementations to avoid
-    // needing multi-threaded code in their own callback routines.
-    private static final int MSG_LOOKUP_CALL_SERVICES = 1;
-
-    /**
-     * Message handler for consolidating binder callbacks onto a single thread.
-     * See {@link CallServiceProviderMessageHandler}.
-     */
-    private final CallServiceProviderMessageHandler mMessageHandler;
-
-    /**
-     * Default binder implementation of {@link ICallServiceProvider} interface.
-     */
-    private final CallServiceProviderWrapper mBinder;
-
-    /**
-     * Protected constructor called only by subclasses creates the binder interface and
-     * single-threaded message handler.
-     */
-    protected CallServiceProvider() {
-        mMessageHandler = new CallServiceProviderMessageHandler();
-        mBinder = new CallServiceProviderWrapper();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public IBinder onBind(Intent intent) {
-        return mBinder;
-    }
-
-    /**
-     * Initiates the process to retrieve the list of {@link CallServiceDescriptor}s implemented by
-     * this provider.
-     *
-     * @param response The response object through which the list of call services is sent.
-     */
-    public abstract void lookupCallServices(CallServiceLookupResponse response);
-}
diff --git a/telecomm/java/android/telecomm/CallState.java b/telecomm/java/android/telecomm/CallState.java
index 152c2023..a464da5 100644
--- a/telecomm/java/android/telecomm/CallState.java
+++ b/telecomm/java/android/telecomm/CallState.java
@@ -48,22 +48,6 @@
     RINGING,
 
     /**
-     * Indicates that the call is active but in a "post-dial" state where Telecomm is now sending
-     * some dual-tone multi-frequency signaling (DTMF) tones appended to the dialed number. Normal
-     * transitions are to {@link #POST_DIAL_WAIT} when the post-dial string requires user
-     * confirmation to proceed, {@link #ACTIVE} when the post-dial tones are completed, or
-     * {@link #DISCONNECTED}.
-     */
-    POST_DIAL,
-
-    /**
-     * Indicates that the call was in the {@link #POST_DIAL} state but is now waiting for user
-     * confirmation before the remaining digits can be sent. Normal transitions are to
-     * {@link #POST_DIAL} when the user asks Telecomm to proceed with the post-dial sequence.
-     */
-    POST_DIAL_WAIT,
-
-    /**
      * Indicates that a call is currently connected to another party and a communication channel is
      * open between them. The normal transition to this state is by the user answering a
      * {@link #DIALING} call or a {@link #RINGING} call being answered by the other party.
diff --git a/telecomm/java/android/telecomm/CallVideoClient.java b/telecomm/java/android/telecomm/CallVideoClient.java
index 76b28fa..fb970dc 100644
--- a/telecomm/java/android/telecomm/CallVideoClient.java
+++ b/telecomm/java/android/telecomm/CallVideoClient.java
@@ -241,6 +241,7 @@
      *
      * @param callCameraCapabilities The changed camera capabilities.
      */
-    public abstract void onHandleCameraCapabilitiesChange(CallCameraCapabilities callCameraCapabilities);
+    public abstract void onHandleCameraCapabilitiesChange(
+            CallCameraCapabilities callCameraCapabilities);
 }
 
diff --git a/telecomm/java/android/telecomm/ConnectionRequest.java b/telecomm/java/android/telecomm/ConnectionRequest.java
index 0db9e29..5888d6a 100644
--- a/telecomm/java/android/telecomm/ConnectionRequest.java
+++ b/telecomm/java/android/telecomm/ConnectionRequest.java
@@ -30,11 +30,11 @@
     // TODO: Token to limit recursive invocations
     // TODO: Consider upgrading "mHandle" to ordered list of handles, indicating a set of phone
     //         numbers that would satisfy the client's needs, in order of preference
+    private final PhoneAccount mAccount;
     private final String mCallId;
     private final Uri mHandle;
     private final int mHandlePresentation;
     private final Bundle mExtras;
-    private final PhoneAccount mAccount;
     private final int mVideoState;
 
     /**
@@ -61,6 +61,15 @@
         mVideoState = videoState;
     }
 
+    private ConnectionRequest(Parcel in) {
+        mAccount = in.readParcelable(getClass().getClassLoader());
+        mCallId = in.readString();
+        mHandle = in.readParcelable(getClass().getClassLoader());
+        mHandlePresentation = in.readInt();
+        mExtras = in.readParcelable(getClass().getClassLoader());
+        mVideoState = in.readInt();
+    }
+
     /**
      * The account which should be used to place the call.
      */
@@ -109,26 +118,17 @@
                 mExtras == null ? "" : mExtras);
     }
 
-    public static final Parcelable.Creator<ConnectionRequest> CREATOR =
-            new Parcelable.Creator<ConnectionRequest> () {
-                @Override
-                public ConnectionRequest createFromParcel(Parcel source) {
-                    PhoneAccount account = (PhoneAccount) source.readParcelable(
-                            getClass().getClassLoader());
-                    String callId = source.readString();
-                    Uri handle = (Uri) source.readParcelable(getClass().getClassLoader());
-                    int presentation = source.readInt();
-                    Bundle extras = (Bundle) source.readParcelable(getClass().getClassLoader());
-                    int videoState = source.readInt();
-                    return new ConnectionRequest(
-                            account, callId, handle, presentation, extras, videoState);
-                }
+    public static final Creator<ConnectionRequest> CREATOR = new Creator<ConnectionRequest> () {
+        @Override
+        public ConnectionRequest createFromParcel(Parcel source) {
+            return new ConnectionRequest(source);
+        }
 
-                @Override
-                public ConnectionRequest[] newArray(int size) {
-                    return new ConnectionRequest[size];
-                }
-            };
+        @Override
+        public ConnectionRequest[] newArray(int size) {
+            return new ConnectionRequest[size];
+        }
+    };
 
     /**
      * {@inheritDoc}
diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java
index 1966081..178cee8 100644
--- a/telecomm/java/android/telecomm/ConnectionService.java
+++ b/telecomm/java/android/telecomm/ConnectionService.java
@@ -47,22 +47,21 @@
     private static final Connection NULL_CONNECTION = new Connection() {};
 
     private static final int MSG_ADD_CALL_SERVICE_ADAPTER = 1;
-    private static final int MSG_CALL = 2;
+    private static final int MSG_CREATE_CONNECTION = 2;
     private static final int MSG_ABORT = 3;
-    private static final int MSG_CREATE_INCOMING_CALL = 4;
-    private static final int MSG_ANSWER = 5;
-    private static final int MSG_REJECT = 6;
-    private static final int MSG_DISCONNECT = 7;
-    private static final int MSG_HOLD = 8;
-    private static final int MSG_UNHOLD = 9;
-    private static final int MSG_ON_AUDIO_STATE_CHANGED = 10;
-    private static final int MSG_PLAY_DTMF_TONE = 11;
-    private static final int MSG_STOP_DTMF_TONE = 12;
-    private static final int MSG_CONFERENCE = 13;
-    private static final int MSG_SPLIT_FROM_CONFERENCE = 14;
-    private static final int MSG_SWAP_WITH_BACKGROUND_CALL = 15;
-    private static final int MSG_ON_POST_DIAL_CONTINUE = 16;
-    private static final int MSG_ON_PHONE_ACCOUNT_CLICKED = 17;
+    private static final int MSG_ANSWER = 4;
+    private static final int MSG_REJECT = 5;
+    private static final int MSG_DISCONNECT = 6;
+    private static final int MSG_HOLD = 7;
+    private static final int MSG_UNHOLD = 8;
+    private static final int MSG_ON_AUDIO_STATE_CHANGED = 9;
+    private static final int MSG_PLAY_DTMF_TONE = 10;
+    private static final int MSG_STOP_DTMF_TONE = 11;
+    private static final int MSG_CONFERENCE = 12;
+    private static final int MSG_SPLIT_FROM_CONFERENCE = 13;
+    private static final int MSG_SWAP_WITH_BACKGROUND_CALL = 14;
+    private static final int MSG_ON_POST_DIAL_CONTINUE = 15;
+    private static final int MSG_ON_PHONE_ACCOUNT_CLICKED = 16;
 
     private final Map<String, Connection> mConnectionById = new HashMap<>();
     private final Map<Connection, String> mIdByConnection = new HashMap<>();
@@ -74,11 +73,11 @@
     private final ConnectionServiceAdapter mAdapter = new ConnectionServiceAdapter();
 
     /**
-     * A callback for providing the resuilt of creating a connection.
+     * A callback for providing the result of creating a connection.
      */
-    public interface OutgoingCallResponse<CONNECTION> {
+    public interface CreateConnectionResponse<CONNECTION> {
         /**
-         * Tells Telecomm that an attempt to place the specified outgoing call succeeded.
+         * Tells Telecomm that an attempt to create the connection succeeded.
          *
          * @param request The original request.
          * @param connection The connection.
@@ -86,7 +85,8 @@
         void onSuccess(ConnectionRequest request, CONNECTION connection);
 
         /**
-         * Tells Telecomm that an attempt to place the specified outgoing call failed.
+         * Tells Telecomm that an attempt to create the connection failed. Telecomm will try a
+         * different service until a service cancels the process or completes it successfully.
          *
          * @param request The original request.
          * @param code An integer code indicating the reason for failure.
@@ -95,7 +95,8 @@
         void onFailure(ConnectionRequest request, int code, String msg);
 
         /**
-         * Tells Telecomm to cancel the call.
+         * Tells Telecomm to cancel creating the connection. Telecomm will stop trying to create
+         * the connection an no more services will be tried.
          *
          * @param request The original request.
          */
@@ -109,8 +110,9 @@
         }
 
         @Override
-        public void call(ConnectionRequest request) {
-            mHandler.obtainMessage(MSG_CALL, request).sendToTarget();
+        public void createConnection(ConnectionRequest request, boolean isIncoming) {
+            mHandler.obtainMessage(
+                    MSG_CREATE_CONNECTION, isIncoming ? 1 : 0, 0, request).sendToTarget();
         }
 
         @Override
@@ -119,11 +121,6 @@
         }
 
         @Override
-        public void createIncomingCall(ConnectionRequest request) {
-            mHandler.obtainMessage(MSG_CREATE_INCOMING_CALL, request).sendToTarget();
-        }
-
-        @Override
         public void answer(String callId) {
             mHandler.obtainMessage(MSG_ANSWER, callId).sendToTarget();
         }
@@ -206,15 +203,12 @@
                     mAdapter.addAdapter((IConnectionServiceAdapter) msg.obj);
                     onAdapterAttached();
                     break;
-                case MSG_CALL:
-                    call((ConnectionRequest) msg.obj);
+                case MSG_CREATE_CONNECTION:
+                    createConnection((ConnectionRequest) msg.obj, msg.arg1 == 1);
                     break;
                 case MSG_ABORT:
                     abort((String) msg.obj);
                     break;
-                case MSG_CREATE_INCOMING_CALL:
-                    createIncomingCall((ConnectionRequest) msg.obj);
-                    break;
                 case MSG_ANSWER:
                     answer((String) msg.obj);
                     break;
@@ -394,29 +388,39 @@
         return mBinder;
     }
 
-    private void call(final ConnectionRequest originalRequest) {
+    /**
+     * This can be used by telecomm to either create a new outgoing call or attach to an existing
+     * incoming call. In either case, telecomm will cycle through a set of services and call
+     * createConnection util a connection service cancels the process or completes it successfully.
+     */
+    private void createConnection(ConnectionRequest originalRequest, boolean isIncoming) {
         Log.d(this, "call %s", originalRequest);
-        onCreateConnections(
-                originalRequest,
-                new OutgoingCallResponse<Connection>() {
-                    @Override
-                    public void onSuccess(ConnectionRequest request, Connection connection) {
-                        Log.d(this, "adapter handleSuccessfulOutgoingCall %s", request.getCallId());
-                        mAdapter.handleSuccessfulOutgoingCall(request);
-                        addConnection(request.getCallId(), connection);
-                    }
+        CreateConnectionResponse response = new CreateConnectionResponse<Connection>() {
+            @Override
+            public void onSuccess(ConnectionRequest request, Connection connection) {
+                Log.d(this, "adapter handleCreateConnectionSuccessful %s",
+                        request.getCallId());
+                mAdapter.handleCreateConnectionSuccessful(request);
+                addConnection(request.getCallId(), connection);
+            }
 
-                    @Override
-                    public void onFailure(ConnectionRequest request, int code, String msg) {
-                        mAdapter.handleFailedOutgoingCall(request, code, msg);
-                    }
+            @Override
+            public void onFailure(ConnectionRequest request, int code, String msg) {
+                // Tell telecomm to try a different service.
+                mAdapter.handleCreateConnectionFailed(request, code, msg);
+            }
 
-                    @Override
-                    public void onCancel(ConnectionRequest request) {
-                        mAdapter.cancelOutgoingCall(request);
-                    }
-                }
-        );
+            @Override
+            public void onCancel(ConnectionRequest request) {
+                // Tell telecomm not to attempt any more services.
+                mAdapter.handleCreateConnectionCancelled(request);
+            }
+        };
+        if (isIncoming) {
+            onCreateIncomingConnection(originalRequest, response);
+        } else {
+            onCreateOutgoingConnection(originalRequest, response);
+        }
     }
 
     private void abort(String callId) {
@@ -424,33 +428,6 @@
         findConnectionForAction(callId, "abort").onAbort();
     }
 
-    private void createIncomingCall(ConnectionRequest originalRequest) {
-        Log.d(this, "createIncomingCall %s", originalRequest);
-        onCreateIncomingConnection(
-                originalRequest,
-                new Response<ConnectionRequest, Connection>() {
-                    @Override
-                    public void onResult(ConnectionRequest request, Connection... result) {
-                        if (result != null && result.length != 1) {
-                            for (Connection c : result) {
-                                c.onAbort();
-                            }
-                        } else {
-                            addConnection(request.getCallId(), result[0]);
-                            Log.d(this, "adapter notifyIncomingCall %s", request);
-                            mAdapter.notifyIncomingCall(request);
-                        }
-                    }
-
-                    @Override
-                    public void onError(ConnectionRequest request, int code, String msg) {
-                        Log.d(this, "adapter failed createIncomingCall %s %d %s",
-                                request, code, msg);
-                    }
-                }
-        );
-    }
-
     private void answer(String callId) {
         Log.d(this, "answer %s", callId);
         findConnectionForAction(callId, "answer").onAnswer();
@@ -570,7 +547,7 @@
                                     IConnectionService.Stub.asInterface(services.get(i)));
                         }
                         mAreAccountsInitialized = true;
-                        Log.d(this, "remote call services found: " + services);
+                        Log.d(this, "remote connection services found: " + services);
                         maybeRespondToAccountLookup();
                     }
                 });
@@ -606,10 +583,16 @@
         }
     }
 
+    public final void createRemoteIncomingConnection(
+            ConnectionRequest request,
+            CreateConnectionResponse<RemoteConnection> response) {
+        mRemoteConnectionManager.createRemoteConnection(request, response, true);
+    }
+
     public final void createRemoteOutgoingConnection(
             ConnectionRequest request,
-            OutgoingCallResponse<RemoteConnection> response) {
-        mRemoteConnectionManager.createOutgoingConnection(request, response);
+            CreateConnectionResponse<RemoteConnection> response) {
+        mRemoteConnectionManager.createRemoteConnection(request, response, false);
     }
 
     /**
@@ -620,14 +603,25 @@
     }
 
     /**
-     * Create a Connection given a request.
+     * Create a Connection given an incoming request. This is used to attach to existing incoming
+     * calls.
      *
-     * @param request Data encapsulating details of the desired Connection.
+     * @param request Details about the incoming call.
      * @param callback A callback for providing the result.
      */
-    protected void onCreateConnections(
+    protected void onCreateIncomingConnection(
             ConnectionRequest request,
-            OutgoingCallResponse<Connection> callback) {}
+            CreateConnectionResponse<Connection> callback) {}
+
+    /**
+     * Create a Connection given an outgoing request. This is used to initiate new outgoing calls.
+     *
+     * @param request Details about the outgoing call.
+     * @param callback A callback for providing the result.
+     */
+    protected void onCreateOutgoingConnection(
+            ConnectionRequest request,
+            CreateConnectionResponse<Connection> callback) {}
 
     /**
      * Returns a new or existing conference connection when the the user elects to convert the
@@ -645,21 +639,6 @@
             Response<String, Connection> callback) {}
 
     /**
-     * Create a Connection to match an incoming connection notification.
-     *
-     * IMPORTANT: If the incoming connection has a phone number (or other handle) that the user
-     * is not supposed to be able to see (e.g. it is PRESENTATION_RESTRICTED), then a compliant
-     * ConnectionService implementation MUST NOT reveal this phone number as part of the Intent
-     * it sends to notify Telecomm of an incoming connection.
-     *
-     * @param request Data encapsulating details of the desired Connection.
-     * @param callback A callback for providing the result.
-     */
-    protected void onCreateIncomingConnection(
-            ConnectionRequest request,
-            Response<ConnectionRequest, Connection> callback) {}
-
-    /**
      * Notifies that a connection has been added to this connection service and sent to Telecomm.
      *
      * @param connection The connection which was added.
diff --git a/telecomm/java/android/telecomm/ConnectionServiceAdapter.java b/telecomm/java/android/telecomm/ConnectionServiceAdapter.java
index b90dec3..a812fa4 100644
--- a/telecomm/java/android/telecomm/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecomm/ConnectionServiceAdapter.java
@@ -73,65 +73,28 @@
         }
     }
 
-    /**
-     * Provides Telecomm with the details of an incoming call. An invocation of this method must
-     * follow {@link ConnectionService#setIncomingCallId} and use the call ID specified therein.
-     * Upon the invocation of this method, Telecomm will bring up the incoming-call interface where
-     * the user can elect to answer or reject a call.
-     *
-     * @param request The connection request.
-     */
-    void notifyIncomingCall(ConnectionRequest request) {
+    void handleCreateConnectionSuccessful(ConnectionRequest request) {
         for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.notifyIncomingCall(request);
+                adapter.handleCreateConnectionSuccessful(request);
             } catch (RemoteException e) {
             }
         }
     }
 
-    /**
-     * Tells Telecomm that an attempt to place the specified outgoing call succeeded.
-     *
-     * @param request The originating request for a connection.
-     */
-    void handleSuccessfulOutgoingCall(ConnectionRequest request) {
+    void handleCreateConnectionFailed(ConnectionRequest request, int errorCode, String errorMsg) {
         for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.handleSuccessfulOutgoingCall(request);
+                adapter.handleCreateConnectionFailed(request, errorCode, errorMsg);
             } catch (RemoteException e) {
             }
         }
     }
 
-    /**
-     * Tells Telecomm that an attempt to place the specified outgoing call failed.
-     *
-     * @param request The originating request for a connection.
-     * @param errorCode The error code associated with the failed call attempt.
-     * @param errorMsg The error message associated with the failed call attempt.
-     */
-    void handleFailedOutgoingCall(
-            ConnectionRequest request,
-            int errorCode,
-            String errorMsg) {
+    void handleCreateConnectionCancelled(ConnectionRequest request) {
         for (IConnectionServiceAdapter adapter : mAdapters) {
             try {
-                adapter.handleFailedOutgoingCall(request, errorCode, errorMsg);
-            } catch (RemoteException e) {
-            }
-        }
-    }
-
-    /**
-     * Tells Telecomm to cancel the call.
-     *
-     * @param request The originating request for a connection.
-     */
-    void cancelOutgoingCall(ConnectionRequest request) {
-        for (IConnectionServiceAdapter adapter : mAdapters) {
-            try {
-                adapter.cancelOutgoingCall(request);
+                adapter.handleCreateConnectionCancelled(request);
             } catch (RemoteException e) {
             }
         }
diff --git a/telecomm/java/android/telecomm/InCallAdapter.java b/telecomm/java/android/telecomm/InCallAdapter.java
index d8293a5..66cf1df 100644
--- a/telecomm/java/android/telecomm/InCallAdapter.java
+++ b/telecomm/java/android/telecomm/InCallAdapter.java
@@ -24,7 +24,7 @@
  * Receives commands from {@link InCallService} implementations which should be executed by
  * Telecomm. When Telecomm binds to a {@link InCallService}, an instance of this class is given to
  * the in-call service through which it can manipulate live (active, dialing, ringing) calls. When
- * the in-call service is notified of new calls ({@link InCallService#addCall}), it can use the
+ * the in-call service is notified of new calls, it can use the
  * given call IDs to execute commands such as {@link #answerCall} for incoming calls or
  * {@link #disconnectCall} for active calls the user would like to end. Some commands are only
  * appropriate for calls in certain states; please consult each method for such limitations.
@@ -167,16 +167,15 @@
      * A post-dial DTMF string is a string of digits entered after a phone number, when dialed,
      * that are immediately sent as DTMF tones to the recipient as soon as the connection is made.
      * While these tones are playing, Telecomm will notify the {@link InCallService} that the call
-     * is in the {@link InCallService#setPostDial(String,String)} state.
+     * is in the post dial state.
      *
      * If the DTMF string contains a {@link TelecommConstants#DTMF_CHARACTER_PAUSE} symbol, Telecomm
      * will temporarily pause playing the tones for a pre-defined period of time.
      *
      * If the DTMF string contains a {@link TelecommConstants#DTMF_CHARACTER_WAIT} symbol, Telecomm
      * will pause playing the tones and notify the {@link InCallService} that the call is in the
-     * {@link InCallService#setPostDialWait(String,String)} state. When the user decides to continue
-     * the postdial sequence, the {@link InCallService} should invoke the
-     * {@link #postDialContinue(String,boolean)} method.
+     * post dial wait state. When the user decides to continue the postdial sequence, the
+     * {@link InCallService} should invoke the {@link #postDialContinue(String,boolean)} method.
      *
      * @param callId The unique ID of the call for which postdial string playing should continue.
      * @param proceed Whether or not to continue with the post-dial sequence.
diff --git a/telecomm/java/android/telecomm/InCallCall.java b/telecomm/java/android/telecomm/InCallCall.java
index 7c35020..355c260 100644
--- a/telecomm/java/android/telecomm/InCallCall.java
+++ b/telecomm/java/android/telecomm/InCallCall.java
@@ -45,7 +45,6 @@
     private final int mCallerDisplayNamePresentation;
     private final GatewayInfo mGatewayInfo;
     private final PhoneAccount mAccount;
-    private final CallServiceDescriptor mCurrentCallServiceDescriptor;
     private final ICallVideoProvider mCallVideoProvider;
     private RemoteCallVideoProvider mRemoteCallVideoProvider;
     private final String mParentCallId;
@@ -67,7 +66,6 @@
             int callerDisplayNamePresentation,
             GatewayInfo gatewayInfo,
             PhoneAccount account,
-            CallServiceDescriptor descriptor,
             ICallVideoProvider callVideoProvider,
             String parentCallId,
             List<String> childCallIds,
@@ -85,7 +83,6 @@
         mCallerDisplayNamePresentation = callerDisplayNamePresentation;
         mGatewayInfo = gatewayInfo;
         mAccount = account;
-        mCurrentCallServiceDescriptor = descriptor;
         mCallVideoProvider = callVideoProvider;
         mParentCallId = parentCallId;
         mChildCallIds = childCallIds;
@@ -165,11 +162,6 @@
         return mAccount;
     }
 
-    /** The descriptor for the call service currently routing this call. */
-    public CallServiceDescriptor getCurrentCallServiceDescriptor() {
-        return mCurrentCallServiceDescriptor;
-    }
-
     /**
      * Returns an object for remotely communicating through the call video provider's binder.
      * @return The call video provider.
@@ -232,7 +224,6 @@
             int callerDisplayNamePresentation = source.readInt();
             GatewayInfo gatewayInfo = source.readParcelable(classLoader);
             PhoneAccount account = source.readParcelable(classLoader);
-            CallServiceDescriptor descriptor = source.readParcelable(classLoader);
             ICallVideoProvider callVideoProvider =
                     ICallVideoProvider.Stub.asInterface(source.readStrongBinder());
             String parentCallId = source.readString();
@@ -242,8 +233,7 @@
             return new InCallCall(id, state, disconnectCauseCode, disconnectCauseMsg,
                     cannedSmsResponses, capabilities, connectTimeMillis, handle, handlePresentation,
                     callerDisplayName, callerDisplayNamePresentation, gatewayInfo,
-                    account, descriptor, callVideoProvider, parentCallId, childCallIds,
-                    statusHints);
+                    account, callVideoProvider, parentCallId, childCallIds, statusHints);
         }
 
         @Override
@@ -274,7 +264,6 @@
         destination.writeInt(mCallerDisplayNamePresentation);
         destination.writeParcelable(mGatewayInfo, 0);
         destination.writeParcelable(mAccount, 0);
-        destination.writeParcelable(mCurrentCallServiceDescriptor, 0);
         destination.writeStrongBinder(
                 mCallVideoProvider != null ? mCallVideoProvider.asBinder() : null);
         destination.writeString(mParentCallId);
diff --git a/telecomm/java/android/telecomm/InCallService.java b/telecomm/java/android/telecomm/InCallService.java
index 31291fb..028b6e4 100644
--- a/telecomm/java/android/telecomm/InCallService.java
+++ b/telecomm/java/android/telecomm/InCallService.java
@@ -16,8 +16,6 @@
 
 package android.telecomm;
 
-import android.app.Service;
-import android.content.Intent;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -31,11 +29,10 @@
  * This service is implemented by any app that wishes to provide the user-interface for managing
  * phone calls. Telecomm binds to this service while there exists a live (active or incoming)
  * call, and uses it to notify the in-call app of any live and and recently disconnected calls.
- * TODO(santoscordon): Needs more/better description of lifecycle once the interface is better
- * defined.
+ *
  * TODO(santoscordon): What happens if two or more apps on a given device implement this interface?
  */
-public abstract class InCallService extends Service {
+public abstract class InCallService {
     private static final int MSG_SET_IN_CALL_ADAPTER = 1;
     private static final int MSG_ADD_CALL = 2;
     private static final int MSG_UPDATE_CALL = 3;
@@ -50,21 +47,21 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_SET_IN_CALL_ADAPTER:
-                    mAdapter = new InCallAdapter((IInCallAdapter) msg.obj);
-                    onAdapterAttached(mAdapter);
+                    mPhone = new Phone(new InCallAdapter((IInCallAdapter) msg.obj));
+                    onPhoneCreated(mPhone);
                     break;
                 case MSG_ADD_CALL:
-                    addCall((InCallCall) msg.obj);
+                    mPhone.internalAddCall((InCallCall) msg.obj);
                     break;
                 case MSG_UPDATE_CALL:
-                    updateCall((InCallCall) msg.obj);
+                    mPhone.internalUpdateCall((InCallCall) msg.obj);
                     break;
-                case MSG_SET_POST_DIAL: {
+                                case MSG_SET_POST_DIAL: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
                         String callId = (String) args.arg1;
                         String remaining = (String) args.arg2;
-                        setPostDial(callId, remaining);
+                        mPhone.internalSetPostDial(callId, remaining);
                     } finally {
                         args.recycle();
                     }
@@ -75,17 +72,17 @@
                     try {
                         String callId = (String) args.arg1;
                         String remaining = (String) args.arg2;
-                        setPostDialWait(callId, remaining);
+                        mPhone.internalSetPostDialWait(callId, remaining);
                     } finally {
                         args.recycle();
                     }
                     break;
                 }
                 case MSG_ON_AUDIO_STATE_CHANGED:
-                    onAudioStateChanged((CallAudioState) msg.obj);
+                    mPhone.internalAudioStateChanged((CallAudioState) msg.obj);
                     break;
                 case MSG_BRING_TO_FOREGROUND:
-                    bringToForeground(msg.arg1 == 1);
+                    mPhone.internalBringToForeground(msg.arg1 == 1);
                     break;
                 default:
                     break;
@@ -142,85 +139,41 @@
         }
     }
 
-    private final InCallServiceBinder mBinder;
+    private Phone mPhone;
 
-    private InCallAdapter mAdapter;
+    protected InCallService() {}
 
-    protected InCallService() {
-        mBinder = new InCallServiceBinder();
-    }
-
-    @Override
-    public final IBinder onBind(Intent intent) {
-        return mBinder;
+    public final IBinder getBinder() {
+        return new InCallServiceBinder();
     }
 
     /**
-     * @return The attached {@link InCallAdapter} if attached, or null otherwise.
+     * Obtain the {@code Phone} associated with this {@code InCallService}.
+     *
+     * @return The {@code Phone} object associated with this {@code InCallService}, or {@code null}
+     * if the {@code InCallService} is not in a state where it has an associated {@code Phone}.
      */
-    protected final InCallAdapter getAdapter() {
-        return mAdapter;
+    public Phone getPhone() {
+        return mPhone;
     }
 
     /**
-     * Lifecycle callback which is called when this {@link InCallService} has been attached
-     * to a {@link InCallAdapter}, indicating {@link #getAdapter()} is now safe to use.
+     * Invoked when the {@code Phone} has been created. This is a signal to the in-call experience
+     * to start displaying in-call information to the user. Each instance of {@code InCallService}
+     * will have only one {@code Phone}, and this method will be called exactly once in the
+     * lifetime of the {@code InCallService}.
      *
-     * @param adapter The adapter now attached to this in-call service.
+     * @param phone The {@code Phone} object associated with this {@code InCallService}.
      */
-    protected void onAdapterAttached(InCallAdapter adapter) {
-    }
+    public void onPhoneCreated(Phone phone) { }
 
     /**
-     * Indicates to the in-call app that a new call has been created and an appropriate
-     * user-interface should be built and shown to notify the user.
+     * Invoked when a {@code Phone} has been destroyed. This is a signal to the in-call experience
+     * to stop displaying in-call information to the user. This method will be called exactly once
+     * in the lifetime of the {@code InCallService}, and it will always be called after a previous
+     * call to {@link #onPhoneCreated(Phone)}.
      *
-     * @param call Information about the new call.
+     * @param phone The {@code Phone} object associated with this {@code InCallService}.
      */
-     protected abstract void addCall(InCallCall call);
-
-    /**
-     * Call when information about a call has changed.
-     *
-     * @param call Information about the new call.
-     */
-     protected abstract void updateCall(InCallCall call);
-
-    /**
-     * Indicates to the in-call app that the specified call is active but in a "post-dial" state
-     * where Telecomm is now sending some dual-tone multi-frequency signaling (DTMF) tones appended
-     * to the dialed number. Normal transitions are to {@link #setPostDialWait(String,String)} when
-     * the post-dial string requires user confirmation to proceed, and {@link CallState#ACTIVE} when
-     * the post-dial tones are completed.
-     *
-     * @param callId The identifier of the call changing state.
-     * @param remaining The remaining postdial string to be dialed.
-     */
-    protected abstract void setPostDial(String callId, String remaining);
-
-    /**
-     * Indicates to the in-call app that the specified call was in the
-     * {@link #setPostDial(String,String)} state but is now waiting for user confirmation before the
-     * remaining digits can be sent. Normal transitions are to {@link #setPostDial(String,String)}
-     * when the user asks Telecomm to proceed with the post-dial sequence and the in-call app
-     * informs Telecomm of this by invoking {@link InCallAdapter#postDialContinue(String,boolean)}.
-     *
-     * @param callId The identifier of the call changing state.
-     * @param remaining The remaining postdial string to be dialed.
-     */
-    protected abstract void setPostDialWait(String callId, String remaining);
-
-    /**
-     * Called when the audio state changes.
-     *
-     * @param audioState The new {@link CallAudioState}.
-     */
-    protected abstract void onAudioStateChanged(CallAudioState audioState);
-
-    /**
-     * Brings the in-call screen to the foreground.
-     *
-     * @param showDialpad If true, put up the dialpad when the screen is shown.
-     */
-    protected abstract void bringToForeground(boolean showDialpad);
+    public void onPhoneDestroyed(Phone phone) { }
 }
diff --git a/telecomm/java/android/telecomm/Phone.java b/telecomm/java/android/telecomm/Phone.java
new file mode 100644
index 0000000..9c97b45
--- /dev/null
+++ b/telecomm/java/android/telecomm/Phone.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2013 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.telecomm;
+
+import android.util.ArrayMap;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * A unified virtual device providing a means of voice (and other) communication on a device.
+ */
+public final class Phone {
+
+    public abstract static class Listener {
+        /**
+         * Called when the audio state changes.
+         *
+         * @param phone The {@code Phone} calling this method.
+         * @param audioState The new {@link CallAudioState}.
+         */
+        public void onAudioStateChanged(Phone phone, CallAudioState audioState) { }
+
+        /**
+         * Called to bring the in-call screen to the foreground. The in-call experience should
+         * respond immediately by coming to the foreground to inform the user of the state of
+         * ongoing {@code Call}s.
+         *
+         * @param phone The {@code Phone} calling this method.
+         * @param showDialpad If true, put up the dialpad when the screen is shown.
+         */
+        public void onBringToForeground(Phone phone, boolean showDialpad) { }
+
+        /**
+         * Called when a {@code Call} has been added to this in-call session. The in-call user
+         * experience should add necessary state listeners to the specified {@code Call} and
+         * immediately start to show the user information about the existence
+         * and nature of this {@code Call}. Subsequent invocations of {@link #getCalls()} will
+         * include this {@code Call}.
+         *
+         * @param phone The {@code Phone} calling this method.
+         * @param call A newly added {@code Call}.
+         */
+        public void onCallAdded(Phone phone, Call call) { }
+
+        /**
+         * Called when a {@code Call} has been removed from this in-call session. The in-call user
+         * experience should remove any state listeners from the specified {@code Call} and
+         * immediately stop displaying any information about this {@code Call}.
+         * Subsequent invocations of {@link #getCalls()} will no longer include this {@code Call}.
+         *
+         * @param phone The {@code Phone} calling this method.
+         * @param call A newly removed {@code Call}.
+         */
+        public void onCallRemoved(Phone phone, Call call) { }
+    }
+
+    // A Map allows us to track each Call by its Telecomm-specified call ID
+    private final Map<String, Call> mCallByTelecommCallId = new ArrayMap<>();
+
+    // A List allows us to keep the Calls in a stable iteration order so that casually developed
+    // user interface components do not incur any spurious jank
+    private final List<Call> mCalls = new ArrayList<>();
+
+    // An unmodifiable view of the above List can be safely shared with subclass implementations
+    private final List<Call> mUnmodifiableCalls = Collections.unmodifiableList(mCalls);
+
+    private final InCallAdapter mInCallAdapter;
+
+    private CallAudioState mAudioState;
+
+    private final List<Listener> mListeners = new ArrayList<>();
+
+    /** {@hide} */
+    Phone(InCallAdapter adapter) {
+        mInCallAdapter = adapter;
+    }
+
+    /** {@hide} */
+    final void internalAddCall(InCallCall inCallCall) {
+        Call call = new Call(this, inCallCall.getId(), mInCallAdapter);
+        mCallByTelecommCallId.put(inCallCall.getId(), call);
+        mCalls.add(call);
+        checkCallTree(inCallCall);
+        call.internalUpdate(inCallCall);
+        fireCallAdded(call);
+     }
+
+    /** {@hide} */
+    final void internalRemoveCall(Call call) {
+        mCallByTelecommCallId.remove(call.internalGetCallId());
+        mCalls.remove(call);
+        fireCallRemoved(call);
+    }
+
+    /** {@hide} */
+    final void internalUpdateCall(InCallCall inCallCall) {
+         Call call = mCallByTelecommCallId.get(inCallCall.getId());
+         if (call != null) {
+             checkCallTree(inCallCall);
+             call.internalUpdate(inCallCall);
+         }
+     }
+
+    /** {@hide} */
+    final void internalSetPostDial(String callId, String remaining) {
+        Call call = mCallByTelecommCallId.get(callId);
+        if (call != null) {
+            call.internalSetPostDial(remaining);
+        }
+    }
+
+    /** {@hide} */
+    final void internalSetPostDialWait(String callId, String remaining) {
+        Call call = mCallByTelecommCallId.get(callId);
+        if (call != null) {
+            call.internalSetPostDialWait(remaining);
+        }
+    }
+
+    /** {@hide} */
+    final void internalAudioStateChanged(CallAudioState callAudioState) {
+        if (!Objects.equals(mAudioState, callAudioState)) {
+            mAudioState = callAudioState;
+            fireAudioStateChanged(callAudioState);
+        }
+    }
+
+    /** {@hide} */
+    final Call internalGetCallByTelecommId(String telecommId) {
+        return mCallByTelecommCallId.get(telecommId);
+    }
+
+    /** {@hide} */
+    final void internalBringToForeground(boolean showDialpad) {
+        fireBringToForeground(showDialpad);
+    }
+
+    /**
+     * Adds a listener to this {@code Phone}.
+     *
+     * @param listener A {@code Listener} object.
+     */
+    public final void addListener(Listener listener) {
+        mListeners.add(listener);
+    }
+
+    /**
+     * Removes a listener from this {@code Phone}.
+     *
+     * @param listener A {@code Listener} object.
+     */
+    public final void removeListener(Listener listener) {
+        mListeners.remove(listener);
+    }
+
+    /**
+     * Obtains the current list of {@code Call}s to be displayed by this in-call experience.
+     *
+     * @return A list of the relevant {@code Call}s.
+     */
+    public final List<Call> getCalls() {
+        return mUnmodifiableCalls;
+    }
+
+    /**
+     * Sets the microphone mute state. When this request is honored, there will be change to
+     * the {@link #getAudioState()}.
+     *
+     * @param state {@code true} if the microphone should be muted; {@code false} otherwise.
+     */
+    public final void setMuted(boolean state) {
+        mInCallAdapter.mute(state);
+    }
+
+    /**
+     * Sets the audio route (speaker, bluetooth, etc...).  When this request is honored, there will
+     * be change to the {@link #getAudioState()}.
+     *
+     * @param route The audio route to use.
+     */
+    public final void setAudioRoute(int route) {
+        mInCallAdapter.setAudioRoute(route);
+    }
+
+    /**
+     * Obtains the current phone call audio state of the {@code Phone}.
+     *
+     * @return An object encapsulating the audio state.
+     */
+    public final CallAudioState getAudioState() {
+        return mAudioState;
+    }
+
+    private void fireCallAdded(Call call) {
+        Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+        for (int i = 0; i < listeners.length; i++) {
+            listeners[i].onCallAdded(this, call);
+        }
+    }
+
+    private void fireCallRemoved(Call call) {
+        Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+        for (int i = 0; i < listeners.length; i++) {
+            listeners[i].onCallRemoved(this, call);
+        }
+    }
+
+    private void fireAudioStateChanged(CallAudioState audioState) {
+        Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+        for (int i = 0; i < listeners.length; i++) {
+            listeners[i].onAudioStateChanged(this, audioState);
+        }
+    }
+
+    private void fireBringToForeground(boolean showDialpad) {
+        Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+        for (int i = 0; i < listeners.length; i++) {
+            listeners[i].onBringToForeground(this, showDialpad);
+        }
+    }
+
+    private void checkCallTree(InCallCall inCallCall) {
+        if (inCallCall.getParentCallId() != null &&
+                !mCallByTelecommCallId.containsKey(inCallCall.getParentCallId())) {
+            Log.wtf(this, "InCallCall %s has nonexistent parent %s",
+                    inCallCall.getId(), inCallCall.getParentCallId());
+        }
+        if (inCallCall.getChildCallIds() != null) {
+            for (int i = 0; i < inCallCall.getChildCallIds().size(); i++) {
+                if (!mCallByTelecommCallId.containsKey(inCallCall.getChildCallIds().get(i))) {
+                    Log.wtf(this, "InCallCall %s has nonexistent child %s",
+                            inCallCall.getId(), inCallCall.getChildCallIds().get(i));
+                }
+            }
+        }
+    }
+}
diff --git a/telecomm/java/android/telecomm/PhoneAccount.java b/telecomm/java/android/telecomm/PhoneAccount.java
index 4e440d8..c1eec83 100644
--- a/telecomm/java/android/telecomm/PhoneAccount.java
+++ b/telecomm/java/android/telecomm/PhoneAccount.java
@@ -17,52 +17,64 @@
 package android.telecomm;
 
 import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.telephony.Rlog;
-import android.util.DisplayMetrics;
-import android.util.Log;
 
-import java.util.MissingResourceException;
 import java.util.Objects;
 
 /**
  * Represents a distinct account, line of service or call placement method that
  * the system can use to place phone calls.
  */
-public final class PhoneAccount implements Parcelable {
+public class PhoneAccount implements Parcelable {
 
-    private static final int NO_DENSITY = -1;
 
-    private static final String LOG_TAG = "Account";
+    /**
+     * Flag indicating that this {@code PhoneAccount} can act as a call manager for traditional
+     * SIM-based telephony calls. The {@link ConnectionService} associated with this phone-account
+     * will be allowed to manage SIM-based phone calls including using its own proprietary
+     * phone-call implementation (like VoIP calling) to make calls instead of the telephony stack.
+     * When a user opts to place a call using the SIM-based telephony stack, the connection-service
+     * associated with this phone-account will be attempted first if the user has explicitly
+     * selected it to be used as the default call-manager.
+     * <p>
+     * See {@link #getCapabilities}
+     */
+    public static final int CAPABILITY_SIM_CALL_MANAGER = 0x1;
 
-    private final ComponentName mComponentName;
-    private final String mId;
-    private final Uri mHandle;
-    private final String mLabel;
-    private final String mShortDescription;
-    private final boolean mIsEnabled;
-    private final boolean mIsSystemDefault;
+    /**
+     * Flag indicating that this {@code PhoneAccount} can make phone calls in place of traditional
+     * SIM-based telephony calls. This account will be treated as a distinct method for placing
+     * calls alongside the traditional SIM-based telephony stack. This flag is distinct from
+     * {@link #CAPABILITY_SIM_CALL_MANAGER} in that it is not allowed to manage calls from or use
+     * the built-in telephony stack to place its calls.
+     * <p>
+     * See {@link #getCapabilities}
+     */
+    public static final int CAPABILITY_CALL_PROVIDER = 0x2;
+
+    /**
+     * Flag indicating that this {@code PhoneAccount} represents a built-in PSTN SIM subscription.
+     * <p>
+     * Only the android framework can set this capability on a phone-account.
+     */
+    public static final int CAPABILITY_SIM_SUBSCRIPTION = 0x4;
+
+    private ComponentName mComponentName;
+    private String mId;
+    private Uri mHandle;
+    private int mCapabilities;
 
     public PhoneAccount(
             ComponentName componentName,
             String id,
             Uri handle,
-            String label,
-            String shortDescription,
-            boolean isEnabled,
-            boolean isSystemDefault) {
+            int capabilities) {
         mComponentName = componentName;
         mId = id;
         mHandle = handle;
-        mLabel = label;
-        mShortDescription = shortDescription;
-        mIsSystemDefault = isSystemDefault;
-        mIsEnabled = isEnabled;
+        mCapabilities = capabilities;
     }
 
     /**
@@ -87,8 +99,8 @@
 
     /**
      * The handle (e.g., a phone number) associated with this {@code PhoneAccount}. This represents
-     * the destination from which outgoing calls using this {@code PhoneAccount} will appear to come
-     * from, if applicable, and the destination to which incoming calls using this
+     * the destination from which outgoing calls using this {@code PhoneAccount} will appear to
+     * come, if applicable, and the destination to which incoming calls using this
      * {@code PhoneAccount} may be addressed.
      *
      * @return A handle expressed as a {@code Uri}, for example, a phone number.
@@ -98,76 +110,23 @@
     }
 
     /**
-     * A short string label describing this {@code PhoneAccount}.
+     * The capabilities of this {@code PhoneAccount}.
      *
-     * @param context The invoking {@code Context}, used for retrieving resources.
-     *
-     * TODO(ihab): If don't need context, remove param
-     *
-     * @return A label for this {@code PhoneAccount}.
+     * @return A bit field of flags describing this {@code PhoneAccount}'s capabilities.
      */
-    public String getLabel(Context context) {
-        return mLabel;
+    public int getCapabilities() {
+        return mCapabilities;
     }
 
-    /**
-     * A short paragraph describing this {@code PhoneAccount}.
-     *
-     * @param context The invoking {@code Context}, used for retrieving resources.
-     *
-     * TODO(ihab): If don't need context, remove param
-     *
-     * @return A description for this {@code PhoneAccount}.
-     */
-    public String getShortDescription(Context context) {
-        return mShortDescription;
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(mComponentName) + Objects.hashCode(mId) +
+                Objects.hashCode(mHandle) + mCapabilities;
     }
 
-    // TODO(ihab): Representation of the icons
     //
-    // Refactor to pass a Bitmap (scale it at runtime), but if they don't pass one, fall
-    // back to the android:icon attr in the manifest (<service /> first, <application /> second)
-
-    /**
-     * An icon to represent this {@code PhoneAccount} in a user interface.
-     *
-     * @param context The invoking {@code Context}, used for retrieving resources.
-     *
-     * @return An icon for this {@code PhoneAccount}.
-     */
-    public Drawable getIcon(Context context) {
-        return null;  // TODO(ihab): See above
-    }
-
-    /**
-     * An icon to represent this {@code PhoneAccount} in a user interface.
-     *
-     * @param context The invoking {@code Context}, used for retrieving resources.
-     * @param density A display density from {@link DisplayMetrics}.
-     *
-     * @return An icon for this {@code PhoneAccount}.
-     */
-    public Drawable getIcon(Context context, int density) {
-        return null;  // TODO(ihab): See above
-    }
-
-    /**
-     * Whether this {@code PhoneAccount} is enabled for use.
-     *
-     * @return {@code true} if this {@code PhoneAccount} is enabled.
-     */
-    public boolean isEnabled() {
-        return mIsEnabled;
-    }
-
-    /**
-     * Whether this {@code PhoneAccount} is the system default.
-     *
-     * @return {@code true} if this {@code PhoneAccount} is the system default.
-     */
-    public boolean isSystemDefault() {
-        return mIsSystemDefault;
-    }
+    // Parcelable implementation.
+    //
 
     @Override
     public int describeContents() {
@@ -179,18 +138,16 @@
         out.writeParcelable(mComponentName, flags);
         out.writeString(mId);
         out.writeString(mHandle != null ? mHandle.toString() : "");
-        out.writeString(mLabel);
-        out.writeString(mShortDescription);
-        out.writeInt(mIsEnabled ? 1 : 0);
-        out.writeInt(mIsSystemDefault ? 1 : 0);
+        out.writeInt(mCapabilities);
     }
 
-    public static final Creator<PhoneAccount> CREATOR
-            = new Creator<PhoneAccount>() {
+    public static final Creator<PhoneAccount> CREATOR = new Creator<PhoneAccount>() {
+        @Override
         public PhoneAccount createFromParcel(Parcel in) {
             return new PhoneAccount(in);
         }
 
+        @Override
         public PhoneAccount[] newArray(int size) {
             return new PhoneAccount[size];
         }
@@ -201,22 +158,6 @@
         mId = in.readString();
         String uriString = in.readString();
         mHandle = uriString.length() > 0 ? Uri.parse(uriString) : null;
-        mLabel = in.readString();
-        mShortDescription = in.readString();
-        mIsEnabled = in.readInt() == 1;
-        mIsSystemDefault = in.readInt() == 1;
-    }
-
-    @Override
-    public boolean equals(Object other) {
-        return
-                other instanceof PhoneAccount &&
-                Objects.equals(mComponentName, ((PhoneAccount) other).mComponentName) &&
-                Objects.equals(mId, ((PhoneAccount) other).mId);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hashCode(mComponentName) + Objects.hashCode(mId);
+        mCapabilities = in.readInt();
     }
 }
diff --git a/telecomm/java/android/telecomm/CallServiceDescriptor.aidl b/telecomm/java/android/telecomm/PhoneAccountMetadata.aidl
similarity index 77%
rename from telecomm/java/android/telecomm/CallServiceDescriptor.aidl
rename to telecomm/java/android/telecomm/PhoneAccountMetadata.aidl
index f517c73..55b8900 100644
--- a/telecomm/java/android/telecomm/CallServiceDescriptor.aidl
+++ b/telecomm/java/android/telecomm/PhoneAccountMetadata.aidl
@@ -1,11 +1,11 @@
 /*
- * Copyright 2014, The Android Open Source Project
+ * Copyright (C) 2014 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      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,
@@ -16,4 +16,7 @@
 
 package android.telecomm;
 
-parcelable CallServiceDescriptor;
+/**
+ * {@hide}
+  */
+parcelable PhoneAccountMetadata;
diff --git a/telecomm/java/android/telecomm/PhoneAccountMetadata.java b/telecomm/java/android/telecomm/PhoneAccountMetadata.java
new file mode 100644
index 0000000..20a4d47
--- /dev/null
+++ b/telecomm/java/android/telecomm/PhoneAccountMetadata.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecomm;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.io.IOException;
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.util.MissingResourceException;
+
+/**
+ * Provides user interface description information for a {@code PhoneAccount}.
+ */
+public class PhoneAccountMetadata implements Parcelable {
+    private PhoneAccount mAccount;
+    private int mIconResId;
+    private String mLabel;
+    private String mShortDescription;
+
+    public PhoneAccountMetadata(
+            PhoneAccount account,
+            int iconResId,
+            String label,
+            String shortDescription) {
+        mAccount = account;
+        mIconResId = iconResId;
+        mLabel = label;
+        mShortDescription = shortDescription;
+    }
+
+    /**
+     * The {@code PhoneAccount} to which this metadata pertains.
+     *
+     * @return A {@code PhoneAccount}.
+     */
+    public PhoneAccount getAccount() {
+        return mAccount;
+    }
+
+    /**
+     * A short string label describing a {@code PhoneAccount}.
+     *
+     * @return A label for this {@code PhoneAccount}.
+     */
+    public String getLabel() {
+        return mLabel;
+    }
+
+    /**
+     * A short paragraph describing a {@code PhoneAccount}.
+     *
+     * @return A description for this {@code PhoneAccount}.
+     */
+    public String getShortDescription() {
+        return mShortDescription;
+    }
+
+    /**
+     * An icon to represent this {@code PhoneAccount} in a user interface.
+     *
+     * @return An icon for this {@code PhoneAccount}.
+     */
+    public Drawable getIcon(Context context) {
+        return getIcon(context, mIconResId);
+    }
+
+    private Drawable getIcon(Context context, int resId) {
+        Context packageContext;
+        try {
+            packageContext = context.createPackageContext(
+                    mAccount.getComponentName().getPackageName(), 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.w(this, "Cannot find package %s", mAccount.getComponentName().getPackageName());
+            return null;
+        }
+        try {
+            return packageContext.getResources().getDrawable(resId);
+        } catch (MissingResourceException e) {
+            Log.e(this, e, "Cannot find icon %d in package %s",
+                    resId, mAccount.getComponentName().getPackageName());
+            return null;
+        }
+    }
+
+    //
+    // Parcelable implementation
+    //
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeParcelable(mAccount, 0);
+        out.writeInt(mIconResId);
+        out.writeString(mLabel);
+        out.writeString(mShortDescription);
+    }
+
+    public static final Creator<PhoneAccountMetadata> CREATOR
+            = new Creator<PhoneAccountMetadata>() {
+        @Override
+        public PhoneAccountMetadata createFromParcel(Parcel in) {
+            return new PhoneAccountMetadata(in);
+        }
+
+        @Override
+        public PhoneAccountMetadata[] newArray(int size) {
+            return new PhoneAccountMetadata[size];
+        }
+    };
+
+    private PhoneAccountMetadata(Parcel in) {
+        mAccount = in.readParcelable(getClass().getClassLoader());
+        mIconResId = in.readInt();
+        mLabel = in.readString();
+        mShortDescription = in.readString();
+    }
+}
diff --git a/telecomm/java/android/telecomm/RemoteCallVideoProvider.java b/telecomm/java/android/telecomm/RemoteCallVideoProvider.java
index 856d321..a49076a 100644
--- a/telecomm/java/android/telecomm/RemoteCallVideoProvider.java
+++ b/telecomm/java/android/telecomm/RemoteCallVideoProvider.java
@@ -20,63 +20,92 @@
 import android.os.RemoteException;
 import android.view.Surface;
 
-import com.android.internal.telecomm.ICallVideoClient;
 import com.android.internal.telecomm.ICallVideoProvider;
 
-public class RemoteCallVideoProvider implements IBinder.DeathRecipient {
+public class RemoteCallVideoProvider {
     private final ICallVideoProvider mCallVideoProvider;
 
+    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
+        @Override
+        public void binderDied() {
+            mCallVideoProvider.asBinder().unlinkToDeath(this, 0);
+        }
+    };
+
+    /** {@hide} */
     RemoteCallVideoProvider(ICallVideoProvider callVideoProvider) throws RemoteException {
         mCallVideoProvider = callVideoProvider;
-        mCallVideoProvider.asBinder().linkToDeath(this, 0);
+        mCallVideoProvider.asBinder().linkToDeath(mDeathRecipient, 0);
     }
 
-    @Override
-    public void binderDied() {
-        mCallVideoProvider.asBinder().unlinkToDeath(this, 0);
-    }
-
-    public void setCallVideoClient(CallVideoClient callVideoClient) throws RemoteException {
-        mCallVideoProvider.setCallVideoClient(callVideoClient.getBinder());
+    public void setCallVideoClient(CallVideoClient callVideoClient) {
+        try {
+            mCallVideoProvider.setCallVideoClient(callVideoClient.getBinder());
+        } catch (RemoteException e) {
+        }
     }
 
     public void setCamera(String cameraId) throws RemoteException {
         mCallVideoProvider.setCamera(cameraId);
     }
 
-    public void setPreviewSurface(Surface surface) throws RemoteException {
-        mCallVideoProvider.setPreviewSurface(surface);
+    public void setPreviewSurface(Surface surface) {
+        try {
+            mCallVideoProvider.setPreviewSurface(surface);
+        } catch (RemoteException e) {
+        }
     }
 
-    public void setDisplaySurface(Surface surface) throws RemoteException {
-        mCallVideoProvider.setDisplaySurface(surface);
+    public void setDisplaySurface(Surface surface) {
+        try {
+            mCallVideoProvider.setDisplaySurface(surface);
+        } catch (RemoteException e) {
+        }
     }
 
-    public void setDeviceOrientation(int rotation) throws RemoteException {
-        mCallVideoProvider.setDeviceOrientation(rotation);
+    public void setDeviceOrientation(int rotation) {
+        try {
+            mCallVideoProvider.setDeviceOrientation(rotation);
+        } catch (RemoteException e) {
+        }
     }
 
     public void setZoom(float value) throws RemoteException {
         mCallVideoProvider.setZoom(value);
     }
 
-    public void sendSessionModifyRequest(VideoCallProfile requestProfile) throws RemoteException {
-        mCallVideoProvider.sendSessionModifyRequest(requestProfile);
+    public void sendSessionModifyRequest(VideoCallProfile requestProfile) {
+        try {
+            mCallVideoProvider.sendSessionModifyRequest(requestProfile);
+        } catch (RemoteException e) {
+        }
     }
 
-    public void sendSessionModifyResponse(VideoCallProfile responseProfile) throws RemoteException {
-        mCallVideoProvider.sendSessionModifyResponse(responseProfile);
+    public void sendSessionModifyResponse(VideoCallProfile responseProfile) {
+        try {
+            mCallVideoProvider.sendSessionModifyResponse(responseProfile);
+        } catch (RemoteException e) {
+        }
     }
 
-    public void requestCameraCapabilities() throws RemoteException {
-        mCallVideoProvider.requestCameraCapabilities();
+    public void requestCameraCapabilities() {
+        try {
+            mCallVideoProvider.requestCameraCapabilities();
+        } catch (RemoteException e) {
+        }
     }
 
-    public void requestCallDataUsage() throws RemoteException {
-        mCallVideoProvider.requestCallDataUsage();
+    public void requestCallDataUsage() {
+        try {
+            mCallVideoProvider.requestCallDataUsage();
+        } catch (RemoteException e) {
+        }
     }
 
-    public void setPauseImage(String uri) throws RemoteException {
-        mCallVideoProvider.setPauseImage(uri);
+    public void setPauseImage(String uri) {
+        try {
+            mCallVideoProvider.setPauseImage(uri);
+        } catch (RemoteException e) {
+        }
     }
 }
\ No newline at end of file
diff --git a/telecomm/java/android/telecomm/RemoteConnectionManager.java b/telecomm/java/android/telecomm/RemoteConnectionManager.java
index 9cffdcc..0a0b245 100644
--- a/telecomm/java/android/telecomm/RemoteConnectionManager.java
+++ b/telecomm/java/android/telecomm/RemoteConnectionManager.java
@@ -54,9 +54,10 @@
         return accounts;
     }
 
-    public void createOutgoingConnection(
+    public void createRemoteConnection(
             ConnectionRequest request,
-            final ConnectionService.OutgoingCallResponse response) {
+            ConnectionService.CreateConnectionResponse response,
+            boolean isIncoming) {
         PhoneAccount account = request.getAccount();
         if (account == null) {
             throw new IllegalArgumentException("account must be specified.");
@@ -67,7 +68,7 @@
             throw new UnsupportedOperationException("account not supported: " + componentName);
         } else {
             RemoteConnectionService remoteService = mRemoteConnectionServices.get(componentName);
-            remoteService.createOutgoingConnection(request, response);
+            remoteService.createRemoteConnection(request, response, isIncoming);
         }
     }
 }
diff --git a/telecomm/java/android/telecomm/RemoteConnectionService.java b/telecomm/java/android/telecomm/RemoteConnectionService.java
index a436af2..7fd8f93 100644
--- a/telecomm/java/android/telecomm/RemoteConnectionService.java
+++ b/telecomm/java/android/telecomm/RemoteConnectionService.java
@@ -44,40 +44,34 @@
 
     private String mConnectionId;
     private ConnectionRequest mPendingRequest;
-    private ConnectionService.OutgoingCallResponse<RemoteConnection> mPendingOutgoingCallResponse;
+    private ConnectionService.CreateConnectionResponse<RemoteConnection> mPendingResponse;
     // Remote connection services only support a single connection.
     private RemoteConnection mConnection;
 
     private final IConnectionServiceAdapter mAdapter = new IConnectionServiceAdapter.Stub() {
-
         @Override
-        public void notifyIncomingCall(ConnectionRequest request) {
-            Log.w(this, "notifyIncomingCall not implemented in Remote connection");
-        }
-
-        @Override
-        public void handleSuccessfulOutgoingCall(ConnectionRequest request) {
+        public void handleCreateConnectionSuccessful(ConnectionRequest request) {
             if (isPendingConnection(request.getCallId())) {
                 mConnection = new RemoteConnection(mConnectionService, request.getCallId());
-                mPendingOutgoingCallResponse.onSuccess(request, mConnection);
+                mPendingResponse.onSuccess(request, mConnection);
                 clearPendingInformation();
             }
         }
 
         @Override
-        public void handleFailedOutgoingCall(
+        public void handleCreateConnectionFailed(
                 ConnectionRequest request, int errorCode, String errorMessage) {
             if (isPendingConnection(request.getCallId())) {
-                mPendingOutgoingCallResponse.onFailure(request, errorCode, errorMessage);
+                mPendingResponse.onFailure(request, errorCode, errorMessage);
                 mConnectionId = null;
                 clearPendingInformation();
             }
         }
 
         @Override
-        public void cancelOutgoingCall(ConnectionRequest request) {
+        public void handleCreateConnectionCancelled(ConnectionRequest request) {
             if (isPendingConnection(request.getCallId())) {
-                mPendingOutgoingCallResponse.onCancel(request);
+                mPendingResponse.onCancel(request);
                 mConnectionId = null;
                 clearPendingInformation();
             }
@@ -226,12 +220,10 @@
         release();
     }
 
-    /**
-     * Places an outgoing call.
-     */
-    final void createOutgoingConnection(
+    final void createRemoteConnection(
             ConnectionRequest request,
-            ConnectionService.OutgoingCallResponse<RemoteConnection> response) {
+            ConnectionService.CreateConnectionResponse<RemoteConnection> response,
+            boolean isIncoming) {
 
         if (mConnectionId == null) {
             String id = UUID.randomUUID().toString();
@@ -243,9 +235,9 @@
                     request.getExtras(),
                     request.getVideoState());
             try {
-                mConnectionService.call(newRequest);
+                mConnectionService.createConnection(newRequest, isIncoming);
                 mConnectionId = id;
-                mPendingOutgoingCallResponse = response;
+                mPendingResponse = response;
                 mPendingRequest = request;
             } catch (RemoteException e) {
                 response.onFailure(request, DisconnectCause.ERROR_UNSPECIFIED, e.toString());
@@ -255,9 +247,6 @@
         }
     }
 
-    // TODO(santoscordon): Handle incoming connections
-    // public final void handleIncomingConnection() {}
-
     final List<PhoneAccount> lookupAccounts(Uri handle) {
         // TODO(santoscordon): Update this so that is actually calls into the RemoteConnection
         // each time.
@@ -266,10 +255,7 @@
                 mComponentName,
                 null /* id */,
                 null /* handle */,
-                "" /* label */,
-                "" /* shortDescription */,
-                true /* isEnabled */,
-                false /* isSystemDefault */));
+                0 /* capabilities */));
         return accounts;
     }
 
@@ -282,7 +268,7 @@
     }
 
     private boolean isPendingConnection(String id) {
-        return TextUtils.equals(mConnectionId, id) && mPendingOutgoingCallResponse != null;
+        return TextUtils.equals(mConnectionId, id) && mPendingResponse != null;
     }
 
     private boolean isCurrentConnection(String id) {
@@ -291,7 +277,7 @@
 
     private void clearPendingInformation() {
         mPendingRequest = null;
-        mPendingOutgoingCallResponse = null;
+        mPendingResponse = null;
     }
 
     private void destroyConnection() {
diff --git a/telecomm/java/android/telecomm/TelecommConstants.java b/telecomm/java/android/telecomm/TelecommConstants.java
index b9fb40c..a94841f 100644
--- a/telecomm/java/android/telecomm/TelecommConstants.java
+++ b/telecomm/java/android/telecomm/TelecommConstants.java
@@ -16,6 +16,7 @@
 
 package android.telecomm;
 
+import android.content.ComponentName;
 import android.content.Intent;
 import android.os.Bundle;
 import android.telephony.TelephonyManager;
@@ -31,9 +32,9 @@
      * to find and bind to the appropriate {@link android.telecomm.ConnectionService} which
      * Telecomm will ultimately use to control and get information about the call.</p>
      *
-     * <p>Input: get*Extra field {@link #EXTRA_CALL_SERVICE_DESCRIPTOR} contains the component name
-     * of the {@link android.telecomm.ConnectionService} that Telecomm should bind to. Telecomm
-     * will then ask the call service for more information about the call prior to showing any UI.
+     * <p>Input: get*Extra field {@link #EXTRA_PHONE_ACCOUNT} contains the component name of the
+     * {@link android.telecomm.ConnectionService} that Telecomm should bind to. Telecomm will then
+     * ask the connection service for more information about the call prior to showing any UI.
      *
      * TODO(santoscordon): Needs permissions.
      * TODO(santoscordon): Consider moving this into a simple method call on a system service.
@@ -41,16 +42,17 @@
     public static final String ACTION_INCOMING_CALL = "android.intent.action.INCOMING_CALL";
 
     /**
-     * The service action used to bind to {@link CallServiceProvider} implementations.
-     */
-    public static final String ACTION_CALL_SERVICE_PROVIDER = CallServiceProvider.class.getName();
-
-    /**
      * The service action used to bind to {@link ConnectionService} implementations.
      */
     public static final String ACTION_CONNECTION_SERVICE = ConnectionService.class.getName();
 
     /**
+     * The {@link Intent} action used to configure a {@link ConnectionService}.
+     */
+    public static final String ACTION_CONNECTION_SERVICE_CONFIGURE =
+            "android.intent.action.CONNECTION_SERVICE_CONFIGURE";
+
+    /**
      * Optional extra for {@link Intent#ACTION_CALL} containing a boolean that determines whether
      * the speakerphone should be automatically turned on for an outgoing call.
      */
@@ -69,11 +71,15 @@
             "android.intent.extra.START_CALL_WITH_VIDEO_STATE";
 
     /**
-     * Extra for {@link #ACTION_INCOMING_CALL} containing the {@link CallServiceDescriptor} that
-     * describes the call service to use for the incoming call.
+     * The extra used with an {@link android.content.Intent#ACTION_CALL},
+     * {@link #ACTION_INCOMING_CALL}, {@link android.content.Intent#ACTION_DIAL} {@code Intent} to
+     * specify a {@link PhoneAccount} to use when making the call.
+     *
+     * <p class="note">
+     * Retrieve with
+     * {@link android.content.Intent#getParcelableExtra(String)}.
      */
-    public static final String EXTRA_CALL_SERVICE_DESCRIPTOR =
-            "android.intent.extra.CALL_SERVICE_DESCRIPTOR";
+    public static final String EXTRA_PHONE_ACCOUNT = "android.intent.extra.PHONE_ACCOUNT";
 
     /**
      * Optional extra for {@link #ACTION_INCOMING_CALL} containing a {@link Bundle} which contains
diff --git a/telecomm/java/android/telecomm/TelecommManager.java b/telecomm/java/android/telecomm/TelecommManager.java
index 1bb18f2..fcd2eba 100644
--- a/telecomm/java/android/telecomm/TelecommManager.java
+++ b/telecomm/java/android/telecomm/TelecommManager.java
@@ -23,31 +23,30 @@
 
 import com.android.internal.telecomm.ITelecommService;
 
+import java.util.List;
+
 /**
  * Provides access to Telecomm-related functionality.
  * TODO(santoscordon): Move this all into PhoneManager.
  * @hide
  */
 public class TelecommManager {
+
+    /**
+     * The extra used with an {@link android.content.Intent#ACTION_CALL} or
+     * {@link android.content.Intent#ACTION_DIAL} {@code Intent} to specify a {@link PhoneAccount}
+     * to use when making the call.
+     *
+     * <p class="note">
+     * Retrieve with
+     * {@link android.content.Intent#getParcelableExtra(String)}.
+     */
+    public static final String EXTRA_PHONE_ACCOUNT = "account";
+
     private static final String TAG = "TelecommManager";
     private static final String TELECOMM_SERVICE_NAME = "telecomm";
 
     private final Context mContext;
-    private final ITelecommService mService;
-
-    /**
-     * @hide
-     */
-    public TelecommManager(Context context, ITelecommService service) {
-        Context appContext = context.getApplicationContext();
-        if (appContext != null) {
-            mContext = appContext;
-        } else {
-            mContext = context;
-        }
-
-        mService = service;
-    }
 
     /**
      * @hide
@@ -59,6 +58,103 @@
     /**
      * @hide
      */
+    public TelecommManager(Context context) {
+        Context appContext = context.getApplicationContext();
+        if (appContext != null) {
+            mContext = appContext;
+        } else {
+            mContext = context;
+        }
+    }
+
+    /**
+     * Return a list of {@link PhoneAccount}s which can be used to make and receive phone calls.
+     *
+     * @see #EXTRA_PHONE_ACCOUNT
+     * @return A list of {@code PhoneAccount} objects.
+     */
+    public List<PhoneAccount> getEnabledPhoneAccounts() {
+        try {
+            if (isServiceConnected()) {
+                return getTelecommService().getEnabledPhoneAccounts();
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelecommService#getEnabledPhoneAccounts", e);
+        }
+        return null;
+    }
+
+    /**
+     * Return the metadata for a specified {@link PhoneAccount}. Metadata includes resources which
+     * can be used in a user interface.
+     *
+     * @param account The {@link PhoneAccount}.
+     *
+     * @return The metadata for the account.
+     */
+    public PhoneAccountMetadata getPhoneAccountMetadata(PhoneAccount account) {
+        try {
+            if (isServiceConnected()) {
+                return getTelecommService().getPhoneAccountMetadata(account);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelecommService#getPhoneAccountMetadata", e);
+        }
+        return null;
+    }
+
+    /**
+     * Register a {@link PhoneAccount} for use by the system.
+     *
+     * @param account The {@link PhoneAccount}.
+     * @param metadata The metadata for the account.
+     */
+    public void registerPhoneAccount(PhoneAccount account, PhoneAccountMetadata metadata) {
+        try {
+            if (isServiceConnected()) {
+                getTelecommService().registerPhoneAccount(account, metadata);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelecommService#registerPhoneAccount", e);
+        }
+    }
+
+    /**
+     * Remove a {@link PhoneAccount} registration from the system.
+     *
+     * @param account An Account.
+     */
+    public void unregisterPhoneAccount(PhoneAccount account) {
+        try {
+            if (isServiceConnected()) {
+                getTelecommService().unregisterPhoneAccount(account);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelecommService#unregisterPhoneAccount", e);
+        }
+    }
+
+    /**
+     * Remove all Accounts for a given package from the system.
+     *
+     * @param packageName A package name that may have registered Accounts.
+     *
+     * @hide
+     */
+    @SystemApi
+    public void clearAccounts(String packageName) {
+        try {
+            if (isServiceConnected()) {
+                getTelecommService().clearAccounts(packageName);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelecommService#clearAccounts", e);
+        }
+    }
+
+    /**
+     * @hide
+     */
     @SystemApi
     public ComponentName getDefaultPhoneApp() {
         try {
@@ -108,7 +204,7 @@
 
     /**
      * Ends an ongoing call. TODO(santoscordon): L-release - need to convert all invocations of
-     * ITelephony#endCall to use this method (clockwork & gearhead).
+     * ITelecommService#endCall to use this method (clockwork & gearhead).
      *
      * @hide
      */
@@ -127,7 +223,7 @@
     /**
      * If there is a ringing incoming call, this method accepts the call on behalf of the user.
      * TODO(santoscordon): L-release - need to convert all invocation of
-     * ITelephony#answerRingingCall to use this method (clockwork & gearhead).
+     * ITelecommService#answerRingingCall to use this method (clockwork & gearhead).
      *
      * @hide
      */
diff --git a/telecomm/java/com/android/internal/telecomm/ICallServiceLookupResponse.aidl b/telecomm/java/com/android/internal/telecomm/ICallServiceLookupResponse.aidl
deleted file mode 100644
index 10d73be..0000000
--- a/telecomm/java/com/android/internal/telecomm/ICallServiceLookupResponse.aidl
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telecomm;
-
-import android.os.IBinder;
-import android.telecomm.CallServiceDescriptor;
-import java.util.List;
-
-/**
- * Internal remote interface for call service lookup response.
- *
- * @see android.telecomm.CallServiceLookupResponse
- *
- * @hide
- */
-oneway interface ICallServiceLookupResponse {
-    void setCallServiceDescriptors(in List<CallServiceDescriptor> callServiceDescriptors);
-}
diff --git a/telecomm/java/com/android/internal/telecomm/ICallServiceProvider.aidl b/telecomm/java/com/android/internal/telecomm/ICallServiceProvider.aidl
deleted file mode 100644
index 96daeed..0000000
--- a/telecomm/java/com/android/internal/telecomm/ICallServiceProvider.aidl
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telecomm;
-
-import android.telecomm.CallServiceDescriptor;
-
-import com.android.internal.telecomm.ICallServiceLookupResponse;
-
-/**
- * Internal remote interface for call service providers.
- *
- * @see android.telecomm.CallServiceProvider
- *
- * @hide
- */
-oneway interface ICallServiceProvider {
-    void lookupCallServices(in ICallServiceLookupResponse response);
-}
diff --git a/telecomm/java/com/android/internal/telecomm/IConnectionService.aidl b/telecomm/java/com/android/internal/telecomm/IConnectionService.aidl
index 16d2edf..9360219 100644
--- a/telecomm/java/com/android/internal/telecomm/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/IConnectionService.aidl
@@ -32,12 +32,10 @@
 oneway interface IConnectionService {
     void addConnectionServiceAdapter(in IConnectionServiceAdapter adapter);
 
-    void call(in ConnectionRequest request);
+    void createConnection(in ConnectionRequest request, boolean isIncoming);
 
     void abort(String callId);
 
-    void createIncomingCall(in ConnectionRequest request);
-
     void answer(String callId);
 
     void reject(String callId);
diff --git a/telecomm/java/com/android/internal/telecomm/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecomm/IConnectionServiceAdapter.aidl
index bc67eab..b36f72c 100644
--- a/telecomm/java/com/android/internal/telecomm/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecomm/IConnectionServiceAdapter.aidl
@@ -31,13 +31,12 @@
  * {@hide}
  */
 oneway interface IConnectionServiceAdapter {
-    void notifyIncomingCall(in ConnectionRequest request);
+    void handleCreateConnectionSuccessful(in ConnectionRequest request);
 
-    void handleSuccessfulOutgoingCall(in ConnectionRequest request);
+    void handleCreateConnectionFailed(
+            in ConnectionRequest request, int errorCode, String errorMessage);
 
-    void handleFailedOutgoingCall(in ConnectionRequest request, int errorCode, String errorMessage);
-
-    void cancelOutgoingCall(in ConnectionRequest request);
+    void handleCreateConnectionCancelled(in ConnectionRequest request);
 
     void setActive(String callId);
 
diff --git a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
index 30e4bdc..3334385 100644
--- a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
@@ -18,6 +18,7 @@
 
 import android.content.ComponentName;
 import android.telecomm.PhoneAccount;
+import android.telecomm.PhoneAccountMetadata;
 
 /**
  * Interface used to interact with Telecomm. Mostly this is used by TelephonyManager for passing
@@ -33,22 +34,32 @@
     void showCallScreen(boolean showDialpad);
 
     /**
-     * Gets a list of accounts.
+     * @see TelecommManager#getEnabledPhoneAccounts
      */
-    List<PhoneAccount> getAccounts();
+    List<PhoneAccount> getEnabledPhoneAccounts();
 
     /**
-     * Sets the enabled state of a given account.
+     * @see TelecommManager#getPhoneAccountMetadata
      */
-    void setEnabled(in PhoneAccount account, boolean enabled);
+    PhoneAccountMetadata getPhoneAccountMetadata(in PhoneAccount account);
 
     /**
-     * Sets a given account as the system default.
+     * @see TelecommManager#registerPhoneAccount
      */
-    void setSystemDefault(in PhoneAccount account);
+    void registerPhoneAccount(in PhoneAccount account, in PhoneAccountMetadata metadata);
 
     /**
-     * Returns the component name of the default phone application.
+     * @see TelecommManager#unregisterPhoneAccount
+     */
+    void unregisterPhoneAccount(in PhoneAccount account);
+
+    /**
+     * @see TelecommManager#clearAccounts
+     */
+    void clearAccounts(String packageName);
+
+    /**
+     * @see TelecommManager#getDefaultPhoneApp
      */
     ComponentName getDefaultPhoneApp();
 
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 91ce73a..c1eb843 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -298,17 +298,6 @@
     public static final String EXTRA_INCOMING_NUMBER = "incoming_number";
 
     /**
-     * The lookup key used with an {@link android.content.Intent#ACTION_CALL} or
-     * {@link android.content.Intent#ACTION_DIAL} {@code Intent} for a {@link PhoneAccount}
-     * object indicating a preference when making a phone connection.
-     *
-     * <p class="note">
-     * Retrieve with
-     * {@link android.content.Intent#getParcelableExtra(String)}.
-     */
-    public static final String EXTRA_ACCOUNT = "account";
-
-    /**
      * Broadcast intent action indicating that a precise call state
      * (cellular) on the device has changed.
      *
@@ -3207,42 +3196,6 @@
     }
 
     /**
-     * Return a list of Accounts that can be used to indicate a preference when making
-     * a phone call.
-     *
-     * @see #EXTRA_ACCOUNT
-     * @return A list of {@code Accouint} objects.
-     */
-    public List<PhoneAccount> getAccounts() {
-        try {
-            return getTelecommService().getAccounts();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#getAccounts", e);
-        }
-        return null;
-    }
-
-    /** @hide */
-    @SystemApi
-    public void setEnabled(PhoneAccount account, boolean enabled) {
-        try {
-            getTelecommService().setEnabled(account, enabled);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#setEnabled", e);
-        }
-    }
-
-    /** @hide */
-    @SystemApi
-    public void setSystemDefault(PhoneAccount account) {
-        try {
-            getTelecommService().setSystemDefault(account);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#setSystemDefault", e);
-        }
-    }
-
-    /**
      * Set whether Android should display a simplified Mobile Network Settings UI.
      * The setting won't be persisted during power cycle.
      * <p>
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 4592717..d41ceda 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -650,7 +650,7 @@
      *
      * TODO: Add a link to documentation.
      *
-     * @return carrier privelege status defined in TelephonyManager.
+     * @return carrier privilege status defined in TelephonyManager.
      */
     int hasCarrierPrivileges();
 
diff --git a/telephony/java/com/android/internal/telephony/IThirdPartyCallListener.aidl b/telephony/java/com/android/internal/telephony/IThirdPartyCallListener.aidl
deleted file mode 100644
index bcf2d81..0000000
--- a/telephony/java/com/android/internal/telephony/IThirdPartyCallListener.aidl
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony;
-
-import com.android.internal.telephony.IThirdPartyCallProvider;
-
-/**
- * Interface provided to ThirdPartyCallService. The service can use this to notify the listener of
- * changes to the call state.
- */
-oneway interface IThirdPartyCallListener {
-    /**
-     * Called by the service when a call provider is available to perform the outgoing or incoming
-     * call.
-     */
-    void onCallProviderAttached(IThirdPartyCallProvider callProvider);
-
-    /**
-     * Notifies the listener that ringing has started for this call.
-     */
-    void onRingingStarted();
-
-    /**
-     * Notifies the listener that the call has been successfully established.
-     */
-    void onCallEstablished();
-
-    /**
-     * Notifies the listener that the call has ended.
-     */
-    void onCallEnded(int reason);
-}
diff --git a/telephony/java/com/android/internal/telephony/IThirdPartyCallProvider.aidl b/telephony/java/com/android/internal/telephony/IThirdPartyCallProvider.aidl
deleted file mode 100644
index 9d595b0..0000000
--- a/telephony/java/com/android/internal/telephony/IThirdPartyCallProvider.aidl
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony;
-
-import com.android.internal.telephony.IThirdPartyCallListener;
-import com.android.internal.telephony.IThirdPartyCallSendDtmfCallback;
-
-/**
- * Interface sent to ThirdPartyCallListener.onCallProviderAttached. This is used to control an
- * outgoing or incoming call.
- */
-oneway interface IThirdPartyCallProvider {
-    /**
-     * Mutes or unmutes the call.
-     */
-    void mute(boolean shouldMute);
-
-    /**
-     * Ends the current call. If this is an unanswered incoming call then the call is rejected (for
-     * example, a notification is sent to a server that the user declined the call).
-     */
-    void hangup();
-
-    /**
-     * Accepts the incoming call.
-     */
-    void incomingCallAccept();
-
-    /**
-     * Sends the given DTMF code. The code can be '0'-'9', 'A'-'D', '#', or '*'.
-     */
-    void sendDtmf(char c, IThirdPartyCallSendDtmfCallback callback);
-}
diff --git a/telephony/java/com/android/internal/telephony/IThirdPartyCallService.aidl b/telephony/java/com/android/internal/telephony/IThirdPartyCallService.aidl
deleted file mode 100644
index 597567a..0000000
--- a/telephony/java/com/android/internal/telephony/IThirdPartyCallService.aidl
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony;
-
-import com.android.internal.telephony.IThirdPartyCallListener;
-
-/**
- * Interface provided by a service to start outgoing calls and attach to incoming calls.
- */
-oneway interface IThirdPartyCallService {
-    /**
-     * Call to start a new outgoing call.
-     */
-    void outgoingCallInitiate(IThirdPartyCallListener listener, String number);
-
-    /**
-     * Call to attach to an incoming call. This is in response to a call to
-     * TelephonyManager.newIncomingThirdPartyCall.
-     */
-    void incomingCallAttach(IThirdPartyCallListener listener, String callId);
-}
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index a54936b..2ebce9b 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -175,6 +175,11 @@
     }
 
     @Override
+    public File getNoBackupFilesDir() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public File getExternalFilesDir(String type) {
         throw new UnsupportedOperationException();
     }
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index a14714a..e388480 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -31,6 +31,7 @@
 import android.content.pm.IPackageMoveObserver;
 import android.content.pm.IPackageStatsObserver;
 import android.content.pm.InstrumentationInfo;
+import android.content.pm.KeySet;
 import android.content.pm.ManifestDigest;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
@@ -616,6 +617,26 @@
         throw new UnsupportedOperationException();
     }
 
+    @Override
+    public KeySet getKeySetByAlias(String packageName, String alias) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public KeySet getSigningKeySet(String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isSignedBy(String packageName, KeySet ks) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isSignedByExactly(String packageName, KeySet ks) {
+        throw new UnsupportedOperationException();
+    }
+
     /**
      * @hide
      */
@@ -729,7 +750,13 @@
     }
 
     /** {@hide} */
-    public PackageInstaller getPackageInstaller() {
+    public PackageInstaller getInstaller() {
+        throw new UnsupportedOperationException();
+    }
+
+    /** {@hide} */
+    @Override
+    public boolean isPackageAvailable(String packageName) {
         throw new UnsupportedOperationException();
     }
 
diff --git a/tests/OneMedia/Android.mk b/tests/OneMedia/Android.mk
index 93b9c9a..4feac68 100644
--- a/tests/OneMedia/Android.mk
+++ b/tests/OneMedia/Android.mk
@@ -10,8 +10,7 @@
 LOCAL_CERTIFICATE := platform
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-        android-support-v7-appcompat \
-        android-support-v7-mediarouter
+    android-support-media-protocols
 
 LOCAL_PROGUARD_ENABLED := disabled
 
diff --git a/tests/OneMedia/AndroidManifest.xml b/tests/OneMedia/AndroidManifest.xml
index 504d471..9d78ca5 100644
--- a/tests/OneMedia/AndroidManifest.xml
+++ b/tests/OneMedia/AndroidManifest.xml
@@ -27,11 +27,11 @@
             android:process="com.android.onemedia.service" />
         <service
             android:name=".provider.OneMediaRouteProvider"
-            android:permission="android.permission.BIND_ROUTE_PROVIDER"
+            android:permission="android.permission.BIND_MEDIA_ROUTE_SERVICE"
             android:exported="true"
             android:process="com.android.onemedia.provider">
             <intent-filter>
-                <action android:name="com.android.media.session.MediaRouteProvider" />
+                <action android:name="android.media.routing.MediaRouteService" />
             </intent-filter>
           </service>
     </application>
diff --git a/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java b/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java
index ee407ad..894377b 100644
--- a/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java
+++ b/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java
@@ -28,8 +28,6 @@
 import android.widget.EditText;
 import android.widget.TextView;
 
-import com.android.onemedia.playback.Renderer;
-
 public class OnePlayerActivity extends Activity {
     private static final String TAG = "OnePlayerActivity";
 
diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerController.java b/tests/OneMedia/src/com/android/onemedia/PlayerController.java
index 145b389..9cbb455 100644
--- a/tests/OneMedia/src/com/android/onemedia/PlayerController.java
+++ b/tests/OneMedia/src/com/android/onemedia/PlayerController.java
@@ -18,13 +18,14 @@
 
 import android.media.MediaMetadata;
 import android.media.session.MediaController;
-import android.media.session.RouteInfo;
+import android.media.session.MediaSession;
 import android.media.session.MediaSessionManager;
 import android.media.session.PlaybackState;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.app.Activity;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -44,7 +45,7 @@
     protected MediaController.TransportControls mTransportControls;
 
     private final Intent mServiceIntent;
-    private Context mContext;
+    private Activity mContext;
     private Listener mListener;
     private SessionCallback mControllerCb;
     private MediaSessionManager mManager;
@@ -52,7 +53,7 @@
 
     private boolean mResumed;
 
-    public PlayerController(Context context, Intent serviceIntent) {
+    public PlayerController(Activity context, Intent serviceIntent) {
         mContext = context;
         if (serviceIntent == null) {
             mServiceIntent = new Intent(mContext, PlayerService.class);
@@ -121,7 +122,7 @@
     }
 
     public void showRoutePicker() {
-        mController.showRoutePicker();
+        // TODO
     }
 
     private void unbindFromService() {
@@ -141,6 +142,7 @@
             mBinder = null;
             mController = null;
             mTransportControls = null;
+            mContext.setMediaController(null);
             Log.d(TAG, "Disconnected from PlayerService");
 
             if (mListener != null) {
@@ -152,12 +154,15 @@
         public void onServiceConnected(ComponentName name, IBinder service) {
             mBinder = IPlayerService.Stub.asInterface(service);
             Log.d(TAG, "service is " + service + " binder is " + mBinder);
+            MediaSession.Token token;
             try {
-                mController = MediaController.fromToken(mBinder.getSessionToken());
+                token = mBinder.getSessionToken();
             } catch (RemoteException e) {
                 Log.e(TAG, "Error getting session", e);
                 return;
             }
+            mController = MediaController.fromToken(token);
+            mContext.setMediaController(mController);
             mController.addCallback(mControllerCb, mHandler);
             mTransportControls = mController.getTransportControls();
             Log.d(TAG, "Ready to use PlayerService");
@@ -173,11 +178,6 @@
 
     private class SessionCallback extends MediaController.Callback {
         @Override
-        public void onRouteChanged(RouteInfo route) {
-            // TODO
-        }
-
-        @Override
         public void onPlaybackStateChanged(PlaybackState state) {
             if (state == null) {
                 return;
diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
index a220107..78353b2 100644
--- a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
+++ b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
@@ -17,14 +17,18 @@
 
 import android.content.Context;
 import android.content.Intent;
-import android.media.session.Route;
-import android.media.session.RouteInfo;
-import android.media.session.RouteOptions;
-import android.media.session.RoutePlaybackControls;
+import android.media.routing.MediaRouteSelector;
+import android.media.routing.MediaRouter;
+import android.media.routing.MediaRouter.ConnectionRequest;
+import android.media.routing.MediaRouter.DestinationInfo;
+import android.media.routing.MediaRouter.RouteInfo;
 import android.media.session.MediaSession;
 import android.media.session.MediaSessionManager;
 import android.media.session.PlaybackState;
 import android.os.Bundle;
+import android.support.media.protocols.MediaPlayerProtocol;
+import android.support.media.protocols.MediaPlayerProtocol.MediaStatus;
+import android.os.SystemClock;
 import android.util.Log;
 import android.view.KeyEvent;
 
@@ -34,11 +38,13 @@
 import com.android.onemedia.playback.RequestUtils;
 
 import java.util.ArrayList;
+import java.util.List;
 
 public class PlayerSession {
     private static final String TAG = "PlayerSession";
 
     protected MediaSession mSession;
+    protected MediaRouter mRouter;
     protected Context mContext;
     protected Renderer mRenderer;
     protected MediaSession.Callback mCallback;
@@ -46,10 +52,6 @@
 
     protected PlaybackState mPlaybackState;
     protected Listener mListener;
-    protected ArrayList<RouteOptions> mRouteOptions;
-    protected Route mRoute;
-    protected RoutePlaybackControls mRouteControls;
-    protected RouteListener mRouteListener;
 
     private String mContent;
 
@@ -58,46 +60,58 @@
         mRenderer = new LocalRenderer(context, null);
         mCallback = new SessionCb();
         mRenderListener = new RenderListener();
-        mPlaybackState = new PlaybackState();
-        mPlaybackState.setActions(PlaybackState.ACTION_PAUSE
-                | PlaybackState.ACTION_PLAY);
+        PlaybackState.Builder psBob = new PlaybackState.Builder();
+        psBob.setActions(PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_PLAY);
+        mPlaybackState = psBob.build();
 
         mRenderer.registerListener(mRenderListener);
-
-        // TODO need an easier way to build route options
-        mRouteOptions = new ArrayList<RouteOptions>();
-        RouteOptions.Builder bob = new RouteOptions.Builder();
-        bob.addInterface(RoutePlaybackControls.NAME);
-        mRouteOptions.add(bob.build());
-        mRouteListener = new RouteListener();
     }
 
     public void createSession() {
-        if (mSession != null) {
-            mSession.release();
-        }
+        releaseSession();
+
         MediaSessionManager man = (MediaSessionManager) mContext
                 .getSystemService(Context.MEDIA_SESSION_SERVICE);
         Log.d(TAG, "Creating session for package " + mContext.getBasePackageName());
+
+        mRouter = new MediaRouter(mContext);
+        mRouter.addSelector(new MediaRouteSelector.Builder()
+                .addRequiredProtocol(MediaPlayerProtocol.class)
+                .build());
+        mRouter.addSelector(new MediaRouteSelector.Builder()
+                .setRequiredFeatures(MediaRouter.ROUTE_FEATURE_LIVE_AUDIO)
+                .setOptionalFeatures(MediaRouter.ROUTE_FEATURE_LIVE_VIDEO)
+                .build());
+        mRouter.setRoutingCallback(new RoutingCallback(), null);
+
         mSession = man.createSession("OneMedia");
         mSession.addCallback(mCallback);
         mSession.addTransportControlsCallback(new TransportCallback());
         mSession.setPlaybackState(mPlaybackState);
         mSession.setFlags(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
-        mSession.setRouteOptions(mRouteOptions);
+        mSession.setMediaRouter(mRouter);
         mSession.setActive(true);
     }
 
     public void onDestroy() {
-        if (mSession != null) {
-            mSession.release();
-        }
+        releaseSession();
         if (mRenderer != null) {
             mRenderer.unregisterListener(mRenderListener);
             mRenderer.onDestroy();
         }
     }
 
+    private void releaseSession() {
+        if (mSession != null) {
+            mSession.release();
+            mSession = null;
+        }
+        if (mRouter != null) {
+            mRouter.release();
+            mRouter = null;
+        }
+    }
+
     public void setListener(Listener listener) {
         mListener = listener;
     }
@@ -118,7 +132,10 @@
     private void updateState(int newState) {
         float rate = newState == PlaybackState.STATE_PLAYING ? 1 : 0;
         long position = mRenderer == null ? -1 : mRenderer.getSeekPosition();
-        mPlaybackState.setState(newState, position, rate);
+        PlaybackState.Builder bob = new PlaybackState.Builder(mPlaybackState);
+        bob.setState(newState, position, rate, SystemClock.elapsedRealtime());
+        bob.setErrorMessage(null);
+        mPlaybackState = bob.build();
         mSession.setPlaybackState(mPlaybackState);
     }
 
@@ -131,10 +148,12 @@
         @Override
         public void onError(int type, int extra, Bundle extras, Throwable error) {
             Log.d(TAG, "Sending onError with type " + type + " and extra " + extra);
-            mPlaybackState.setState(PlaybackState.STATE_ERROR, -1, 0);
+            PlaybackState.Builder bob = new PlaybackState.Builder(mPlaybackState);
+            bob.setState(PlaybackState.STATE_ERROR, -1, 0, 0);
             if (error != null) {
-                mPlaybackState.setErrorMessage(error.getLocalizedMessage());
+                bob.setErrorMessage(error.getLocalizedMessage());
             }
+            mPlaybackState = bob.build();
             mSession.setPlaybackState(mPlaybackState);
             if (mListener != null) {
                 mListener.onPlayStateChanged(mPlaybackState);
@@ -143,36 +162,41 @@
 
         @Override
         public void onStateChanged(int newState) {
-            if (newState != Renderer.STATE_ERROR) {
-                mPlaybackState.setErrorMessage(null);
-            }
             long position = -1;
             if (mRenderer != null) {
                 position = mRenderer.getSeekPosition();
             }
+            int pbState;
+            float rate = 0;
+            String errorMsg = null;
             switch (newState) {
                 case Renderer.STATE_ENDED:
                 case Renderer.STATE_STOPPED:
-                    mPlaybackState.setState(PlaybackState.STATE_STOPPED, position, 0);
+                    pbState = PlaybackState.STATE_STOPPED;
                     break;
                 case Renderer.STATE_INIT:
                 case Renderer.STATE_PREPARING:
-                    mPlaybackState.setState(PlaybackState.STATE_BUFFERING, position, 0);
+                    pbState = PlaybackState.STATE_BUFFERING;
                     break;
                 case Renderer.STATE_ERROR:
-                    mPlaybackState.setState(PlaybackState.STATE_ERROR, position, 0);
+                    pbState = PlaybackState.STATE_ERROR;
                     break;
                 case Renderer.STATE_PAUSED:
-                    mPlaybackState.setState(PlaybackState.STATE_PAUSED, position, 0);
+                    pbState = PlaybackState.STATE_PAUSED;
                     break;
                 case Renderer.STATE_PLAYING:
-                    mPlaybackState.setState(PlaybackState.STATE_PLAYING, position, 1);
+                    pbState = PlaybackState.STATE_PLAYING;
+                    rate = 1;
                     break;
                 default:
-                    mPlaybackState.setState(PlaybackState.STATE_ERROR, position, 0);
-                    mPlaybackState.setErrorMessage("unkown state");
+                    pbState = PlaybackState.STATE_ERROR;
+                    errorMsg = "unknown state";
                     break;
             }
+            PlaybackState.Builder bob = new PlaybackState.Builder(mPlaybackState);
+            bob.setState(pbState, position, rate, SystemClock.elapsedRealtime());
+            bob.setErrorMessage(errorMsg);
+            mPlaybackState = bob.build();
             mSession.setPlaybackState(mPlaybackState);
             if (mListener != null) {
                 mListener.onPlayStateChanged(mPlaybackState);
@@ -187,7 +211,10 @@
         public void onFocusLost() {
             Log.d(TAG, "Focus lost, changing state to " + Renderer.STATE_PAUSED);
             long position = mRenderer == null ? -1 : mRenderer.getSeekPosition();
-            mPlaybackState.setState(PlaybackState.STATE_PAUSED, position, 0);
+            PlaybackState.Builder bob = new PlaybackState.Builder(mPlaybackState);
+            bob.setState(PlaybackState.STATE_PAUSED, position, 0, SystemClock.elapsedRealtime());
+            bob.setErrorMessage(null);
+            mPlaybackState = bob.build();
             mSession.setPlaybackState(mPlaybackState);
             if (mListener != null) {
                 mListener.onPlayStateChanged(mPlaybackState);
@@ -218,40 +245,6 @@
                 }
             }
         }
-
-        @Override
-        public void onRequestRouteChange(RouteInfo route) {
-            if (mRenderer != null) {
-                mRenderer.onStop();
-            }
-            if (route == null) {
-                // Use local route
-                mRoute = null;
-                mRenderer = new LocalRenderer(mContext, null);
-                mRenderer.registerListener(mRenderListener);
-                updateState(PlaybackState.STATE_NONE);
-            } else {
-                // Use remote route
-                mSession.connect(route, mRouteOptions.get(0));
-                mRenderer = null;
-                updateState(PlaybackState.STATE_CONNECTING);
-            }
-        }
-
-        @Override
-        public void onRouteConnected(Route route) {
-            mRoute = route;
-            mRouteControls = RoutePlaybackControls.from(route);
-            mRouteControls.addListener(mRouteListener);
-            Log.d(TAG, "Connected to route, registering listener");
-            mRenderer = new OneMRPRenderer(mRouteControls);
-            updateState(PlaybackState.STATE_NONE);
-        }
-
-        @Override
-        public void onRouteDisconnected(Route route, int reason) {
-
-        }
     }
 
     private class TransportCallback extends MediaSession.TransportControlsCallback {
@@ -266,12 +259,62 @@
         }
     }
 
-    private class RouteListener extends RoutePlaybackControls.Listener {
+    private class RoutingCallback extends MediaRouter.RoutingCallback {
         @Override
-        public void onPlaybackStateChange(int state) {
-            Log.d(TAG, "Updating state to " + state);
-            updateState(state);
+        public void onConnectionStateChanged(int state) {
+            if (state == MediaRouter.CONNECTION_STATE_CONNECTING) {
+                if (mRenderer != null) {
+                    mRenderer.onStop();
+                }
+                mRenderer = null;
+                updateState(PlaybackState.STATE_CONNECTING);
+                return;
+            }
+
+            MediaRouter.ConnectionInfo connection = mRouter.getConnection();
+            if (connection != null) {
+                MediaPlayerProtocol protocol =
+                        connection.getProtocolObject(MediaPlayerProtocol.class);
+                if (protocol != null) {
+                    Log.d(TAG, "Connected to route using media player protocol");
+
+                    protocol.setCallback(new PlayerCallback(), null);
+                    mRenderer = new OneMRPRenderer(protocol);
+                    updateState(PlaybackState.STATE_NONE);
+                    return;
+                }
+            }
+
+            // Use local route
+            mRenderer = new LocalRenderer(mContext, null);
+            mRenderer.registerListener(mRenderListener);
+            updateState(PlaybackState.STATE_NONE);
         }
     }
 
+    private class PlayerCallback extends MediaPlayerProtocol.Callback {
+        @Override
+        public void onStatusUpdated(MediaStatus status, Bundle extras) {
+            if (status != null) {
+                Log.d(TAG, "Received status update: " + status.toBundle());
+                switch (status.getPlayerState()) {
+                    case MediaStatus.PLAYER_STATE_BUFFERING:
+                        updateState(PlaybackState.STATE_BUFFERING);
+                        break;
+                    case MediaStatus.PLAYER_STATE_IDLE:
+                        updateState(PlaybackState.STATE_STOPPED);
+                        break;
+                    case MediaStatus.PLAYER_STATE_PAUSED:
+                        updateState(PlaybackState.STATE_PAUSED);
+                        break;
+                    case MediaStatus.PLAYER_STATE_PLAYING:
+                        updateState(PlaybackState.STATE_PLAYING);
+                        break;
+                    case MediaStatus.PLAYER_STATE_UNKNOWN:
+                        updateState(PlaybackState.STATE_NONE);
+                        break;
+                }
+            } 
+        }
+    }
 }
diff --git a/tests/OneMedia/src/com/android/onemedia/playback/MediaItem.java b/tests/OneMedia/src/com/android/onemedia/playback/MediaItem.java
index 05516d2..c133325 100644
--- a/tests/OneMedia/src/com/android/onemedia/playback/MediaItem.java
+++ b/tests/OneMedia/src/com/android/onemedia/playback/MediaItem.java
@@ -15,10 +15,10 @@
  */
 package com.android.onemedia.playback;
 
+import android.media.MediaMetadata;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.support.v7.media.MediaItemMetadata;
 
 /**
  * TODO: Insert description here. (generated by epastern)
@@ -35,11 +35,11 @@
     }
 
     public String getTitle() {
-        return mBundle.getString(MediaItemMetadata.KEY_TITLE);
+        return mBundle.getString(MediaMetadata.METADATA_KEY_TITLE);
     }
 
     public String getArtist() {
-        return mBundle.getString(MediaItemMetadata.KEY_ALBUM_ARTIST);
+        return mBundle.getString(MediaMetadata.METADATA_KEY_ALBUM_ARTIST);
     }
 
     /* (non-Javadoc)
diff --git a/tests/OneMedia/src/com/android/onemedia/playback/OneMRPRenderer.java b/tests/OneMedia/src/com/android/onemedia/playback/OneMRPRenderer.java
index 9b0a2b2..55eb92c 100644
--- a/tests/OneMedia/src/com/android/onemedia/playback/OneMRPRenderer.java
+++ b/tests/OneMedia/src/com/android/onemedia/playback/OneMRPRenderer.java
@@ -1,39 +1,42 @@
 package com.android.onemedia.playback;
 
-import android.media.session.RoutePlaybackControls;
 import android.os.Bundle;
+import android.support.media.protocols.MediaPlayerProtocol;
+import android.support.media.protocols.MediaPlayerProtocol.MediaInfo;
 
 /**
  * Renderer for communicating with the OneMRP route
  */
 public class OneMRPRenderer extends Renderer {
-    private final RoutePlaybackControls mControls;
+    private final MediaPlayerProtocol mProtocol;
 
-    public OneMRPRenderer(RoutePlaybackControls controls) {
+    public OneMRPRenderer(MediaPlayerProtocol protocol) {
         super(null, null);
-        mControls = controls;
+        mProtocol = protocol;
     }
 
     @Override
     public void setContent(Bundle request) {
-        mControls.playNow(request.getString(RequestUtils.EXTRA_KEY_SOURCE));
+        MediaInfo mediaInfo = new MediaInfo(request.getString(RequestUtils.EXTRA_KEY_SOURCE),
+                MediaInfo.STREAM_TYPE_BUFFERED, "audio/mp3");
+        mProtocol.load(mediaInfo, true, 0, null);
     }
 
     @Override
     public boolean onStop() {
-        mControls.pause();
+        mProtocol.stop(null);
         return true;
     }
 
     @Override
     public boolean onPlay() {
-        mControls.resume();
+        mProtocol.play(null);
         return true;
     }
 
     @Override
     public boolean onPause() {
-        mControls.pause();
+        mProtocol.pause(null);
         return true;
     }
 
diff --git a/tests/OneMedia/src/com/android/onemedia/playback/RequestUtils.java b/tests/OneMedia/src/com/android/onemedia/playback/RequestUtils.java
index dd0d982..3778c5f 100644
--- a/tests/OneMedia/src/com/android/onemedia/playback/RequestUtils.java
+++ b/tests/OneMedia/src/com/android/onemedia/playback/RequestUtils.java
@@ -16,7 +16,6 @@
 package com.android.onemedia.playback;
 
 import android.os.Bundle;
-import android.support.v7.media.MediaItemMetadata;
 
 import java.util.HashMap;
 import java.util.Map;
diff --git a/tests/OneMedia/src/com/android/onemedia/provider/OneMediaRouteProvider.java b/tests/OneMedia/src/com/android/onemedia/provider/OneMediaRouteProvider.java
index f2d691c..5845e48 100644
--- a/tests/OneMedia/src/com/android/onemedia/provider/OneMediaRouteProvider.java
+++ b/tests/OneMedia/src/com/android/onemedia/provider/OneMediaRouteProvider.java
@@ -15,19 +15,23 @@
  */
 package com.android.onemedia.provider;
 
-import android.media.routeprovider.RouteConnection;
-import android.media.routeprovider.RouteInterfaceHandler;
-import android.media.routeprovider.RoutePlaybackControlsHandler;
-import android.media.routeprovider.RouteProviderService;
-import android.media.routeprovider.RouteRequest;
-import android.media.session.RouteInfo;
-import android.media.session.RoutePlaybackControls;
-import android.media.session.RouteInterface;
+import android.media.routing.MediaRouteSelector;
+import android.media.routing.MediaRouteService;
+import android.media.routing.MediaRouter.ConnectionInfo;
+import android.media.routing.MediaRouter.ConnectionRequest;
+import android.media.routing.MediaRouter.DestinationInfo;
+import android.media.routing.MediaRouter.DiscoveryRequest;
+import android.media.routing.MediaRouter.RouteInfo;
 import android.media.session.PlaybackState;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.Process;
+import android.support.media.protocols.MediaPlayerProtocol;
+import android.support.media.protocols.MediaPlayerProtocol.MediaInfo;
+import android.support.media.protocols.MediaPlayerProtocol.MediaStatus;
 import android.os.Looper;
 import android.os.ResultReceiver;
+import android.os.SystemClock;
 import android.util.Log;
 
 import com.android.onemedia.playback.LocalRenderer;
@@ -35,111 +39,135 @@
 import com.android.onemedia.playback.RequestUtils;
 
 import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
 
 /**
  * Test of MediaRouteProvider. Show a dummy provider with a simple interface for
  * playing music.
  */
-public class OneMediaRouteProvider extends RouteProviderService {
+public class OneMediaRouteProvider extends MediaRouteService {
     private static final String TAG = "OneMRP";
     private static final boolean DEBUG = true;
 
+    private static final String TEST_DESTINATION_ID = "testDestination";
+    private static final String TEST_ROUTE_ID = "testRoute";
+
     private Renderer mRenderer;
     private RenderListener mRenderListener;
     private PlaybackState mPlaybackState;
-    private RouteConnection mConnection;
-    private RoutePlaybackControlsHandler mControls;
-    private String mRouteId;
     private Handler mHandler;
 
+    private OneStub mStub;
+
     @Override
     public void onCreate() {
         mHandler = new Handler();
-        mRouteId = UUID.randomUUID().toString();
         mRenderer = new LocalRenderer(this, null);
         mRenderListener = new RenderListener();
-        mPlaybackState = new PlaybackState();
-        mPlaybackState.setActions(PlaybackState.ACTION_PAUSE
-                | PlaybackState.ACTION_PLAY);
+        PlaybackState.Builder bob = new PlaybackState.Builder();
+        bob.setActions(PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_PLAY);
+        mPlaybackState = bob.build();
 
         mRenderer.registerListener(mRenderListener);
-
-        if (DEBUG) {
-            Log.d(TAG, "onCreate, routeId is " + mRouteId);
-        }
     }
 
     @Override
-    public List<RouteInfo> getMatchingRoutes(List<RouteRequest> requests) {
-        RouteInfo.Builder bob = new RouteInfo.Builder();
-        bob.setName("OneMedia").setId(mRouteId);
-        // TODO add a helper library for generating route info with the correct
-        // options
-        Log.d(TAG, "Requests:");
-        for (RouteRequest request : requests) {
-            List<String> ifaces = request.getConnectionOptions().getInterfaceNames();
-            Log.d(TAG, "  request ifaces:" + ifaces.toString());
-            if (ifaces != null && ifaces.size() == 1
-                    && RoutePlaybackControls.NAME.equals(ifaces.get(0))) {
-                bob.addRouteOptions(request.getConnectionOptions());
-            }
+    public ClientSession onCreateClientSession(ClientInfo client) {
+        if (client.getUid() != Process.myUid()) {
+            // for testing purposes, only allow connections from this application
+            // since this provider is not fully featured
+            return null;
         }
-        ArrayList<RouteInfo> result = new ArrayList<RouteInfo>();
-        if (bob.getOptionsSize() > 0) {
-            RouteInfo info = bob.build();
-            result.add(info);
-        }
-        if (DEBUG) {
-            Log.d(TAG, "getRoutes returning " + result.toString());
-        }
-        return result;
+        return new OneSession(client);
     }
 
-    @Override
-    public RouteConnection connect(RouteInfo route, RouteRequest request) {
-        if (mConnection != null) {
-            disconnect(mConnection);
-        }
-        RouteConnection connection = new RouteConnection(this, route);
-        mControls = RoutePlaybackControlsHandler.addTo(connection);
-        mControls.addListener(new PlayHandler(mRouteId), mHandler);
-        if (DEBUG) {
-            Log.d(TAG, "Connected to route");
-        }
-        return connection;
-    }
+    private final class OneSession extends ClientSession {
+        private final ClientInfo mClient;
 
-    private class PlayHandler extends RoutePlaybackControlsHandler.Listener {
-        private final String mRouteId;
-
-        public PlayHandler(String routeId) {
-            mRouteId = routeId;
+        public OneSession(ClientInfo client) {
+            mClient = client;
         }
 
         @Override
-        public void playNow(String content, ResultReceiver cb) {
+        public boolean onStartDiscovery(DiscoveryRequest req, DiscoveryCallback callback) {
+            for (MediaRouteSelector selector : req.getSelectors()) {
+                if (isMatch(selector)) {
+                    DestinationInfo destination = new DestinationInfo.Builder(
+                            TEST_DESTINATION_ID, getServiceMetadata(), "OneMedia")
+                            .setDescription("Test route from OneMedia app.")
+                            .build();
+                    ArrayList<RouteInfo> routes = new ArrayList<RouteInfo>();
+                    routes.add(new RouteInfo.Builder(
+                            TEST_ROUTE_ID, destination, selector).build());
+                    callback.onDestinationFound(destination, routes);
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public void onStopDiscovery() {
+        }
+
+        @Override
+        public boolean onConnect(ConnectionRequest req, ConnectionCallback callback) {
+            if (req.getRoute().getId().equals(TEST_ROUTE_ID)) {
+                mStub = new OneStub();
+                ConnectionInfo connection = new ConnectionInfo.Builder(req.getRoute())
+                        .setProtocolStub(MediaPlayerProtocol.class, mStub)
+                        .build();
+                callback.onConnected(connection);
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public void onDisconnect() {
+            mStub = null;
+        }
+
+        private boolean isMatch(MediaRouteSelector selector) {
+            if (!selector.containsProtocol(MediaPlayerProtocol.class)) {
+                return false;
+            }
+            for (String protocol : selector.getRequiredProtocols()) {
+                if (!protocol.equals(MediaPlayerProtocol.class.getName())) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+
+    private final class OneStub extends MediaPlayerProtocol.Stub {
+        MediaInfo mMediaInfo;
+
+        public OneStub() {
+            super(mHandler);
+        }
+
+        @Override
+        public void onLoad(MediaInfo mediaInfo, boolean autoplay, long playPosition,
+                Bundle extras) {
             if (DEBUG) {
-                Log.d(TAG, "Attempting to play " + content);
+                Log.d(TAG, "Attempting to play " + mediaInfo.getContentId());
             }
             // look up the route and send a play command to it
+            mMediaInfo = mediaInfo;
             Bundle bundle = new Bundle();
-            bundle.putString(RequestUtils.EXTRA_KEY_SOURCE, content);
+            bundle.putString(RequestUtils.EXTRA_KEY_SOURCE, mediaInfo.getContentId());
             mRenderer.setContent(bundle);
-            RouteInterfaceHandler.sendResult(cb, RouteInterface.RESULT_SUCCESS, null);
         }
 
         @Override
-        public boolean resume() {
+        public void onPlay(Bundle extras) {
             mRenderer.onPlay();
-            return true;
         }
 
         @Override
-        public boolean pause() {
+        public void onPause(Bundle extras) {
             mRenderer.onPause();
-            return true;
         }
     }
 
@@ -148,45 +176,48 @@
         @Override
         public void onError(int type, int extra, Bundle extras, Throwable error) {
             Log.d(TAG, "Sending onError with type " + type + " and extra " + extra);
-            if (mControls != null) {
-                mControls.sendPlaybackChangeEvent(PlaybackState.STATE_ERROR);
-            }
+            sendStatusUpdate(PlaybackState.STATE_ERROR);
         }
 
         @Override
         public void onStateChanged(int newState) {
-            if (newState != Renderer.STATE_ERROR) {
-                mPlaybackState.setErrorMessage(null);
-            }
             long position = -1;
             if (mRenderer != null) {
                 position = mRenderer.getSeekPosition();
             }
+            int pbState;
+            float rate = 0;
+            String errorMsg = null;
             switch (newState) {
                 case Renderer.STATE_ENDED:
                 case Renderer.STATE_STOPPED:
-                    mPlaybackState.setState(PlaybackState.STATE_STOPPED, position, 0);
+                    pbState = PlaybackState.STATE_STOPPED;
                     break;
                 case Renderer.STATE_INIT:
                 case Renderer.STATE_PREPARING:
-                    mPlaybackState.setState(PlaybackState.STATE_BUFFERING, position, 0);
+                    pbState = PlaybackState.STATE_BUFFERING;
                     break;
                 case Renderer.STATE_ERROR:
-                    mPlaybackState.setState(PlaybackState.STATE_ERROR, position, 0);
+                    pbState = PlaybackState.STATE_ERROR;
                     break;
                 case Renderer.STATE_PAUSED:
-                    mPlaybackState.setState(PlaybackState.STATE_PAUSED, position, 0);
+                    pbState = PlaybackState.STATE_PAUSED;
                     break;
                 case Renderer.STATE_PLAYING:
-                    mPlaybackState.setState(PlaybackState.STATE_PLAYING, position, 1);
+                    pbState = PlaybackState.STATE_PLAYING;
+                    rate = 1;
                     break;
                 default:
-                    mPlaybackState.setState(PlaybackState.STATE_ERROR, position, 0);
-                    mPlaybackState.setErrorMessage("unkown state");
+                    pbState = PlaybackState.STATE_ERROR;
+                    errorMsg = "unknown state";
                     break;
             }
+            PlaybackState.Builder bob = new PlaybackState.Builder(mPlaybackState);
+            bob.setState(pbState, position, rate, SystemClock.elapsedRealtime());
+            bob.setErrorMessage(errorMsg);
+            mPlaybackState = bob.build();
 
-            mControls.sendPlaybackChangeEvent(mPlaybackState.getState());
+            sendStatusUpdate(mPlaybackState.getState());
         }
 
         @Override
@@ -195,13 +226,45 @@
 
         @Override
         public void onFocusLost() {
-            Log.d(TAG, "Focus lost, changing state to " + Renderer.STATE_PAUSED);
-            mPlaybackState.setState(PlaybackState.STATE_PAUSED, mRenderer.getSeekPosition(), 0);
+            Log.d(TAG, "Focus lost, pausing");
+            // Don't update state here, we'll get a separate call to
+            // onStateChanged when it pauses
             mRenderer.onPause();
         }
 
         @Override
         public void onNextStarted() {
         }
+
+        private void sendStatusUpdate(int state) {
+            if (mStub != null) {
+                MediaStatus status = new MediaStatus(1, mStub.mMediaInfo);
+                switch (state) {
+                    case PlaybackState.STATE_BUFFERING:
+                    case PlaybackState.STATE_FAST_FORWARDING:
+                    case PlaybackState.STATE_REWINDING:
+                    case PlaybackState.STATE_SKIPPING_TO_NEXT:
+                    case PlaybackState.STATE_SKIPPING_TO_PREVIOUS:
+                        status.setPlayerState(MediaStatus.PLAYER_STATE_BUFFERING);
+                        break;
+                    case PlaybackState.STATE_CONNECTING:
+                    case PlaybackState.STATE_STOPPED:
+                        status.setPlayerState(MediaStatus.PLAYER_STATE_IDLE);
+                        break;
+                    case PlaybackState.STATE_PAUSED:
+                        status.setPlayerState(MediaStatus.PLAYER_STATE_PAUSED);
+                        break;
+                    case PlaybackState.STATE_PLAYING:
+                        status.setPlayerState(MediaStatus.PLAYER_STATE_PLAYING);
+                        break;
+                    case PlaybackState.STATE_NONE:
+                    case PlaybackState.STATE_ERROR:
+                    default:
+                        status.setPlayerState(MediaStatus.PLAYER_STATE_UNKNOWN);
+                        break;
+                }
+                mStub.sendStatusUpdatedEvent(status, null);
+            }
+        }
     }
 }
diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp
index 12f5b92..28de933 100644
--- a/tools/aapt/Images.cpp
+++ b/tools/aapt/Images.cpp
@@ -77,6 +77,14 @@
     int32_t layoutBoundsRight;
     int32_t layoutBoundsBottom;
 
+    // Round rect outline description
+    int32_t outlineInsetsLeft;
+    int32_t outlineInsetsTop;
+    int32_t outlineInsetsRight;
+    int32_t outlineInsetsBottom;
+    float outlineRadius;
+    bool outlineFilled;
+
     png_uint_32 allocHeight;
     png_bytepp allocRows;
 };
@@ -397,6 +405,98 @@
     return NO_ERROR;
 }
 
+static void find_max_opacity(png_byte** rows,
+                             int startX, int startY, int endX, int endY, int dX, int dY,
+                             int* out_inset)
+{
+    bool opaque_within_inset = true;
+    unsigned char max_opacity = 0;
+    int inset = 0;
+    *out_inset = 0;
+    for (int x = startX, y = startY; x != endX && y != endY; x += dX, y += dY, inset++) {
+        png_byte* color = rows[y] + x * 4;
+        unsigned char opacity = color[3];
+        if (opacity > max_opacity) {
+            max_opacity = opacity;
+            *out_inset = inset;
+        }
+        if (opacity == 0xff) return;
+    }
+}
+
+static bool is_opaque_over_row(png_byte* row, int startX, int endX)
+{
+    for (int x = startX; x < endX; x++) {
+        png_byte* color = row + x * 4;
+        if (color[3] != 0xff) return false;
+    }
+    return true;
+}
+
+static bool is_opaque_over_col(png_byte** rows, int offsetX, int startY, int endY)
+{
+    for (int y = startY; y < endY; y++) {
+        png_byte* color = rows[y] + offsetX * 4;
+        if (color[3] != 0xff) return false;
+    }
+    return true;
+}
+
+static void get_outline(image_info* image)
+{
+    int midX = image->width / 2;
+    int midY = image->height / 2;
+    int endX = image->width - 2;
+    int endY = image->height - 2;
+
+    // find left and right extent of nine patch content on center row
+    if (image->width > 4) {
+        find_max_opacity(image->rows, 1, midY, midX, -1, 1, 0, &image->outlineInsetsLeft);
+        find_max_opacity(image->rows, endX, midY, midX, -1, -1, 0, &image->outlineInsetsRight);
+    } else {
+        image->outlineInsetsLeft = 0;
+        image->outlineInsetsRight = 0;
+    }
+
+    // find top and bottom extent of nine patch content on center column
+    if (image->height > 4) {
+        find_max_opacity(image->rows, midX, 1, -1, midY, 0, 1, &image->outlineInsetsTop);
+        find_max_opacity(image->rows, midX, endY, -1, midY, 0, -1, &image->outlineInsetsBottom);
+    } else {
+        image->outlineInsetsTop = 0;
+        image->outlineInsetsBottom = 0;
+    }
+
+    int innerStartX = 1 + image->outlineInsetsLeft;
+    int innerStartY = 1 + image->outlineInsetsTop;
+    int innerEndX = endX - image->outlineInsetsRight;
+    int innerEndY = endY - image->outlineInsetsBottom;
+    int innerMidX = (innerEndX + innerStartX) / 2;
+    int innerMidY = (innerEndY + innerStartY) / 2;
+
+    // assuming the image is a round rect, compute the radius by marching
+    // diagonally from the top left corner towards the center
+    image->outlineFilled = is_opaque_over_row(image->rows[innerMidY], innerStartX, innerEndX)
+            && is_opaque_over_col(image->rows, innerMidX, innerStartY, innerStartY);
+
+    int diagonalInset = 0;
+    find_max_opacity(image->rows, innerStartX, innerStartY, innerMidX, innerMidY, 1, 1,
+            &diagonalInset);
+
+    // Determine source radius based upon inset
+    // radius = 1 / (sqrt(2) - 1) * inset
+    image->outlineRadius = 2.4142f * diagonalInset;
+
+    NOISY(printf("outline insets %d %d %d %d, rad %f, filled %d\n",
+            image->outlineFilled,
+            image->outlineInsetsLeft,
+            image->outlineInsetsTop,
+            image->outlineInsetsRight,
+            image->outlineInsetsBottom,
+            image->outlineRadius,
+            image->outlineFilled));
+}
+
 
 static uint32_t get_color(
     png_bytepp rows, int left, int top, int right, int bottom)
@@ -571,6 +671,9 @@
                 image->layoutBoundsRight, image->layoutBoundsBottom));
     }
 
+    // use opacity of pixels to estimate the round rect outline
+    get_outline(image);
+
     // If padding is not yet specified, take values from size.
     if (image->info9Patch.paddingLeft < 0) {
         image->info9Patch.paddingLeft = xDivs[0];
@@ -966,9 +1069,10 @@
     int bit_depth, interlace_type, compression_type;
     int i;
 
-    png_unknown_chunk unknowns[2];
+    png_unknown_chunk unknowns[3];
     unknowns[0].data = NULL;
     unknowns[1].data = NULL;
+    unknowns[2].data = NULL;
 
     png_bytepp outRows = (png_bytepp) malloc((int) imageInfo.height * sizeof(png_bytep));
     if (outRows == (png_bytepp) 0) {
@@ -1038,12 +1142,17 @@
     }
 
     if (imageInfo.is9Patch) {
-        int chunk_count = 1 + (imageInfo.haveLayoutBounds ? 1 : 0);
-        int p_index = imageInfo.haveLayoutBounds ? 1 : 0;
-        int b_index = 0;
+        int chunk_count = 2 + (imageInfo.haveLayoutBounds ? 1 : 0);
+        int p_index = imageInfo.haveLayoutBounds ? 2 : 1;
+        int b_index = 1;
+        int o_index = 0;
+
+        // Chunks ordered thusly because older platforms depend on the base 9 patch data being last
         png_byte *chunk_names = imageInfo.haveLayoutBounds
-                ? (png_byte*)"npLb\0npTc\0"
-                : (png_byte*)"npTc";
+                ? (png_byte*)"npOl\0npLb\0npTc\0"
+                : (png_byte*)"npOl\0npTc";
+
+        // base 9 patch data
         NOISY(printf("Adding 9-patch info...\n"));
         strcpy((char*)unknowns[p_index].name, "npTc");
         unknowns[p_index].data = (png_byte*)imageInfo.serialize9patch();
@@ -1051,6 +1160,18 @@
         // TODO: remove the check below when everything works
         checkNinePatchSerialization(&imageInfo.info9Patch, unknowns[p_index].data);
 
+        // automatically generated 9 patch outline data
+        int chunk_size = sizeof(png_uint_32) * 6;
+        strcpy((char*)unknowns[o_index].name, "npOl");
+        unknowns[o_index].data = (png_byte*) calloc(chunk_size, 1);
+        png_byte outputData[chunk_size];
+        memcpy(&outputData, &imageInfo.outlineInsetsLeft, 4 * sizeof(png_uint_32));
+        ((float*) outputData)[4] = imageInfo.outlineRadius;
+        ((png_uint_32*) outputData)[5] = imageInfo.outlineFilled ? 1 : 0;
+        memcpy(unknowns[o_index].data, &outputData, chunk_size);
+        unknowns[o_index].size = chunk_size;
+
+        // optional optical inset / layout bounds data
         if (imageInfo.haveLayoutBounds) {
             int chunk_size = sizeof(png_uint_32) * 4;
             strcpy((char*)unknowns[b_index].name, "npLb");
@@ -1099,6 +1220,7 @@
     free(outRows);
     free(unknowns[0].data);
     free(unknowns[1].data);
+    free(unknowns[2].data);
 
     png_get_IHDR(write_ptr, write_info, &width, &height,
        &bit_depth, &color_type, &interlace_type,
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 efd55bf..ca61ffb 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
@@ -1083,6 +1083,12 @@
     }
 
     @Override
+    public File getNoBackupFilesDir() {
+        // pass
+        return null;
+    }
+
+    @Override
     public File getExternalFilesDir(String type) {
         // pass
         return null;