Merge "Fix alpha animations in ViewPropAnimRT"
diff --git a/Android.mk b/Android.mk
index 56e9df6..8f7779e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -175,6 +175,7 @@
 	core/java/android/nfc/INfcAdapterExtras.aidl \
 	core/java/android/nfc/INfcTag.aidl \
 	core/java/android/nfc/INfcCardEmulation.aidl \
+	core/java/android/nfc/INfcLockscreenDispatch.aidl \
 	core/java/android/os/IBatteryPropertiesListener.aidl \
 	core/java/android/os/IBatteryPropertiesRegistrar.aidl \
 	core/java/android/os/ICancellationSignal.aidl \
diff --git a/api/current.txt b/api/current.txt
index ddde2f7..a263946 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2525,7 +2525,14 @@
 
   public static final class R.transition {
     ctor public R.transition();
+    field public static final int explode = 17760259; // 0x10f0003
+    field public static final int fade = 17760258; // 0x10f0002
+    field public static final int move = 17760257; // 0x10f0001
     field public static final int no_transition = 17760256; // 0x10f0000
+    field public static final int slide_bottom = 17760260; // 0x10f0004
+    field public static final int slide_left = 17760263; // 0x10f0007
+    field public static final int slide_right = 17760262; // 0x10f0006
+    field public static final int slide_top = 17760261; // 0x10f0005
   }
 
   public static final class R.xml {
@@ -13798,6 +13805,7 @@
     method public deprecated void setWiredHeadsetOn(boolean);
     method public deprecated boolean shouldVibrate(int);
     method public void startBluetoothSco();
+    method public void startBluetoothScoVirtualCall();
     method public void stopBluetoothSco();
     method public void unloadSoundEffects();
     method public void unregisterMediaButtonEventReceiver(android.content.ComponentName);
@@ -14482,6 +14490,11 @@
     method public java.lang.String getDefaultUrl();
   }
 
+  public static final class MediaDrm.MediaDrmStateException extends java.lang.IllegalStateException {
+    ctor public MediaDrm.MediaDrmStateException(int, java.lang.String);
+    method public int getErrorCode();
+  }
+
   public static abstract interface MediaDrm.OnEventListener {
     method public abstract void onEvent(android.media.MediaDrm, byte[], int, int, byte[]);
   }
@@ -15350,6 +15363,19 @@
     ctor public UnsupportedSchemeException(java.lang.String);
   }
 
+  public abstract class VolumeProvider {
+    ctor public VolumeProvider(int, int);
+    method public final int getMaxVolume();
+    method public final int getVolumeControl();
+    method public final void notifyVolumeChanged();
+    method public void onAdjustVolumeBy(int);
+    method public abstract int onGetCurrentVolume();
+    method public void onSetVolumeTo(int);
+    field public static final int VOLUME_CONTROL_ABSOLUTE = 2; // 0x2
+    field public static final int VOLUME_CONTROL_FIXED = 0; // 0x0
+    field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
+  }
+
 }
 
 package android.media.audiofx {
@@ -15716,6 +15742,7 @@
     method public android.media.session.PlaybackState getPlaybackState();
     method public int getRatingType();
     method public android.media.session.MediaController.TransportControls getTransportControls();
+    method public android.media.session.MediaController.VolumeInfo getVolumeInfo();
     method public void removeCallback(android.media.session.MediaController.Callback);
     method public void sendControlCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
   }
@@ -15739,6 +15766,14 @@
     method public void stop();
   }
 
+  public static final class MediaController.VolumeInfo {
+    method public int getAudioStream();
+    method public int getCurrentVolume();
+    method public int getMaxVolume();
+    method public int getVolumeControl();
+    method public int getVolumeType();
+  }
+
   public final class MediaSession {
     method public void addCallback(android.media.session.MediaSession.Callback);
     method public void addCallback(android.media.session.MediaSession.Callback, android.os.Handler);
@@ -15756,9 +15791,11 @@
     method public void setMetadata(android.media.MediaMetadata);
     method public void setPlaybackState(android.media.session.PlaybackState);
     method public void setPlaybackToLocal(int);
-    method public void setPlaybackToRemote(android.media.session.RemoteVolumeProvider);
+    method public void setPlaybackToRemote(android.media.VolumeProvider);
     field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
     field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
+    field public static final int VOLUME_TYPE_LOCAL = 1; // 0x1
+    field public static final int VOLUME_TYPE_REMOTE = 2; // 0x2
   }
 
   public static abstract class MediaSession.Callback {
@@ -15829,19 +15866,6 @@
     field public static final int STATE_STOPPED = 1; // 0x1
   }
 
-  public abstract class RemoteVolumeProvider {
-    ctor public RemoteVolumeProvider(int, int);
-    method public final int getMaxVolume();
-    method public final int getVolumeControl();
-    method public final void notifyVolumeChanged();
-    method public void onAdjustVolumeBy(int);
-    method public abstract int onGetCurrentVolume();
-    method public void onSetVolumeTo(int);
-    field public static final int VOLUME_CONTROL_ABSOLUTE = 2; // 0x2
-    field public static final int VOLUME_CONTROL_FIXED = 0; // 0x0
-    field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
-  }
-
 }
 
 package android.media.tv {
@@ -17640,6 +17664,7 @@
     method public boolean invokeBeam(android.app.Activity);
     method public boolean isEnabled();
     method public boolean isNdefPushEnabled();
+    method public boolean registerLockscreenDispatch(android.nfc.NfcAdapter.NfcLockscreenDispatch, int[]);
     method public void setBeamPushUris(android.net.Uri[], android.app.Activity);
     method public void setBeamPushUrisCallback(android.nfc.NfcAdapter.CreateBeamUrisCallback, android.app.Activity);
     method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, android.app.Activity...);
@@ -17675,6 +17700,10 @@
     method public abstract android.nfc.NdefMessage createNdefMessage(android.nfc.NfcEvent);
   }
 
+  public static abstract interface NfcAdapter.NfcLockscreenDispatch {
+    method public abstract boolean onTagDetected(android.nfc.Tag);
+  }
+
   public static abstract interface NfcAdapter.OnNdefPushCompleteCallback {
     method public abstract void onNdefPushComplete(android.nfc.NfcEvent);
   }
@@ -27797,7 +27826,6 @@
     field public static final java.lang.String ACTION_CALL_SERVICE;
     field public static final java.lang.String ACTION_CALL_SERVICE_PROVIDER;
     field public static final java.lang.String ACTION_CALL_SERVICE_SELECTOR;
-    field public static final java.lang.String ACTION_CHANGE_DEFAULT_PHONE = "android.telecomm.ACTION_CHANGE_DEFAULT_PHONE";
     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 ';'
@@ -27805,7 +27833,6 @@
     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_INCOMING_CALL_EXTRAS = "android.intent.extra.INCOMING_CALL_EXTRAS";
-    field public static final java.lang.String EXTRA_PACKAGE_NAME = "package";
     field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.intent.extra.START_CALL_WITH_SPEAKERPHONE";
   }
 
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 6296dd1..6b2a0e2 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -52,13 +52,13 @@
     );
 }
 
-static SkBitmap::Config flinger2skia(PixelFormat f)
+static SkColorType flinger2skia(PixelFormat f)
 {
     switch (f) {
         case PIXEL_FORMAT_RGB_565:
-            return SkBitmap::kRGB_565_Config;
+            return kRGB_565_SkColorType;
         default:
-            return SkBitmap::kARGB_8888_Config;
+            return kN32_SkColorType;
     }
 }
 
@@ -174,9 +174,10 @@
 
     if (base) {
         if (png) {
+            const SkImageInfo info = SkImageInfo::Make(w, h, flinger2skia(f),
+                                                       kPremul_SkAlphaType);
             SkBitmap b;
-            b.setConfig(flinger2skia(f), w, h, s*bytesPerPixel(f));
-            b.setPixels((void*)base);
+            b.installPixels(info, const_cast<void*>(base), s*bytesPerPixel(f));
             SkDynamicMemoryWStream stream;
             SkImageEncoder::EncodeStream(&stream, b,
                     SkImageEncoder::kPNG_Type, SkImageEncoder::kDefaultQuality);
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index b78b9c9..c583998 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -188,6 +188,9 @@
             endPerformanceSnapshot();
         }
         if (mPerfMetrics != null) {
+            if (results == null) {
+                results = new Bundle();
+            }
             results.putAll(mPerfMetrics);
         }
         if (mUiAutomation != null) {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 3dfa78b..e24dc84 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -55,6 +55,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -6821,72 +6822,12 @@
         if (other == null) {
             return false;
         }
-        if (mAction != other.mAction) {
-            if (mAction != null) {
-                if (!mAction.equals(other.mAction)) {
-                    return false;
-                }
-            } else {
-                if (!other.mAction.equals(mAction)) {
-                    return false;
-                }
-            }
-        }
-        if (mData != other.mData) {
-            if (mData != null) {
-                if (!mData.equals(other.mData)) {
-                    return false;
-                }
-            } else {
-                if (!other.mData.equals(mData)) {
-                    return false;
-                }
-            }
-        }
-        if (mType != other.mType) {
-            if (mType != null) {
-                if (!mType.equals(other.mType)) {
-                    return false;
-                }
-            } else {
-                if (!other.mType.equals(mType)) {
-                    return false;
-                }
-            }
-        }
-        if (mPackage != other.mPackage) {
-            if (mPackage != null) {
-                if (!mPackage.equals(other.mPackage)) {
-                    return false;
-                }
-            } else {
-                if (!other.mPackage.equals(mPackage)) {
-                    return false;
-                }
-            }
-        }
-        if (mComponent != other.mComponent) {
-            if (mComponent != null) {
-                if (!mComponent.equals(other.mComponent)) {
-                    return false;
-                }
-            } else {
-                if (!other.mComponent.equals(mComponent)) {
-                    return false;
-                }
-            }
-        }
-        if (mCategories != other.mCategories) {
-            if (mCategories != null) {
-                if (!mCategories.equals(other.mCategories)) {
-                    return false;
-                }
-            } else {
-                if (!other.mCategories.equals(mCategories)) {
-                    return false;
-                }
-            }
-        }
+        if (!Objects.equals(this.mAction, other.mAction)) return false;
+        if (!Objects.equals(this.mData, other.mData)) return false;
+        if (!Objects.equals(this.mType, other.mType)) return false;
+        if (!Objects.equals(this.mPackage, other.mPackage)) return false;
+        if (!Objects.equals(this.mComponent, other.mComponent)) return false;
+        if (!Objects.equals(this.mCategories, other.mCategories)) return false;
 
         return true;
     }
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 44fc3b6..cc8503b 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -167,7 +167,7 @@
     private boolean mOneShot;
     private boolean mWithBuffer;
     private boolean mFaceDetectionRunning = false;
-    private Object mAutoFocusCallbackLock = new Object();
+    private final Object mAutoFocusCallbackLock = new Object();
 
     private static final int NO_ERROR = 0;
     private static final int EACCESS = -13;
@@ -202,14 +202,14 @@
 
     /**
      * A constant meaning the normal camera connect/open will be used.
-     * @hide
      */
-    public static final int CAMERA_HAL_API_VERSION_NORMAL_OPEN = -2;
+    private static final int CAMERA_HAL_API_VERSION_NORMAL_CONNECT = -2;
 
     /**
      * Used to indicate HAL version un-specified.
      */
     private static final int CAMERA_HAL_API_VERSION_UNSPECIFIED = -1;
+
     /**
      * Hardware face detection. It does not use much CPU.
      */
@@ -376,18 +376,25 @@
      *
      * @param cameraId The hardware camera to access, between 0 and
      * {@link #getNumberOfCameras()}-1.
-     * @param halVersion The HAL API version this camera device to be opened as. When
-     * it is {@value #CAMERA_HAL_API_VERSION_NORMAL_OPEN}, the methods will be equivalent
-     * to {@link #open}, but more detailed error information will be returned to managed code.
+     * @param halVersion The HAL API version this camera device to be opened as.
      * @return a new Camera object, connected, locked and ready for use.
+     *
+     * @throws IllegalArgumentException if the {@code halVersion} is invalid
+     *
      * @throws RuntimeException if opening the camera fails (for example, if the
      * camera is in use by another process or device policy manager has disabled
      * the camera).
+     *
      * @see android.app.admin.DevicePolicyManager#getCameraDisabled(android.content.ComponentName)
+     * @see #CAMERA_HAL_API_VERSION_1_0
      *
      * @hide
      */
     public static Camera openLegacy(int cameraId, int halVersion) {
+        if (halVersion < CAMERA_HAL_API_VERSION_1_0) {
+            throw new IllegalArgumentException("Invalid HAL version " + halVersion);
+        }
+
         return new Camera(cameraId, halVersion);
     }
 
@@ -399,7 +406,7 @@
      * @param halVersion The HAL API version this camera device to be opened as.
      */
     private Camera(int cameraId, int halVersion) {
-        int err = cameraInit(cameraId, halVersion);
+        int err = cameraInitVersion(cameraId, halVersion);
         if (checkInitErrors(err)) {
             switch(err) {
                 case EACCESS:
@@ -428,13 +435,7 @@
         }
     }
 
-    private int cameraInit(int cameraId, int halVersion) {
-        // This function should be only called by Camera(int cameraId, int halVersion).
-        if (halVersion < CAMERA_HAL_API_VERSION_1_0 &&
-                halVersion != CAMERA_HAL_API_VERSION_NORMAL_OPEN) {
-            throw new IllegalArgumentException("Invalid HAL version " + halVersion);
-        }
-
+    private int cameraInitVersion(int cameraId, int halVersion) {
         mShutterCallback = null;
         mRawImageCallback = null;
         mJpegCallback = null;
@@ -457,8 +458,31 @@
         return native_setup(new WeakReference<Camera>(this), cameraId, halVersion, packageName);
     }
 
+    private int cameraInitNormal(int cameraId) {
+        return cameraInitVersion(cameraId, CAMERA_HAL_API_VERSION_NORMAL_CONNECT);
+    }
+
+    /**
+     * Connect to the camera service using #connectLegacy
+     *
+     * <p>
+     * This acts the same as normal except that it will return
+     * the detailed error code if open fails instead of
+     * converting everything into {@code NO_INIT}.</p>
+     *
+     * <p>Intended to use by the camera2 shim only, do <i>not</i> use this for other code.</p>
+     *
+     * @return a detailed errno error code, or {@code NO_ERROR} on success
+     *
+     * @hide
+     */
+    public int cameraInitUnspecified(int cameraId) {
+        return cameraInitVersion(cameraId, CAMERA_HAL_API_VERSION_UNSPECIFIED);
+    }
+
+    /** used by Camera#open, Camera#open(int) */
     Camera(int cameraId) {
-        int err = cameraInit(cameraId);
+        int err = cameraInitNormal(cameraId);
         if (checkInitErrors(err)) {
             switch(err) {
                 case EACCESS:
@@ -472,32 +496,6 @@
         }
     }
 
-    /**
-     * @hide
-     */
-    public int cameraInit(int cameraId) {
-        mShutterCallback = null;
-        mRawImageCallback = null;
-        mJpegCallback = null;
-        mPreviewCallback = null;
-        mPostviewCallback = null;
-        mUsingPreviewAllocation = false;
-        mZoomListener = null;
-
-        Looper looper;
-        if ((looper = Looper.myLooper()) != null) {
-            mEventHandler = new EventHandler(this, looper);
-        } else if ((looper = Looper.getMainLooper()) != null) {
-            mEventHandler = new EventHandler(this, looper);
-        } else {
-            mEventHandler = null;
-        }
-
-        String packageName = ActivityThread.currentPackageName();
-
-        return native_setup(new WeakReference<Camera>(this), cameraId,
-                CAMERA_HAL_API_VERSION_UNSPECIFIED, packageName);
-    }
 
     /**
      * @hide
@@ -519,6 +517,7 @@
     Camera() {
     }
 
+    @Override
     protected void finalize() {
         release();
     }
@@ -1056,7 +1055,7 @@
 
     private class EventHandler extends Handler
     {
-        private Camera mCamera;
+        private final Camera mCamera;
 
         public EventHandler(Camera c, Looper looper) {
             super(looper);
@@ -2337,6 +2336,7 @@
          * @hide
          * @deprecated
          */
+        @Deprecated
         public void dump() {
             Log.e(TAG, "dump: size=" + mMap.size());
             for (String k : mMap.keySet()) {
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 4a87680..e2f88eb 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -579,6 +579,7 @@
      * of the lens that can be focused correctly.</p>
      * <p>If the lens is fixed-focus, this should be
      * 0.</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      */
     public static final Key<Float> LENS_INFO_MINIMUM_FOCUS_DISTANCE =
             new Key<Float>("android.lens.info.minimumFocusDistance", float.class);
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 73188ff..9a3d806 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -278,14 +278,19 @@
                 ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks();
                 int id = Integer.parseInt(cameraId);
                 try {
-                    mCameraService.connectDevice(callbacks, id, mContext.getPackageName(),
-                            USE_CALLING_UID, holder);
-                    cameraUser = ICameraDeviceUser.Stub.asInterface(holder.getBinder());
-                } catch (CameraRuntimeException e) {
-                    if (e.getReason() == CameraAccessException.CAMERA_DEPRECATED_HAL) {
+                    if (supportsCamera2Api(cameraId)) {
+                        // Use cameraservice's cameradeviceclient implementation for HAL3.2+ devices
+                        mCameraService.connectDevice(callbacks, id, mContext.getPackageName(),
+                                USE_CALLING_UID, holder);
+                        cameraUser = ICameraDeviceUser.Stub.asInterface(holder.getBinder());
+                    } else {
                         // Use legacy camera implementation for HAL1 devices
                         Log.i(TAG, "Using legacy camera HAL.");
                         cameraUser = CameraDeviceUserShim.connectBinderShim(callbacks, id);
+                    }
+                } catch (CameraRuntimeException e) {
+                    if (e.getReason() == CameraAccessException.CAMERA_DEPRECATED_HAL) {
+                        throw new AssertionError("Should've gone down the shim path");
                     } else if (e.getReason() == CameraAccessException.CAMERA_IN_USE ||
                             e.getReason() == CameraAccessException.MAX_CAMERAS_IN_USE ||
                             e.getReason() == CameraAccessException.CAMERA_DISABLED ||
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 2cfb611..5bc59dc 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -18,6 +18,7 @@
 
 import android.hardware.camera2.CameraCharacteristics.Key;
 import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.utils.HashCodeHelpers;
 import android.hardware.camera2.utils.TypeReference;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -280,7 +281,7 @@
 
     @Override
     public int hashCode() {
-        return mSettings.hashCode();
+        return HashCodeHelpers.hashCode(mSettings, mSurfaceSet, mUserTag);
     }
 
     public static final Parcelable.Creator<CaptureRequest> CREATOR =
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index d9f3af4..2e59eee 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -28,6 +28,8 @@
 import android.hardware.camera2.TotalCaptureResult;
 import android.hardware.camera2.utils.CameraBinderDecorator;
 import android.hardware.camera2.utils.CameraRuntimeException;
+import android.hardware.camera2.utils.CloseableLock;
+import android.hardware.camera2.utils.CloseableLock.ScopedLock;
 import android.hardware.camera2.utils.LongParcelable;
 import android.os.Handler;
 import android.os.IBinder;
@@ -57,13 +59,14 @@
     // TODO: guard every function with if (!mRemoteDevice) check (if it was closed)
     private ICameraDeviceUser mRemoteDevice;
 
-    private final Object mLock = new Object();
+    private final CloseableLock mCloseLock;
     private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
 
     private final StateListener mDeviceListener;
     private volatile StateListener mSessionStateListener;
     private final Handler mDeviceHandler;
 
+    private volatile boolean mClosing = false;
     private boolean mInError = false;
     private boolean mIdle = true;
 
@@ -100,7 +103,9 @@
     private final Runnable mCallOnOpened = new Runnable() {
         @Override
         public void run() {
-            if (!CameraDeviceImpl.this.isClosed()) {
+            try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+                if (scopedLock == null) return; // Camera already closed
+
                 StateListener sessionListener = mSessionStateListener;
                 if (sessionListener != null) {
                     sessionListener.onOpened(CameraDeviceImpl.this);
@@ -113,7 +118,9 @@
     private final Runnable mCallOnUnconfigured = new Runnable() {
         @Override
         public void run() {
-            if (!CameraDeviceImpl.this.isClosed()) {
+            try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+                if (scopedLock == null) return; // Camera already closed
+
                 StateListener sessionListener = mSessionStateListener;
                 if (sessionListener != null) {
                     sessionListener.onUnconfigured(CameraDeviceImpl.this);
@@ -126,7 +133,9 @@
     private final Runnable mCallOnActive = new Runnable() {
         @Override
         public void run() {
-            if (!CameraDeviceImpl.this.isClosed()) {
+            try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+                if (scopedLock == null) return; // Camera already closed
+
                 StateListener sessionListener = mSessionStateListener;
                 if (sessionListener != null) {
                     sessionListener.onActive(CameraDeviceImpl.this);
@@ -139,7 +148,9 @@
     private final Runnable mCallOnBusy = new Runnable() {
         @Override
         public void run() {
-            if (!CameraDeviceImpl.this.isClosed()) {
+            try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+                if (scopedLock == null) return; // Camera already closed
+
                 StateListener sessionListener = mSessionStateListener;
                 if (sessionListener != null) {
                     sessionListener.onBusy(CameraDeviceImpl.this);
@@ -150,20 +161,29 @@
     };
 
     private final Runnable mCallOnClosed = new Runnable() {
+        private boolean mClosedOnce = false;
+
         @Override
         public void run() {
+            if (mClosedOnce) {
+                throw new AssertionError("Don't post #onClosed more than once");
+            }
+
             StateListener sessionListener = mSessionStateListener;
             if (sessionListener != null) {
                 sessionListener.onClosed(CameraDeviceImpl.this);
             }
             mDeviceListener.onClosed(CameraDeviceImpl.this);
+            mClosedOnce = true;
         }
     };
 
     private final Runnable mCallOnIdle = new Runnable() {
         @Override
         public void run() {
-            if (!CameraDeviceImpl.this.isClosed()) {
+            try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+                if (scopedLock == null) return; // Camera already closed
+
                 StateListener sessionListener = mSessionStateListener;
                 if (sessionListener != null) {
                     sessionListener.onIdle(CameraDeviceImpl.this);
@@ -176,7 +196,9 @@
     private final Runnable mCallOnDisconnected = new Runnable() {
         @Override
         public void run() {
-            if (!CameraDeviceImpl.this.isClosed()) {
+            try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+                if (scopedLock == null) return; // Camera already closed
+
                 StateListener sessionListener = mSessionStateListener;
                 if (sessionListener != null) {
                     sessionListener.onDisconnected(CameraDeviceImpl.this);
@@ -195,6 +217,7 @@
         mDeviceListener = listener;
         mDeviceHandler = handler;
         mCharacteristics = characteristics;
+        mCloseLock = new CloseableLock(/*name*/"CD-" + mCameraId);
 
         final int MAX_TAG_LEN = 23;
         String tag = String.format("CameraDevice-JV-%s", mCameraId);
@@ -210,8 +233,8 @@
     }
 
     public void setRemoteDevice(ICameraDeviceUser remoteDevice) {
+        try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
         // TODO: Move from decorator to direct binder-mediated exceptions
-        synchronized(mLock) {
             // If setRemoteFailure already called, do nothing
             if (mInError) return;
 
@@ -254,7 +277,9 @@
         }
         final int code = failureCode;
         final boolean isError = failureIsError;
-        synchronized (mLock) {
+        try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+            if (scopedLock == null) return; // Camera already closed, can't go to error state
+
             mInError = true;
             mDeviceHandler.post(new Runnable() {
                 @Override
@@ -280,7 +305,7 @@
         if (outputs == null) {
             outputs = new ArrayList<Surface>();
         }
-        synchronized (mLock) {
+        try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
             checkIfCameraClosedOrInError();
 
             HashSet<Surface> addSet = new HashSet<Surface>(outputs);    // Streams to create
@@ -344,7 +369,7 @@
     public void createCaptureSession(List<Surface> outputs,
             CameraCaptureSession.StateListener listener, Handler handler)
             throws CameraAccessException {
-        synchronized (mLock) {
+        try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
             if (DEBUG) {
                 Log.d(TAG, "createCaptureSession");
             }
@@ -389,7 +414,7 @@
     @Override
     public CaptureRequest.Builder createCaptureRequest(int templateType)
             throws CameraAccessException {
-        synchronized (mLock) {
+        try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
             checkIfCameraClosedOrInError();
 
             CameraMetadataNative templatedRequest = new CameraMetadataNative();
@@ -435,7 +460,7 @@
      * starting and stopping repeating request and flushing.
      *
      * <p>If lastFrameNumber is NO_FRAMES_CAPTURED, it means that the request was never
-     * sent to HAL. Then onCaptureSequenceCompleted is immediately triggered.
+     * sent to HAL. Then onCaptureSequenceAborted is immediately triggered.
      * If lastFrameNumber is non-negative, then the requestId and lastFrameNumber pair
      * is added to the list mFrameNumberRequestPairs.</p>
      *
@@ -446,7 +471,7 @@
     private void checkEarlyTriggerSequenceComplete(
             final int requestId, final long lastFrameNumber) {
         // lastFrameNumber being equal to NO_FRAMES_CAPTURED means that the request
-        // was never sent to HAL. Should trigger onCaptureSequenceCompleted immediately.
+        // was never sent to HAL. Should trigger onCaptureSequenceAborted immediately.
         if (lastFrameNumber == CaptureListener.NO_FRAMES_CAPTURED) {
             final CaptureListenerHolder holder;
             int index = mCaptureListenerMap.indexOfKey(requestId);
@@ -463,7 +488,7 @@
 
             if (holder != null) {
                 if (DEBUG) {
-                    Log.v(TAG, "immediately trigger onCaptureSequenceCompleted because"
+                    Log.v(TAG, "immediately trigger onCaptureSequenceAborted because"
                             + " request did not reach HAL");
                 }
 
@@ -480,10 +505,9 @@
                                     || lastFrameNumber > Integer.MAX_VALUE) {
                                 throw new AssertionError(lastFrameNumber + " cannot be cast to int");
                             }
-                            holder.getListener().onCaptureSequenceCompleted(
+                            holder.getListener().onCaptureSequenceAborted(
                                     CameraDeviceImpl.this,
-                                    requestId,
-                                    lastFrameNumber);
+                                    requestId);
                         }
                     }
                 };
@@ -509,7 +533,7 @@
             handler = checkHandler(handler);
         }
 
-        synchronized (mLock) {
+        try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
             checkIfCameraClosedOrInError();
             int requestId;
 
@@ -581,7 +605,7 @@
     @Override
     public void stopRepeating() throws CameraAccessException {
 
-        synchronized (mLock) {
+        try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
             checkIfCameraClosedOrInError();
             if (mRepeatingRequestId != REQUEST_ID_NONE) {
 
@@ -612,7 +636,7 @@
 
     private void waitUntilIdle() throws CameraAccessException {
 
-        synchronized (mLock) {
+        try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
             checkIfCameraClosedOrInError();
             if (mRepeatingRequestId != REQUEST_ID_NONE) {
                 throw new IllegalStateException("Active repeating request ongoing");
@@ -633,7 +657,7 @@
 
     @Override
     public void flush() throws CameraAccessException {
-        synchronized (mLock) {
+        try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
             checkIfCameraClosedOrInError();
 
             mDeviceHandler.post(mCallOnBusy);
@@ -656,8 +680,15 @@
 
     @Override
     public void close() {
-        synchronized (mLock) {
+        mClosing = true;
+        // Acquire exclusive lock, close, release (idempotent)
+        mCloseLock.close();
 
+        /*
+         *  The rest of this is safe, since no other methods will be able to execute
+         *  (they will throw ISE instead; the callbacks will get dropped)
+         */
+        {
             try {
                 if (mRemoteDevice != null) {
                     mRemoteDevice.disconnect();
@@ -805,7 +836,12 @@
                 // remove request from mCaptureListenerMap
                 final int requestId = frameNumberRequestPair.getValue();
                 final CaptureListenerHolder holder;
-                synchronized (mLock) {
+                try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+                    if (scopedLock == null) {
+                        Log.w(TAG, "Camera closed while checking sequences");
+                        return;
+                    }
+
                     int index = mCaptureListenerMap.indexOfKey(requestId);
                     holder = (index >= 0) ? mCaptureListenerMap.valueAt(index)
                             : null;
@@ -890,9 +926,12 @@
         @Override
         public void onCameraError(final int errorCode, CaptureResultExtras resultExtras) {
             Runnable r = null;
-            if (isClosed()) return;
 
-            synchronized(mLock) {
+            try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+                if (scopedLock == null) {
+                    return; // Camera already closed
+                }
+
                 mInError = true;
                 switch (errorCode) {
                     case ERROR_CAMERA_DISCONNECTED:
@@ -914,25 +953,24 @@
                         break;
                 }
                 CameraDeviceImpl.this.mDeviceHandler.post(r);
-            }
 
-            // Fire onCaptureSequenceCompleted
-            if (DEBUG) {
-                Log.v(TAG, String.format("got error frame %d", resultExtras.getFrameNumber()));
+                // Fire onCaptureSequenceCompleted
+                if (DEBUG) {
+                    Log.v(TAG, String.format("got error frame %d", resultExtras.getFrameNumber()));
+                }
+                mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/true);
+                checkAndFireSequenceComplete();
             }
-            mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/true);
-            checkAndFireSequenceComplete();
-
         }
 
         @Override
         public void onCameraIdle() {
-            if (isClosed()) return;
-
             if (DEBUG) {
                 Log.d(TAG, "Camera now idle");
             }
-            synchronized (mLock) {
+            try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+                if (scopedLock == null) return; // Camera already closed
+
                 if (!CameraDeviceImpl.this.mIdle) {
                     CameraDeviceImpl.this.mDeviceHandler.post(mCallOnIdle);
                 }
@@ -948,30 +986,33 @@
             }
             final CaptureListenerHolder holder;
 
-            // Get the listener for this frame ID, if there is one
-            synchronized (mLock) {
+            try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+                if (scopedLock == null) return; // Camera already closed
+
+                // Get the listener for this frame ID, if there is one
                 holder = CameraDeviceImpl.this.mCaptureListenerMap.get(requestId);
-            }
 
-            if (holder == null) {
-                return;
-            }
+                if (holder == null) {
+                    return;
+                }
 
-            if (isClosed()) return;
+                if (isClosed()) return;
 
-            // Dispatch capture start notice
-            holder.getHandler().post(
-                new Runnable() {
-                    @Override
-                    public void run() {
-                        if (!CameraDeviceImpl.this.isClosed()) {
-                            holder.getListener().onCaptureStarted(
-                                CameraDeviceImpl.this,
-                                holder.getRequest(resultExtras.getSubsequenceId()),
-                                timestamp);
+                // Dispatch capture start notice
+                holder.getHandler().post(
+                    new Runnable() {
+                        @Override
+                        public void run() {
+                            if (!CameraDeviceImpl.this.isClosed()) {
+                                holder.getListener().onCaptureStarted(
+                                    CameraDeviceImpl.this,
+                                    holder.getRequest(resultExtras.getSubsequenceId()),
+                                    timestamp);
+                            }
                         }
-                    }
-                });
+                    });
+
+            }
         }
 
         @Override
@@ -984,88 +1025,91 @@
                         + requestId);
             }
 
+            try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+                if (scopedLock == null) return; // Camera already closed
 
-            // TODO: Handle CameraCharacteristics access from CaptureResult correctly.
-            result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE,
-                    getCharacteristics().get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE));
+                // TODO: Handle CameraCharacteristics access from CaptureResult correctly.
+                result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE,
+                        getCharacteristics().get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE));
 
-            final CaptureListenerHolder holder;
-            synchronized (mLock) {
-                holder = CameraDeviceImpl.this.mCaptureListenerMap.get(requestId);
-            }
+                final CaptureListenerHolder holder =
+                        CameraDeviceImpl.this.mCaptureListenerMap.get(requestId);
 
-            Boolean quirkPartial = result.get(CaptureResult.QUIRKS_PARTIAL_RESULT);
-            boolean quirkIsPartialResult = (quirkPartial != null && quirkPartial);
+                Boolean quirkPartial = result.get(CaptureResult.QUIRKS_PARTIAL_RESULT);
+                boolean quirkIsPartialResult = (quirkPartial != null && quirkPartial);
 
-            // Update tracker (increment counter) when it's not a partial result.
-            if (!quirkIsPartialResult) {
-                mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/false);
-            }
-
-            // Check if we have a listener for this
-            if (holder == null) {
-                if (DEBUG) {
-                    Log.d(TAG,
-                            "holder is null, early return at frame "
-                                    + resultExtras.getFrameNumber());
+                // Update tracker (increment counter) when it's not a partial result.
+                if (!quirkIsPartialResult) {
+                    mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(),
+                            /*error*/false);
                 }
-                return;
-            }
 
-            if (isClosed()) {
-                if (DEBUG) {
-                    Log.d(TAG,
-                            "camera is closed, early return at frame "
-                                    + resultExtras.getFrameNumber());
+                // Check if we have a listener for this
+                if (holder == null) {
+                    if (DEBUG) {
+                        Log.d(TAG,
+                                "holder is null, early return at frame "
+                                        + resultExtras.getFrameNumber());
+                    }
+                    return;
                 }
-                return;
-            }
 
-            final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
-
-
-            Runnable resultDispatch = null;
-
-            // Either send a partial result or the final capture completed result
-            if (quirkIsPartialResult) {
-                final CaptureResult resultAsCapture =
-                        new CaptureResult(result, request, requestId);
-
-                // Partial result
-                resultDispatch = new Runnable() {
-                    @Override
-                    public void run() {
-                        if (!CameraDeviceImpl.this.isClosed()){
-                            holder.getListener().onCapturePartial(
-                                CameraDeviceImpl.this,
-                                request,
-                                resultAsCapture);
-                        }
+                if (isClosed()) {
+                    if (DEBUG) {
+                        Log.d(TAG,
+                                "camera is closed, early return at frame "
+                                        + resultExtras.getFrameNumber());
                     }
-                };
-            } else {
-                final TotalCaptureResult resultAsCapture =
-                        new TotalCaptureResult(result, request, requestId);
+                    return;
+                }
 
-                // Final capture result
-                resultDispatch = new Runnable() {
-                    @Override
-                    public void run() {
-                        if (!CameraDeviceImpl.this.isClosed()){
-                            holder.getListener().onCaptureCompleted(
-                                CameraDeviceImpl.this,
-                                request,
-                                resultAsCapture);
+                final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
+
+
+                Runnable resultDispatch = null;
+
+                // Either send a partial result or the final capture completed result
+                if (quirkIsPartialResult) {
+                    final CaptureResult resultAsCapture =
+                            new CaptureResult(result, request, requestId);
+
+                    // Partial result
+                    resultDispatch = new Runnable() {
+                        @Override
+                        public void run() {
+                            if (!CameraDeviceImpl.this.isClosed()){
+                                holder.getListener().onCapturePartial(
+                                    CameraDeviceImpl.this,
+                                    request,
+                                    resultAsCapture);
+                            }
                         }
-                    }
-                };
-            }
+                    };
+                } else {
+                    final TotalCaptureResult resultAsCapture =
+                            new TotalCaptureResult(result, request, requestId);
 
-            holder.getHandler().post(resultDispatch);
+                    // Final capture result
+                    resultDispatch = new Runnable() {
+                        @Override
+                        public void run() {
+                            if (!CameraDeviceImpl.this.isClosed()){
+                                holder.getListener().onCaptureCompleted(
+                                    CameraDeviceImpl.this,
+                                    request,
+                                    resultAsCapture);
+                            }
+                        }
+                    };
+                }
 
-            // Fire onCaptureSequenceCompleted
-            if (!quirkIsPartialResult) {
-                checkAndFireSequenceComplete();
+                holder.getHandler().post(resultDispatch);
+
+                // Fire onCaptureSequenceCompleted
+                if (!quirkIsPartialResult) {
+                    checkAndFireSequenceComplete();
+                }
+
             }
         }
 
@@ -1101,10 +1145,9 @@
         }
     }
 
+    /** Whether the camera device has started to close (may not yet have finished) */
     private boolean isClosed() {
-        synchronized(mLock) {
-            return (mRemoteDevice == null);
-        }
+        return mClosing;
     }
 
     private CameraCharacteristics getCharacteristics() {
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index aa2f026..abd69c1 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -76,13 +76,11 @@
         }
         // TODO: Move open/init into LegacyCameraDevice thread when API is switched to async.
         Camera legacyCamera = Camera.openUninitialized();
-        int initErrors = legacyCamera.cameraInit(cameraId);
+        int initErrors = legacyCamera.cameraInitUnspecified(cameraId);
+
         // Check errors old HAL initialization
-        if (Camera.checkInitErrors(initErrors)) {
-            // TODO: Map over old camera error codes.  This likely involves improving the error
-            // reporting in the HAL1 connect path.
-            throw new CameraRuntimeException(CameraAccessException.CAMERA_DISCONNECTED);
-        }
+        CameraBinderDecorator.throwOnError(initErrors);
+
         LegacyCameraDevice device = new LegacyCameraDevice(cameraId, legacyCamera, callbacks);
         return new CameraDeviceUserShim(cameraId, device);
     }
@@ -169,6 +167,7 @@
             }
             int numSurfaces = mSurfaces.size();
             if (numSurfaces > 0) {
+                surfaces = new ArrayList<>();
                 for (int i = 0; i < numSurfaces; ++i) {
                     surfaces.add(mSurfaces.valueAt(i));
                 }
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index 3f9c6ed..50515a2 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -23,7 +23,6 @@
 import android.hardware.camera2.ICameraDeviceCallbacks;
 import android.hardware.camera2.utils.LongParcelable;
 import android.hardware.camera2.impl.CameraMetadataNative;
-import android.hardware.camera2.utils.CameraBinderDecorator;
 import android.hardware.camera2.utils.CameraRuntimeException;
 import android.os.ConditionVariable;
 import android.os.Handler;
@@ -34,7 +33,8 @@
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
+
+import static android.hardware.camera2.utils.CameraBinderDecorator.*;
 
 /**
  * This class emulates the functionality of a Camera2 device using a the old Camera class.
@@ -54,6 +54,7 @@
     private final int mCameraId;
     private final ICameraDeviceCallbacks mDeviceCallbacks;
     private final CameraDeviceState mDeviceState = new CameraDeviceState();
+    private List<Surface> mConfiguredSurfaces;
 
     private final ConditionVariable mIdle = new ConditionVariable(/*open*/true);
 
@@ -213,16 +214,35 @@
     /**
      * Configure the device with a set of output surfaces.
      *
+     * <p>Using empty or {@code null} {@code outputs} is the same as unconfiguring.</p>
+     *
+     * <p>Every surface in {@code outputs} must be non-{@code null}.</p>
+     *
      * @param outputs a list of surfaces to set.
-     * @return an error code for this binder operation, or {@link CameraBinderDecorator.NO_ERROR}
+     * @return an error code for this binder operation, or {@link NO_ERROR}
      *          on success.
      */
     public int configureOutputs(List<Surface> outputs) {
+        if (outputs != null) {
+            for (Surface output : outputs) {
+                if (output == null) {
+                    Log.e(TAG, "configureOutputs - null outputs are not allowed");
+                    return BAD_VALUE;
+                }
+            }
+        }
+
         int error = mDeviceState.setConfiguring();
-        if (error == CameraBinderDecorator.NO_ERROR) {
+        if (error == NO_ERROR) {
             mRequestThreadManager.configure(outputs);
             error = mDeviceState.setIdle();
         }
+
+        // TODO: May also want to check the surfaces more deeply (e.g. state, formats, sizes..)
+        if (error == NO_ERROR) {
+            mConfiguredSurfaces = outputs != null ? new ArrayList<>(outputs) : null;
+        }
+
         return error;
     }
 
@@ -239,7 +259,35 @@
      */
     public int submitRequestList(List<CaptureRequest> requestList, boolean repeating,
             /*out*/LongParcelable frameNumber) {
-        // TODO: validate request here
+        if (requestList == null || requestList.isEmpty()) {
+            Log.e(TAG, "submitRequestList - Empty/null requests are not allowed");
+            return BAD_VALUE;
+        }
+
+        // Make sure that there all requests have at least 1 surface; all surfaces are non-null
+        for (CaptureRequest request : requestList) {
+            if (request.getTargets().isEmpty()) {
+                Log.e(TAG, "submitRequestList - "
+                        + "Each request must have at least one Surface target");
+                return BAD_VALUE;
+            }
+
+            for (Surface surface : request.getTargets()) {
+                if (surface == null) {
+                    Log.e(TAG, "submitRequestList - Null Surface targets are not allowed");
+                    return BAD_VALUE;
+                } else if (mConfiguredSurfaces == null) {
+                    Log.e(TAG, "submitRequestList - must configure " +
+                            " device with valid surfaces before submitting requests");
+                    return INVALID_OPERATION;
+                } else if (!mConfiguredSurfaces.contains(surface)) {
+                    Log.e(TAG, "submitRequestList - cannot use a surface that wasn't configured");
+                    return BAD_VALUE;
+                }
+            }
+        }
+
+        // TODO: further validation of request here
         mIdle.close();
         return mRequestThreadManager.submitCaptureRequests(requestList, repeating,
                 frameNumber);
diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
index e675f87..048878c 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
@@ -21,12 +21,16 @@
 import android.hardware.Camera.CameraInfo;
 import android.hardware.Camera.Size;
 import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.params.StreamConfiguration;
 import android.hardware.camera2.params.StreamConfigurationDuration;
 import android.util.Log;
+import android.util.Range;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 import static com.android.internal.util.Preconditions.*;
@@ -44,6 +48,9 @@
     private static final int HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22;
     private static final int HAL_PIXEL_FORMAT_BLOB = 0x21;
 
+    // for metadata
+    private static final float LENS_INFO_MINIMUM_FOCUS_DISTANCE_FIXED_FOCUS = 0.0f;
+
     private static final long APPROXIMATE_CAPTURE_DELAY_MS = 200; // ms
     private static final long APPROXIMATE_SENSOR_AREA = (1 << 20); // 8mp
     private static final long APPROXIMATE_JPEG_ENCODE_TIME = 600; // ms
@@ -90,8 +97,12 @@
     }
 
     private static void mapCameraParameters(CameraMetadataNative m, Camera.Parameters p) {
+        m.set(INFO_SUPPORTED_HARDWARE_LEVEL, INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED);
         mapStreamConfigs(m, p);
-
+        mapAeConfig(m, p);
+        mapCapabilities(m, p);
+        mapLens(m, p);
+        mapFlash(m, p);
         // TODO: map other fields
     }
 
@@ -137,12 +148,85 @@
         StreamConfigurationDuration[] jpegStalls =
                 new StreamConfigurationDuration[jpegSizes.size()];
         int i = 0;
+        long longestStallDuration = -1;
         for (Camera.Size s : jpegSizes) {
+            long stallDuration =  calculateJpegStallDuration(s);
             jpegStalls[i++] = new StreamConfigurationDuration(HAL_PIXEL_FORMAT_BLOB, s.width,
-                    s.height, calculateJpegStallDuration(s));
+                    s.height, stallDuration);
+            if (longestStallDuration < stallDuration) {
+                longestStallDuration = stallDuration;
+            }
         }
         // Set stall durations for jpeg, other formats use default stall duration
         m.set(SCALER_AVAILABLE_STALL_DURATIONS, jpegStalls);
+
+        m.set(SENSOR_INFO_MAX_FRAME_DURATION, longestStallDuration);
+    }
+
+    @SuppressWarnings({"unchecked"})
+    private static void mapAeConfig(CameraMetadataNative m, Camera.Parameters p) {
+
+        List<int[]> fpsRanges = p.getSupportedPreviewFpsRange();
+        if (fpsRanges == null) {
+            throw new AssertionError("Supported FPS ranges cannot be null.");
+        }
+        int rangesSize = fpsRanges.size();
+        if (rangesSize <= 0) {
+            throw new AssertionError("At least one FPS range must be supported.");
+        }
+        Range<Integer>[] ranges = new Range[rangesSize];
+        int i = 0;
+        for (int[] r : fpsRanges) {
+            ranges[i++] = Range.create(r[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
+                    r[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
+        }
+        m.set(CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, ranges);
+
+        List<String> antiBandingModes = p.getSupportedAntibanding();
+        int antiBandingModesSize = antiBandingModes.size();
+        if (antiBandingModesSize > 0) {
+            int[] modes = new int[antiBandingModesSize];
+            int j = 0;
+            for (String mode : antiBandingModes) {
+                int convertedMode = convertAntiBandingMode(mode);
+                if (convertedMode == -1) {
+                    Log.w(TAG, "Antibanding mode " + ((mode == null) ? "NULL" : mode) +
+                            " not supported, skipping...");
+                } else {
+                    modes[j++] = convertedMode;
+                }
+            }
+            m.set(CONTROL_AE_AVAILABLE_ANTIBANDING_MODES, Arrays.copyOf(modes, j));
+        }
+    }
+
+    private static void mapCapabilities(CameraMetadataNative m, Camera.Parameters p) {
+        int[] capabilities = { REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE };
+        m.set(REQUEST_AVAILABLE_CAPABILITIES, capabilities);
+    }
+
+    private static void mapLens(CameraMetadataNative m, Camera.Parameters p) {
+        /*
+         *  We can tell if the lens is fixed focus;
+         *  but if it's not, we can't tell the minimum focus distance, so leave it null then.
+         */
+        if (p.getFocusMode() == Camera.Parameters.FOCUS_MODE_FIXED) {
+            m.set(LENS_INFO_MINIMUM_FOCUS_DISTANCE, LENS_INFO_MINIMUM_FOCUS_DISTANCE_FIXED_FOCUS);
+        }
+    }
+
+    private static void mapFlash(CameraMetadataNative m, Camera.Parameters p) {
+        boolean flashAvailable = false;
+        List<String> supportedFlashModes = p.getSupportedFlashModes();
+        if (supportedFlashModes != null) {
+            // If only 'OFF' is available, we don't really have flash support
+            if (!(supportedFlashModes.contains(Camera.Parameters.FLASH_MODE_OFF) &&
+                    supportedFlashModes.size() == 1)) {
+                flashAvailable = true;
+            }
+        }
+
+        m.set(FLASH_INFO_AVAILABLE, flashAvailable);
     }
 
     private static void appendStreamConfig(
@@ -155,6 +239,63 @@
     }
 
     /**
+     * Returns -1 if the anti-banding mode string is null, or not supported.
+     */
+    private static int convertAntiBandingMode(final String mode) {
+        if (mode == null) {
+            return -1;
+        }
+        switch(mode) {
+            case Camera.Parameters.ANTIBANDING_OFF: {
+                return CONTROL_AE_ANTIBANDING_MODE_OFF;
+            }
+            case Camera.Parameters.ANTIBANDING_50HZ: {
+                return CONTROL_AE_ANTIBANDING_MODE_50HZ;
+            }
+            case Camera.Parameters.ANTIBANDING_60HZ: {
+                return CONTROL_AE_ANTIBANDING_MODE_60HZ;
+            }
+            case Camera.Parameters.ANTIBANDING_AUTO: {
+                return CONTROL_AE_ANTIBANDING_MODE_AUTO;
+            }
+            default: {
+                return -1;
+            }
+        }
+    }
+
+    /**
+     * Returns null if the anti-banding mode enum is not supported.
+     */
+    private static String convertAntiBandingModeToLegacy(int mode) {
+        switch(mode) {
+            case CONTROL_AE_ANTIBANDING_MODE_OFF: {
+                return Camera.Parameters.ANTIBANDING_OFF;
+            }
+            case CONTROL_AE_ANTIBANDING_MODE_50HZ: {
+                return Camera.Parameters.ANTIBANDING_50HZ;
+            }
+            case CONTROL_AE_ANTIBANDING_MODE_60HZ: {
+                return Camera.Parameters.ANTIBANDING_60HZ;
+            }
+            case CONTROL_AE_ANTIBANDING_MODE_AUTO: {
+                return Camera.Parameters.ANTIBANDING_AUTO;
+            }
+            default: {
+                return null;
+            }
+        }
+    }
+
+
+    private static int[] convertAeFpsRangeToLegacy(Range<Integer> fpsRange) {
+        int[] legacyFps = new int[2];
+        legacyFps[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] = fpsRange.getLower();
+        legacyFps[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] = fpsRange.getUpper();
+        return legacyFps;
+    }
+
+    /**
      * Return the stall duration for a given output jpeg size in nanoseconds.
      *
      * <p>An 8mp image is chosen to have a stall duration of 0.8 seconds.</p>
@@ -166,4 +307,45 @@
                 APPROXIMATE_SENSOR_AREA; // 600ms stall for 8mp
         return baseDuration + area * stallPerArea;
     }
+
+    /**
+     * Generate capture result metadata from legacy camera parameters.
+     *
+     * @param params a {@link Camera.Parameters} object to generate metadata from.
+     * @param request the {@link CaptureRequest} used for this result.
+     * @param timestamp the timestamp to use for this result in nanoseconds.
+     * @return a {@link CameraMetadataNative} object containing result metadata.
+     */
+    public static CameraMetadataNative convertResultMetadata(Camera.Parameters params,
+                                                      CaptureRequest request,
+                                                      long timestamp) {
+        CameraMetadataNative result = new CameraMetadataNative();
+        result.set(CaptureResult.LENS_FOCAL_LENGTH, params.getFocalLength());
+        result.set(CaptureResult.SENSOR_TIMESTAMP, timestamp);
+
+        // TODO: Remaining result metadata tags conversions.
+        return result;
+    }
+
+    /**
+     * Set the legacy parameters using the request metadata.
+     *
+     * @param request a {@link CaptureRequest} object to generate parameters from.
+     * @param params the a {@link Camera.Parameters} to set parameters in.
+     */
+    public static void convertRequestMetadata(CaptureRequest request,
+            /*out*/Camera.Parameters params) {
+        Integer antiBandingMode = request.get(CaptureRequest.CONTROL_AE_ANTIBANDING_MODE);
+        if (antiBandingMode != null) {
+            String legacyMode = convertAntiBandingModeToLegacy(antiBandingMode);
+            if (legacyMode != null) params.setAntibanding(legacyMode);
+        }
+
+        Range<Integer> aeFpsRange = request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
+        if (aeFpsRange != null) {
+            int[] legacyFps = convertAeFpsRangeToLegacy(aeFpsRange);
+            params.setPreviewFpsRange(legacyFps[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
+                    legacyFps[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
+        }
+    }
 }
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index bf250a1..e0f3429 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -16,11 +16,9 @@
 
 package android.hardware.camera2.legacy;
 
-import android.graphics.ImageFormat;
 import android.graphics.SurfaceTexture;
 import android.hardware.Camera;
 import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.utils.LongParcelable;
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.os.ConditionVariable;
@@ -76,8 +74,8 @@
     private volatile RequestHolder mInFlightPreview;
     private volatile RequestHolder mInFlightJpeg;
 
-    private List<Surface> mPreviewOutputs = new ArrayList<Surface>();
-    private List<Surface> mCallbackOutputs = new ArrayList<Surface>();
+    private final List<Surface> mPreviewOutputs = new ArrayList<Surface>();
+    private final List<Surface> mCallbackOutputs = new ArrayList<Surface>();
     private GLThreadManager mGLThreadManager;
     private SurfaceTexture mPreviewTexture;
     private Camera.Parameters mParams;
@@ -85,6 +83,7 @@
     private Size mIntermediateBufferSize;
 
     private final RequestQueue mRequestQueue = new RequestQueue();
+    private CaptureRequest mLastRequest = null;
     private SurfaceTexture mDummyTexture;
     private Surface mDummySurface;
 
@@ -315,16 +314,17 @@
         mInFlightPreview = null;
         mInFlightJpeg = null;
 
-
-        for (Surface s : outputs) {
-            int format = LegacyCameraDevice.nativeDetectSurfaceType(s);
-            switch (format) {
-                case CameraMetadataNative.NATIVE_JPEG_FORMAT:
-                    mCallbackOutputs.add(s);
-                    break;
-                default:
-                    mPreviewOutputs.add(s);
-                    break;
+        if (outputs != null) {
+            for (Surface s : outputs) {
+                int format = LegacyCameraDevice.nativeDetectSurfaceType(s);
+                switch (format) {
+                    case CameraMetadataNative.NATIVE_JPEG_FORMAT:
+                        mCallbackOutputs.add(s);
+                        break;
+                    default:
+                        mPreviewOutputs.add(s);
+                        break;
+                }
             }
         }
         mParams = mCamera.getParameters();
@@ -370,8 +370,6 @@
             }
         }
 
-
-
         // TODO: Detect and optimize single-output paths here to skip stream teeing.
         if (mGLThreadManager == null) {
             mGLThreadManager = new GLThreadManager(mCameraId);
@@ -432,7 +430,6 @@
 
     private final Handler.Callback mRequestHandlerCb = new Handler.Callback() {
         private boolean mCleanup = false;
-        private List<RequestHolder> mRepeating = null;
 
         @SuppressWarnings("unchecked")
         @Override
@@ -447,7 +444,8 @@
             switch (msg.what) {
                 case MSG_CONFIGURE_OUTPUTS:
                     ConfigureHolder config = (ConfigureHolder) msg.obj;
-                    Log.i(TAG, "Configure outputs: " + config.surfaces.size() +
+                    int sizes = config.surfaces != null ? config.surfaces.size() : 0;
+                    Log.i(TAG, "Configure outputs: " + sizes +
                             " surfaces configured.");
                     try {
                         configureOutputs(config.surfaces);
@@ -475,6 +473,13 @@
                     List<RequestHolder> requests =
                             nextBurst.first.produceRequestHolders(nextBurst.second);
                     for (RequestHolder holder : requests) {
+                        CaptureRequest request = holder.getRequest();
+                        if (mLastRequest == null || mLastRequest != request) {
+                            mLastRequest = request;
+                            LegacyMetadataMapper.convertRequestMetadata(mLastRequest,
+                                /*out*/mParams);
+                            mCamera.setParameters(mParams);
+                        }
                         mDeviceState.setCaptureStart(holder);
                         long timestamp = 0;
                         try {
@@ -502,8 +507,8 @@
                             // TODO: err handling
                             throw new IOError(e);
                         }
-                        CameraMetadataNative result = convertResultMetadata(mParams,
-                                holder.getRequest(), timestamp);
+                        CameraMetadataNative result = LegacyMetadataMapper.convertResultMetadata(mParams,
+                                request, timestamp);
                         mDeviceState.setCaptureResult(holder, result);
                     }
                     if (DEBUG) {
@@ -527,17 +532,6 @@
         }
     };
 
-    private CameraMetadataNative convertResultMetadata(Camera.Parameters params,
-                                                       CaptureRequest request,
-                                                       long timestamp) {
-        CameraMetadataNative result = new CameraMetadataNative();
-        result.set(CaptureResult.LENS_FOCAL_LENGTH, params.getFocalLength());
-        result.set(CaptureResult.SENSOR_TIMESTAMP, timestamp);
-
-        // TODO: Remaining result metadata tags conversions.
-        return result;
-    }
-
     /**
      * Create a new RequestThreadManager.
      *
@@ -620,12 +614,14 @@
 
 
     /**
-     * Configure with the current output Surfaces.
+     * Configure with the current list of output Surfaces.
      *
      * <p>
      * This operation blocks until the configuration is complete.
      * </p>
      *
+     * <p>Using a {@code null} or empty {@code outputs} list is the equivalent of unconfiguring.</p>
+     *
      * @param outputs a {@link java.util.Collection} of outputs to configure.
      */
     public void configure(Collection<Surface> outputs) {
diff --git a/core/java/android/hardware/camera2/utils/CloseableLock.java b/core/java/android/hardware/camera2/utils/CloseableLock.java
new file mode 100644
index 0000000..af55055
--- /dev/null
+++ b/core/java/android/hardware/camera2/utils/CloseableLock.java
@@ -0,0 +1,330 @@
+/*
+ * 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.utils;
+
+import android.util.Log;
+
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Implement a shared/exclusive lock that can be closed.
+ *
+ * <p>A shared lock can be acquired if any other shared locks are also acquired. An
+ * exclusive lock acquire will block until all shared locks have been released.</p>
+ *
+ * <p>Locks are re-entrant; trying to acquire another lock (of the same type)
+ * while a lock is already held will immediately succeed.</p>
+ *
+ * <p>Acquiring to acquire a shared lock while holding an exclusive lock or vice versa is not
+ * supported; attempting it will throw an {@link IllegalStateException}.</p>
+ *
+ * <p>If the lock is closed, all future and current acquires will immediately return {@code null}.
+ * </p>
+ */
+public class CloseableLock implements AutoCloseable {
+
+    private static final boolean VERBOSE = false;
+
+    private final String TAG = "CloseableLock";
+    private final String mName;
+
+    private volatile boolean mClosed = false;
+
+    /** If an exclusive lock is acquired by some thread. */
+    private boolean mExclusive = false;
+    /**
+     * How many shared locks are acquired by any thread:
+     *
+     * <p>Reentrant locking increments this. If an exclusive lock is held,
+     * this value will stay at 0.</p>
+     */
+    private int mSharedLocks = 0;
+
+    private final ReentrantLock mLock = new ReentrantLock();
+    /** This condition automatically releases mLock when waiting; re-acquiring it after notify */
+    private final Condition mCondition = mLock.newCondition();
+
+    /** How many times the current thread is holding the lock */
+    private final ThreadLocal<Integer> mLockCount =
+        new ThreadLocal<Integer>() {
+            @Override protected Integer initialValue() {
+                return 0;
+            }
+        };
+
+    /**
+     * Helper class to release a lock at the end of a try-with-resources statement.
+     */
+    public class ScopedLock implements AutoCloseable {
+        private ScopedLock() {}
+
+        /** Release the lock with {@link CloseableLock#releaseLock}. */
+        @Override
+        public void close() {
+            releaseLock();
+        }
+    }
+
+    /**
+     * Create a new instance; starts out with 0 locks acquired.
+     */
+    public CloseableLock() {
+        mName = "";
+    }
+
+    /**
+     * Create a new instance; starts out with 0 locks acquired.
+     *
+     * @param name set an optional name for logging functionality
+     */
+    public CloseableLock(String name) {
+        mName = name;
+    }
+
+    /**
+     * Acquires the lock exclusively (blocking), marks it as closed, then releases the lock.
+     *
+     * <p>Marking a lock as closed will fail all further acquisition attempts;
+     * it will also immediately unblock all other threads currently trying to acquire a lock.</p>
+     *
+     * <p>This operation is idempotent; calling it more than once has no effect.</p>
+     *
+     * @throws IllegalStateException
+     *          if an attempt is made to {@code close} while this thread has a lock acquired
+     */
+    @Override
+    public void close() {
+        if (mClosed) {
+            log("close - already closed; ignoring");
+            return;
+        }
+
+        ScopedLock scoper = acquireExclusiveLock();
+        // Already closed by another thread?
+        if (scoper == null) {
+            return;
+        } else if (mLockCount.get() != 1) {
+            // Future: may want to add a #releaseAndClose to allow this.
+            throw new IllegalStateException(
+                    "Cannot close while one or more acquired locks are being held by this " +
+                     "thread; release all other locks first");
+        }
+
+        try {
+            mLock.lock();
+
+            mClosed = true;
+            mExclusive = false;
+            mSharedLocks = 0;
+            mLockCount.remove();
+
+            // Notify all threads that are waiting to unblock and return immediately
+            mCondition.signalAll();
+        } finally {
+            mLock.unlock();
+        }
+
+        log("close - completed");
+    }
+
+    /**
+     * Try to acquire the lock non-exclusively, blocking until the operation completes.
+     *
+     * <p>If the lock has already been closed, or being closed before this operation returns,
+     * the call will immediately return {@code false}.</p>
+     *
+     * <p>If other threads hold a non-exclusive lock (and the lock is not yet closed),
+     * this operation will return immediately. If another thread holds an exclusive lock,
+     * this thread will block until the exclusive lock has been released.</p>
+     *
+     * <p>This lock is re-entrant; acquiring more than one non-exclusive lock per thread is
+     * supported, and must be matched by an equal number of {@link #releaseLock} calls.</p>
+     *
+     * @return {@code ScopedLock} instance if the lock was acquired, or {@code null} if the lock
+     *         was already closed.
+     *
+     * @throws IllegalStateException if this thread is already holding an exclusive lock
+     */
+    public ScopedLock acquireLock() {
+
+        int ownedLocks;
+
+        try {
+            mLock.lock();
+
+            // Lock is already closed, all further acquisitions will fail
+            if (mClosed) {
+                log("acquire lock early aborted (already closed)");
+                return null;
+            }
+
+            ownedLocks = mLockCount.get();
+
+            // This thread is already holding an exclusive lock
+            if (mExclusive && ownedLocks > 0) {
+                throw new IllegalStateException(
+                        "Cannot acquire shared lock while holding exclusive lock");
+            }
+
+            // Is another thread holding the exclusive lock? Block until we can get in.
+            while (mExclusive) {
+                mCondition.awaitUninterruptibly();
+
+                // Did another thread #close while we were waiting? Unblock immediately.
+                if (mClosed) {
+                    log("acquire lock unblocked aborted (already closed)");
+                    return null;
+                }
+            }
+
+            mSharedLocks++;
+
+            ownedLocks = mLockCount.get() + 1;
+            mLockCount.set(ownedLocks);
+        } finally {
+            mLock.unlock();
+        }
+
+        log("acquired lock (local own count = " + ownedLocks + "");
+        return new ScopedLock();
+    }
+
+    /**
+     * Try to acquire the lock exclusively, blocking until all other threads release their locks.
+     *
+     * <p>If the lock has already been closed, or being closed before this operation returns,
+     * the call will immediately return {@code false}.</p>
+     *
+     * <p>If any other threads are holding a lock, this thread will block until all
+     * other locks are released.</p>
+     *
+     * <p>This lock is re-entrant; acquiring more than one exclusive lock per thread is supported,
+     * and must be matched by an equal number of {@link #releaseLock} calls.</p>
+     *
+     * @return {@code ScopedLock} instance if the lock was acquired, or {@code null} if the lock
+     *         was already closed.
+     *
+     * @throws IllegalStateException
+     *          if an attempt is made to acquire an exclusive lock while already holding a lock
+     */
+    public ScopedLock acquireExclusiveLock() {
+
+        int ownedLocks;
+
+        try {
+            mLock.lock();
+
+            // Lock is already closed, all further acquisitions will fail
+            if (mClosed) {
+                log("acquire exclusive lock early aborted (already closed)");
+                return null;
+            }
+
+            ownedLocks = mLockCount.get();
+
+            // This thread is already holding a shared lock
+            if (!mExclusive && ownedLocks > 0) {
+                throw new IllegalStateException(
+                        "Cannot acquire exclusive lock while holding shared lock");
+            }
+
+            /*
+             * Is another thread holding the lock? Block until we can get in.
+             *
+             * If we are already holding the lock, always let it through since
+             * we are just reentering the exclusive lock.
+             */
+            while (ownedLocks == 0 && (mExclusive || mSharedLocks > 0)) {
+                mCondition.awaitUninterruptibly();
+
+             // Did another thread #close while we were waiting? Unblock immediately.
+                if (mClosed) {
+                    log("acquire exclusive lock unblocked aborted (already closed)");
+                    return null;
+                }
+            }
+
+            mExclusive = true;
+
+            ownedLocks = mLockCount.get() + 1;
+            mLockCount.set(ownedLocks);
+        } finally {
+            mLock.unlock();
+        }
+
+        log("acquired exclusive lock (local own count = " + ownedLocks + "");
+        return new ScopedLock();
+    }
+
+    /**
+     * Release a single lock that was acquired.
+     *
+     * <p>Any other other that is blocked and trying to acquire a lock will get a chance
+     * to acquire the lock.</p>
+     *
+     * @throws IllegalStateException if no locks were acquired, or if the lock was already closed
+     */
+    public void releaseLock() {
+        if (mLockCount.get() <= 0) {
+            throw new IllegalStateException(
+                    "Cannot release lock that was not acquired by this thread");
+        }
+
+        int ownedLocks;
+
+        try {
+            mLock.lock();
+
+            // Lock is already closed, it couldn't have been acquired in the first place
+            if (mClosed) {
+                throw new IllegalStateException("Do not release after the lock has been closed");
+            }
+
+            if (!mExclusive) {
+                mSharedLocks--;
+            } else {
+                if (mSharedLocks != 0) {
+                    throw new AssertionError("Too many shared locks " + mSharedLocks);
+                }
+            }
+
+            ownedLocks = mLockCount.get() - 1;
+            mLockCount.set(ownedLocks);
+
+            if (ownedLocks == 0 && mExclusive) {
+                // Wake up any threads that might be waiting for the exclusive lock to be released
+                mExclusive = false;
+                mCondition.signalAll();
+            } else if (ownedLocks == 0 && mSharedLocks == 0) {
+                // Wake up any threads that might be trying to get the exclusive lock
+                mCondition.signalAll();
+            }
+        } finally {
+            mLock.unlock();
+        }
+
+        log("released lock (local lock count " + ownedLocks + ")");
+    }
+
+    private void log(String what) {
+        if (VERBOSE) {
+            Log.v(TAG + "[" + mName + "]", what);
+        }
+    }
+
+}
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 9218c11..53e87a6 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -25,6 +25,7 @@
 import android.nfc.INfcAdapterExtras;
 import android.nfc.INfcTag;
 import android.nfc.INfcCardEmulation;
+import android.nfc.INfcLockscreenDispatch;
 import android.os.Bundle;
 
 /**
@@ -52,4 +53,6 @@
 
     void setReaderMode (IBinder b, IAppCallback callback, int flags, in Bundle extras);
     void setP2pModes(int initatorModes, int targetModes);
+
+    void registerLockscreenDispatch(INfcLockscreenDispatch lockscreenDispatch, in int[] techList);
 }
diff --git a/core/java/android/nfc/INfcLockscreenDispatch.aidl b/core/java/android/nfc/INfcLockscreenDispatch.aidl
new file mode 100644
index 0000000..27e9a8c
--- /dev/null
+++ b/core/java/android/nfc/INfcLockscreenDispatch.aidl
@@ -0,0 +1,12 @@
+package android.nfc;
+
+import android.nfc.Tag;
+
+/**
+ * @hide
+ */
+interface INfcLockscreenDispatch {
+
+    boolean onTagDetected(in Tag tag);
+
+}
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index dd8e41c..8991066 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -380,6 +380,16 @@
         public Uri[] createBeamUris(NfcEvent event);
     }
 
+
+    /**
+     * A callback to be invoked when an application has registered for receiving
+     * tags at the lockscreen.
+     */
+    public interface NfcLockscreenDispatch {
+        public boolean onTagDetected(Tag tag);
+    }
+
+
     /**
      * Helper to check if this device has FEATURE_NFC, but without using
      * a context.
@@ -1417,6 +1427,23 @@
         }
     }
 
+    public boolean registerLockscreenDispatch(final NfcLockscreenDispatch lockscreenDispatch,
+                                           int[] techList) {
+        try {
+            sService.registerLockscreenDispatch(new INfcLockscreenDispatch.Stub() {
+                @Override
+                public boolean onTagDetected(Tag tag) throws RemoteException {
+                    return lockscreenDispatch.onTagDetected(tag);
+                }
+            }, techList);
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            return false;
+        }
+
+        return true;
+    }
+
     /**
      * @hide
      */
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 06c05ee..2a5e9fd 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4537,12 +4537,6 @@
         public static final String SMS_DEFAULT_APPLICATION = "sms_default_application";
 
         /**
-         * Specifies the package name currently configured to be the primary phone application
-         * @hide
-         */
-        public static final String PHONE_DEFAULT_APPLICATION = "phone_default_application";
-
-        /**
          * Name of a package that the current user has explicitly allowed to see all of that
          * user's notifications.
          *
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 5b8e854..9a8380d 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -570,8 +570,8 @@
 
         mTrackDrawable.getPadding(mTempRect);
 
-        final int maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth())
-                + mThumbTextPadding * 2;
+        final int maxTextWidth = mShowText ? Math.max(mOnLayout.getWidth(), mOffLayout.getWidth())
+                + mThumbTextPadding * 2 : 0;
         mThumbWidth = Math.max(maxTextWidth, mThumbDrawable.getIntrinsicWidth());
 
         final int switchWidth = Math.max(mSwitchMinWidth,
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 9998995..c139c9d 100644
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -258,16 +258,16 @@
 
 // can return NULL
 static ToColorProc ChooseToColorProc(const SkBitmap& src, bool isPremultiplied) {
-    switch (src.config()) {
-        case SkBitmap::kARGB_8888_Config:
+    switch (src.colorType()) {
+        case kN32_SkColorType:
             if (src.isOpaque()) return ToColor_S32_Opaque;
             return isPremultiplied ? ToColor_S32_Alpha : ToColor_S32_Raw;
-        case SkBitmap::kARGB_4444_Config:
+        case kARGB_4444_SkColorType:
             if (src.isOpaque()) return ToColor_S4444_Opaque;
             return isPremultiplied ? ToColor_S4444_Alpha : ToColor_S4444_Raw;
-        case SkBitmap::kRGB_565_Config:
+        case kRGB_565_SkColorType:
             return ToColor_S565;
-        case SkBitmap::kIndex8_Config:
+        case kIndex_8_SkColorType:
             if (src.getColorTable() == NULL) {
                 return NULL;
             }
@@ -291,7 +291,7 @@
 static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,
                               jint offset, jint stride, jint width, jint height,
                               jint configHandle, jboolean isMutable) {
-    SkBitmap::Config config = static_cast<SkBitmap::Config>(configHandle);
+    SkColorType colorType = SkBitmapConfigToColorType(static_cast<SkBitmap::Config>(configHandle));
     if (NULL != jColors) {
         size_t n = env->GetArrayLength(jColors);
         if (n < SkAbs32(stride) * (size_t)height) {
@@ -301,12 +301,12 @@
     }
 
     // ARGB_4444 is a deprecated format, convert automatically to 8888
-    if (config == SkBitmap::kARGB_4444_Config) {
-        config = SkBitmap::kARGB_8888_Config;
+    if (colorType == kARGB_4444_SkColorType) {
+        colorType = kN32_SkColorType;
     }
 
     SkBitmap bitmap;
-    bitmap.setConfig(config, width, height);
+    bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType));
 
     jbyteArray buff = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL);
     if (NULL == buff) {
@@ -515,28 +515,29 @@
 
     android::Parcel* p = android::parcelForJavaObject(env, parcel);
 
-    const bool              isMutable = p->readInt32() != 0;
-    const SkBitmap::Config  config = (SkBitmap::Config)p->readInt32();
-    const int               width = p->readInt32();
-    const int               height = p->readInt32();
-    const int               rowBytes = p->readInt32();
-    const int               density = p->readInt32();
+    const bool        isMutable = p->readInt32() != 0;
+    const SkColorType colorType = (SkColorType)p->readInt32();
+    const SkAlphaType alphaType = (SkAlphaType)p->readInt32();
+    const int         width = p->readInt32();
+    const int         height = p->readInt32();
+    const int         rowBytes = p->readInt32();
+    const int         density = p->readInt32();
 
-    if (SkBitmap::kARGB_8888_Config != config &&
-            SkBitmap::kRGB_565_Config != config &&
-            SkBitmap::kARGB_4444_Config != config &&
-            SkBitmap::kIndex8_Config != config &&
-            SkBitmap::kA8_Config != config) {
-        SkDebugf("Bitmap_createFromParcel unknown config: %d\n", config);
+    if (kN32_SkColorType != colorType &&
+            kRGB_565_SkColorType != colorType &&
+            kARGB_4444_SkColorType != colorType &&
+            kIndex_8_SkColorType != colorType &&
+            kAlpha_8_SkColorType != colorType) {
+        SkDebugf("Bitmap_createFromParcel unknown colortype: %d\n", colorType);
         return NULL;
     }
 
     SkBitmap* bitmap = new SkBitmap;
 
-    bitmap->setConfig(config, width, height, rowBytes);
+    bitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType), rowBytes);
 
     SkColorTable* ctable = NULL;
-    if (config == SkBitmap::kIndex8_Config) {
+    if (colorType == kIndex_8_SkColorType) {
         int count = p->readInt32();
         if (count > 0) {
             size_t size = count * sizeof(SkPMColor);
@@ -587,13 +588,14 @@
     android::Parcel* p = android::parcelForJavaObject(env, parcel);
 
     p->writeInt32(isMutable);
-    p->writeInt32(bitmap->config());
+    p->writeInt32(bitmap->colorType());
+    p->writeInt32(bitmap->alphaType());
     p->writeInt32(bitmap->width());
     p->writeInt32(bitmap->height());
     p->writeInt32(bitmap->rowBytes());
     p->writeInt32(density);
 
-    if (bitmap->config() == SkBitmap::kIndex8_Config) {
+    if (bitmap->colorType() == kIndex_8_SkColorType) {
         SkColorTable* ctable = bitmap->getColorTable();
         if (ctable != NULL) {
             int count = ctable->count();
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 5106f0d..86ed677 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -386,7 +386,7 @@
         // FIXME: If the alphaType is kUnpremul and the image has alpha, the
         // colors may not be correct, since Skia does not yet support drawing
         // to/from unpremultiplied bitmaps.
-        outputBitmap->setConfig(SkImageInfo::Make(scaledWidth, scaledHeight,
+        outputBitmap->setInfo(SkImageInfo::Make(scaledWidth, scaledHeight,
                 colorType, decodingBitmap.alphaType()));
         if (!outputBitmap->allocPixels(outputAllocator, NULL)) {
             return nullObjectReturn("allocation failed for scaled bitmap");
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index 935e3a0..9e09280 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -703,10 +703,11 @@
                                 jboolean hasAlpha, jlong paintHandle) {
         SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+        SkImageInfo info = SkImageInfo::Make(width, height,
+                               hasAlpha ? kN32_SkColorType : kRGB_565_SkColorType,
+                               kPremul_SkAlphaType);
         SkBitmap    bitmap;
-        bitmap.setConfig(hasAlpha ? SkBitmap::kARGB_8888_Config :
-                         SkBitmap::kRGB_565_Config, width, height);
-        if (!bitmap.allocPixels()) {
+        if (!bitmap.allocPixels(info)) {
             return;
         }
 
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 4c9feca..f8bab24 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -36,6 +36,11 @@
 
 using namespace android;
 
+enum {
+    // Keep up to date with Camera.java
+    CAMERA_HAL_API_VERSION_NORMAL_CONNECT = -2,
+};
+
 struct fields_t {
     jfieldID    context;
     jfieldID    facing;
@@ -475,8 +480,8 @@
     env->ReleaseStringChars(clientPackageName, rawClientName);
 
     sp<Camera> camera;
-    if (halVersion == ICameraService::CAMERA_HAL_API_VERSION_UNSPECIFIED) {
-        // Default path: hal version is unspecified, do normal camera open.
+    if (halVersion == CAMERA_HAL_API_VERSION_NORMAL_CONNECT) {
+        // Default path: hal version is don't care, do normal camera connect.
         camera = Camera::connect(cameraId, clientName,
                 Camera::USE_CALLING_UID);
     } else {
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index ee4c619..849531c 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -287,6 +287,8 @@
     env->CallStaticVoidMethod(clazz, env->GetStaticMethodID(clazz,
                               "errorCallbackFromNative","(I)V"),
                               check_AudioSystem_Command(err));
+
+    env->DeleteLocalRef(clazz);
 }
 
 static jint
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index dc1ea06..4362018 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -388,11 +388,11 @@
 static void android_view_GLES20Canvas_drawBitmapData(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jintArray colors, jint offset, jint stride,
         jfloat left, jfloat top, jint width, jint height, jboolean hasAlpha, jlong paintPtr) {
+    const SkImageInfo info = SkImageInfo::Make(width, height,
+                               hasAlpha ? kN32_SkColorType : kRGB_565_SkColorType,
+                               kPremul_SkAlphaType);
     SkBitmap* bitmap = new SkBitmap;
-    bitmap->setConfig(hasAlpha ? SkBitmap::kARGB_8888_Config : SkBitmap::kRGB_565_Config,
-            width, height);
-
-    if (!bitmap->allocPixels()) {
+    if (!bitmap->allocPixels(info)) {
         delete bitmap;
         return;
     }
diff --git a/core/jni/android_view_GraphicBuffer.cpp b/core/jni/android_view_GraphicBuffer.cpp
index 0210bd9..5ebed9c 100644
--- a/core/jni/android_view_GraphicBuffer.cpp
+++ b/core/jni/android_view_GraphicBuffer.cpp
@@ -142,16 +142,16 @@
 // Canvas management
 // ----------------------------------------------------------------------------
 
-static inline SkBitmap::Config convertPixelFormat(int32_t format) {
+static inline SkColorType convertPixelFormat(int32_t format) {
     switch (format) {
         case PIXEL_FORMAT_RGBA_8888:
-            return SkBitmap::kARGB_8888_Config;
+            return kN32_SkColorType;
         case PIXEL_FORMAT_RGBX_8888:
-            return SkBitmap::kARGB_8888_Config;
+            return kN32_SkColorType;
         case PIXEL_FORMAT_RGB_565:
-            return SkBitmap::kRGB_565_Config;
+            return kRGB_565_SkColorType;
         default:
-            return SkBitmap::kNo_Config;
+            return kUnknown_SkColorType;
     }
 }
 
@@ -188,8 +188,10 @@
     ssize_t bytesCount = buffer->getStride() * bytesPerPixel(buffer->getPixelFormat());
 
     SkBitmap bitmap;
-    bitmap.setConfig(convertPixelFormat(buffer->getPixelFormat()),
-            buffer->getWidth(), buffer->getHeight(), bytesCount);
+    bitmap.setInfo(SkImageInfo::Make(buffer->getWidth(), buffer->getHeight(),
+                                     convertPixelFormat(buffer->getPixelFormat()),
+                                     kPremul_SkAlphaType),
+                   bytesCount);
 
     if (buffer->getWidth() > 0 && buffer->getHeight() > 0) {
         bitmap.setPixels(bits);
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 3d14aaf..7018751 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -173,17 +173,17 @@
     return value;
 }
 
-static inline SkBitmap::Config convertPixelFormat(PixelFormat format) {
+static inline SkColorType convertPixelFormat(PixelFormat format) {
     /* note: if PIXEL_FORMAT_RGBX_8888 means that all alpha bytes are 0xFF, then
-        we can map to SkBitmap::kARGB_8888_Config, and optionally call
+        we can map to kN32_SkColorType, and optionally call
         bitmap.setAlphaType(kOpaque_SkAlphaType) on the resulting SkBitmap
         (as an accelerator)
     */
     switch (format) {
-    case PIXEL_FORMAT_RGBX_8888:    return SkBitmap::kARGB_8888_Config;
-    case PIXEL_FORMAT_RGBA_8888:    return SkBitmap::kARGB_8888_Config;
-    case PIXEL_FORMAT_RGB_565:      return SkBitmap::kRGB_565_Config;
-    default:                        return SkBitmap::kNo_Config;
+    case PIXEL_FORMAT_RGBX_8888:    return kN32_SkColorType;
+    case PIXEL_FORMAT_RGBA_8888:    return kN32_SkColorType;
+    case PIXEL_FORMAT_RGB_565:      return kRGB_565_SkColorType;
+    default:                        return kUnknown_SkColorType;
     }
 }
 
@@ -220,12 +220,16 @@
     // Associate a SkCanvas object to this surface
     env->SetIntField(canvasObj, gCanvasClassInfo.mSurfaceFormat, outBuffer.format);
 
+    SkImageInfo info = SkImageInfo::Make(outBuffer.width, outBuffer.height,
+                                         convertPixelFormat(outBuffer.format),
+                                         kPremul_SkAlphaType);
+    if (outBuffer.format == PIXEL_FORMAT_RGBX_8888) {
+        info.fAlphaType = kOpaque_SkAlphaType;
+    }
+
     SkBitmap bitmap;
     ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
-    bitmap.setConfig(convertPixelFormat(outBuffer.format), outBuffer.width, outBuffer.height, bpr);
-    if (outBuffer.format == PIXEL_FORMAT_RGBX_8888) {
-        bitmap.setAlphaType(kOpaque_SkAlphaType);
-    }
+    bitmap.setInfo(info, bpr);
     if (outBuffer.width > 0 && outBuffer.height > 0) {
         bitmap.setPixels(outBuffer.bits);
     } else {
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index cfc8eb8..9783e91 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -175,7 +175,7 @@
             screenshot->getStride() * android::bytesPerPixel(screenshot->getFormat());
 
     SkBitmap* bitmap = new SkBitmap();
-    bitmap->setConfig(screenshotInfo, (size_t)rowBytes);
+    bitmap->setInfo(screenshotInfo, (size_t)rowBytes);
     if (screenshotInfo.fWidth > 0 && screenshotInfo.fHeight > 0) {
         // takes ownership of ScreenshotClient
         SkMallocPixelRef* pixels = SkMallocPixelRef::NewWithProc(screenshotInfo,
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index c1ab515..5c04a78 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -73,17 +73,28 @@
 // Native layer
 // ----------------------------------------------------------------------------
 
-static inline SkBitmap::Config convertPixelFormat(int32_t format) {
-    switch (format) {
+// FIXME: consider exporting this to share (e.g. android_view_Surface.cpp)
+static inline SkImageInfo convertPixelFormat(const ANativeWindow_Buffer& buffer) {
+    SkImageInfo info;
+    info.fWidth = buffer.width;
+    info.fHeight = buffer.height;
+    switch (buffer.format) {
         case WINDOW_FORMAT_RGBA_8888:
-            return SkBitmap::kARGB_8888_Config;
+            info.fColorType = kN32_SkColorType;
+            info.fAlphaType = kPremul_SkAlphaType;
+            break;
         case WINDOW_FORMAT_RGBX_8888:
-            return SkBitmap::kARGB_8888_Config;
+            info.fColorType = kN32_SkColorType;
+            info.fAlphaType = kOpaque_SkAlphaType;
         case WINDOW_FORMAT_RGB_565:
-            return SkBitmap::kRGB_565_Config;
+            info.fColorType = kRGB_565_SkColorType;
+            info.fAlphaType = kOpaque_SkAlphaType;
         default:
-            return SkBitmap::kNo_Config;
+            info.fColorType = kUnknown_SkColorType;
+            info.fAlphaType = kIgnore_SkAlphaType;
+            break;
     }
+    return info;
 }
 
 /**
@@ -148,11 +159,7 @@
     ssize_t bytesCount = buffer.stride * bytesPerPixel(buffer.format);
 
     SkBitmap bitmap;
-    bitmap.setConfig(convertPixelFormat(buffer.format), buffer.width, buffer.height, bytesCount);
-
-    if (buffer.format == WINDOW_FORMAT_RGBX_8888) {
-        bitmap.setAlphaType(kOpaque_SkAlphaType);
-    }
+    bitmap.setInfo(convertPixelFormat(buffer), bytesCount);
 
     if (buffer.width > 0 && buffer.height > 0) {
         bitmap.setPixels(buffer.bits);
diff --git a/core/res/res/transition/explode.xml b/core/res/res/transition/explode.xml
new file mode 100644
index 0000000..fe22284
--- /dev/null
+++ b/core/res/res/transition/explode.xml
@@ -0,0 +1,16 @@
+<?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.
+-->
+<explode xmlns:android="http://schemas.android.com/apk/res/android"/>
diff --git a/core/res/res/transition/slide_bottom.xml b/core/res/res/transition/slide_bottom.xml
new file mode 100644
index 0000000..46dc0d6
--- /dev/null
+++ b/core/res/res/transition/slide_bottom.xml
@@ -0,0 +1,16 @@
+<?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.
+-->
+<slide xmlns:android="http://schemas.android.com/apk/res/android" android:slideEdge="bottom"/>
diff --git a/core/res/res/transition/slide_left.xml b/core/res/res/transition/slide_left.xml
new file mode 100644
index 0000000..997bd97
--- /dev/null
+++ b/core/res/res/transition/slide_left.xml
@@ -0,0 +1,16 @@
+<?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.
+-->
+<slide xmlns:android="http://schemas.android.com/apk/res/android" android:slideEdge="left"/>
diff --git a/core/res/res/transition/slide_right.xml b/core/res/res/transition/slide_right.xml
new file mode 100644
index 0000000..98f8f6a
--- /dev/null
+++ b/core/res/res/transition/slide_right.xml
@@ -0,0 +1,16 @@
+<?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.
+-->
+<slide xmlns:android="http://schemas.android.com/apk/res/android" android:slideEdge="right"/>
diff --git a/core/res/res/transition/slide_top.xml b/core/res/res/transition/slide_top.xml
new file mode 100644
index 0000000..07ab945
--- /dev/null
+++ b/core/res/res/transition/slide_top.xml
@@ -0,0 +1,16 @@
+<?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.
+-->
+<slide xmlns:android="http://schemas.android.com/apk/res/android" android:slideEdge="top"/>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 07a1e51..c3e4d94 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2461,5 +2461,26 @@
   <!-- An interpolator which accelerates fast and keeps accelerating until the end. -->
   <public type="interpolator" name="fast_out_linear_in" />
 
+  <!-- Used for Activity Transitions, this transition indicates that no Transition
+       should be used. -->
   <public type="transition" name="no_transition" id="0x010f0000"/>
+  <!-- A transition that moves and resizes a view -->
+  <public type="transition" name="move"/>
+  <!-- A transition that fades views in and out. -->
+  <public type="transition" name="fade"/>
+  <!-- A transition that moves views in or out of the scene to or from the edges when
+       a view visibility changes. -->
+  <public type="transition" name="explode"/>
+  <!-- A transition that moves views in or out of the scene to or from the bottom edge when
+       a view visibility changes. -->
+  <public type="transition" name="slide_bottom"/>
+  <!-- A transition that moves views in or out of the scene to or from the top edge when
+       a view visibility changes. -->
+  <public type="transition" name="slide_top"/>
+  <!-- A transition that moves views in or out of the scene to or from the right edge when
+       a view visibility changes. -->
+  <public type="transition" name="slide_right"/>
+  <!-- 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"/>
 </resources>
diff --git a/docs/html/preview/images/l-dev-prev.png b/docs/html/preview/images/l-dev-prev.png
index 95bad8c..eae6ede 100644
--- a/docs/html/preview/images/l-dev-prev.png
+++ b/docs/html/preview/images/l-dev-prev.png
Binary files differ
diff --git a/docs/html/preview/index.html b/docs/html/preview/index.html
index 368db84..d4610b5 100644
--- a/docs/html/preview/index.html
+++ b/docs/html/preview/index.html
@@ -163,8 +163,8 @@
               platform officially launches.
             </div>
 
-            <img src="/preview/images/l-dev-prev.png" style=" margin:10px 0 0 100px" width="700px"/>
-            <div class="col-6" style="margin-left:630px; margin-top:-40px">
+            <img src="/preview/images/l-dev-prev.png" style=" margin:0px 0 0 40px" width="860px"/>
+            <div class="col-6" style="margin-left:660px; margin-top:-105px">
    <a href="/preview/setup-sdk.html" class="landing-button landing-secondary" style="position:absolute;z-index:100;float:right;margin-top: 0px; background-color:#09c">Get Started</a><!--
             <p>Set up your environment and check out all the docs to get up and running.</p>-->
              
@@ -176,7 +176,7 @@
 
 
 
-<div class="landing-section landing-gray-background" style="margin-top:-80px; padding-bottom:20px">
+<div class="landing-section landing-gray-background" style="margin-top:-135px; padding-bottom:20px">
         <div class="wrap">
           <div class="cols">
 <div class="landing-body" style="margin-top:-80px" >
@@ -186,14 +186,14 @@
                 <p>A New UI Design</p>
                 <p class="landing-small">
                   Create a consistent experience across mobile and the web with
-                   material design, the new Google-wide standard.
+                   <b>material design</b>, the new Google-wide standard.
                 </p>
                 <p class="landing-small">
                   <a href="/preview/material/index.html">Learn about material</a>
                 </p>
               </div>
               <div class="col-4">
-                <p>A Rehauled Runtime</p>
+                <p>A New Runtime</p>
                 <p class="landing-small">
                   Test your apps and get them ready for <b>ART</b> (<b>A</b>ndroid <b>R</b>un<b>t</b>ime),
                   the default runtime in the next release.
@@ -205,21 +205,21 @@
               <div class="col-4">
                 <p style="width:230px">Enhanced Notifications</p>
                 <p class="landing-small">
-                   Get more control over where notifications appear,
-                   how they look, and automatic syncing to non-handheld devices.
+                   Get control over where notifications appear,
+                   how they look, and how they sync to non-handheld devices.
                 </p>
                 <p class="landing-small">
-                  <a href="/preview/api-overview.html#UI">Learn more</a>
+                  <a href="/preview/api-overview.html#UI">Learn about notifications</a>
                 </p>
               </div>
               <div class="col-4">
-                <p>Project Volta</p>
+                <p>Increased Efficiency</p>
                 <p class="landing-small">
-                  We've tuned the platform to be more energy efficient and
+                  <b>Project Volta</b> is our effort to make the platform energy efficient and
                   to give you more control over resource usage.
                 </p>
                 <p class="landing-small">
-                  <a href="/preview/api-overview.html#Power">Learn more</a>
+                  <a href="/preview/api-overview.html#Power">Learn about Project Volta</a>
                 </p>
               </div>
             </div>
diff --git a/libs/hwui/Interpolator.cpp b/libs/hwui/Interpolator.cpp
index 1f84b86..fc0e8a0 100644
--- a/libs/hwui/Interpolator.cpp
+++ b/libs/hwui/Interpolator.cpp
@@ -112,6 +112,10 @@
     int i1 = (int) ipart;
     int i2 = MathUtils::min(i1 + 1, mSize - 1);
 
+    LOG_ALWAYS_FATAL_IF(i1 < 0 || i2 < 0, "negatives in interpolation!"
+            " i1=%d, i2=%d, input=%f, lutpos=%f, size=%zu, values=%p, ipart=%f, weight=%f",
+            i1, i2, input, lutpos, mSize, mValues, ipart, weight);
+
     float v1 = mValues[i1];
     float v2 = mValues[i2];
 
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 9dd5aa5..97eb583 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -104,8 +104,7 @@
 }
 
 static void initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height) {
-    bitmap.setConfig(SkBitmap::kA8_Config, width, height);
-    bitmap.allocPixels();
+    bitmap.allocPixels(SkImageInfo::MakeA8(width, height));
     bitmap.eraseColor(0);
 }
 
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 1001cae0..9212d0a 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -333,8 +333,7 @@
 void TextureCache::uploadLoFiTexture(bool resize, const SkBitmap* bitmap,
         uint32_t width, uint32_t height) {
     SkBitmap rgbaBitmap;
-    rgbaBitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height, 0, bitmap->alphaType());
-    rgbaBitmap.allocPixels();
+    rgbaBitmap.allocPixels(SkImageInfo::MakeN32(width, height, bitmap->alphaType()));
     rgbaBitmap.eraseColor(0);
 
     SkCanvas canvas(rgbaBitmap);
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index 3f6ccc9..063383b 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -206,9 +206,8 @@
             } else {
                 SkBitmap surfaceBitmap;
                 ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
-                surfaceBitmap.setConfig(SkBitmap::kARGB_8888_Config,
-                        outBuffer.width, outBuffer.height, bpr);
-                surfaceBitmap.setPixels(outBuffer.bits);
+                surfaceBitmap.installPixels(SkImageInfo::MakeN32Premul(outBuffer.width, outBuffer.height),
+                                            outBuffer.bits, bpr);
 
                 SkCanvas surfaceCanvas(surfaceBitmap);
 
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index d35225a..fb19242 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1254,11 +1254,6 @@
      * call {@link #stopBluetoothSco()} to clear the request and turn down the bluetooth connection.
      * <p>Even if a SCO connection is established, the following restrictions apply on audio
      * output streams so that they can be routed to SCO headset:
-     * <p>NOTE: up to and including API version
-     * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}, this method initiates a virtual
-     * voice call to the bluetooth headset.
-     * After API version {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} only a raw SCO audio
-     * connection is established.
      * <ul>
      *   <li> the stream type must be {@link #STREAM_VOICE_CALL} </li>
      *   <li> the format must be mono </li>
@@ -1274,6 +1269,11 @@
      * it will be ignored. Similarly, if a call is received or sent while an application
      * is using the SCO connection, the connection will be lost for the application and NOT
      * returned automatically when the call ends.
+     * <p>NOTE: up to and including API version
+     * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}, this method initiates a virtual
+     * voice call to the bluetooth headset.
+     * After API version {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} only a raw SCO audio
+     * connection is established.
      * @see #stopBluetoothSco()
      * @see #ACTION_SCO_AUDIO_STATE_UPDATED
      */
@@ -1287,13 +1287,38 @@
     }
 
     /**
+     * Start bluetooth SCO audio connection in virtual call mode.
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}.
+     * <p>Similar to {@link #startBluetoothSco()} with explicit selection of virtual call mode.
+     * Telephony and communication applications (VoIP, Video Chat) should preferably select
+     * virtual call mode.
+     * Applications using voice input for search or commands should first try raw audio connection
+     * with {@link #startBluetoothSco()} and fall back to startBluetoothScoVirtualCall() in case of
+     * failure.
+     * @see #startBluetoothSco()
+     * @see #stopBluetoothSco()
+     * @see #ACTION_SCO_AUDIO_STATE_UPDATED
+     */
+    public void startBluetoothScoVirtualCall() {
+        IAudioService service = getService();
+        try {
+            service.startBluetoothScoVirtualCall(mICallBack);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Dead object in startBluetoothScoVirtualCall", e);
+        }
+    }
+
+    /**
      * Stop bluetooth SCO audio connection.
      * <p>Requires Permission:
      *   {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}.
      * <p>This method must be called by applications having requested the use of
-     * bluetooth SCO audio with {@link #startBluetoothSco()}
-     * when finished with the SCO connection or if connection fails.
+     * bluetooth SCO audio with {@link #startBluetoothSco()} or
+     * {@link #startBluetoothScoVirtualCall()} when finished with the SCO connection or
+     * if connection fails.
      * @see #startBluetoothSco()
+     * @see #startBluetoothScoVirtualCall()
      */
     public void stopBluetoothSco(){
         IAudioService service = getService();
@@ -2169,48 +2194,8 @@
             Log.e(TAG, "Cannot call registerMediaButtonIntent() with a null parameter");
             return;
         }
-        IAudioService service = getService();
-        try {
-            // pi != null, this is currently still needed to support across
-            // reboot launching of the last app.
-            service.registerMediaButtonIntent(pi, eventReceiver,
-                    eventReceiver == null ? mToken : null);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Dead object in registerMediaButtonIntent"+e);
-        }
         MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
-        helper.addMediaButtonListener(pi, mContext);
-    }
-
-    /**
-     * @hide
-     * Used internally by telephony package to register an intent receiver for ACTION_MEDIA_BUTTON.
-     * @param eventReceiver the component that will receive the media button key events,
-     *          no-op if eventReceiver is null
-     */
-    public void registerMediaButtonEventReceiverForCalls(ComponentName eventReceiver) {
-        if (eventReceiver == null) {
-            return;
-        }
-        IAudioService service = getService();
-        try {
-            // eventReceiver != null
-            service.registerMediaButtonEventReceiverForCalls(eventReceiver);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Dead object in registerMediaButtonEventReceiverForCalls", e);
-        }
-    }
-
-    /**
-     * @hide
-     */
-    public void unregisterMediaButtonEventReceiverForCalls() {
-        IAudioService service = getService();
-        try {
-            service.unregisterMediaButtonEventReceiverForCalls();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Dead object in unregisterMediaButtonEventReceiverForCalls", e);
-        }
+        helper.addMediaButtonListener(pi, eventReceiver, mContext);
     }
 
     /**
@@ -2247,12 +2232,6 @@
      * @hide
      */
     public void unregisterMediaButtonIntent(PendingIntent pi) {
-        IAudioService service = getService();
-        try {
-            service.unregisterMediaButtonIntent(pi);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Dead object in unregisterMediaButtonIntent"+e);
-        }
         MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
         helper.removeMediaButtonListener(pi);
     }
@@ -2418,46 +2397,6 @@
     }
 
     /**
-     * @hide
-     * Request the user of a RemoteControlClient to seek to the given playback position.
-     * @param generationId the RemoteControlClient generation counter for which this request is
-     *         issued. Requests for an older generation than current one will be ignored.
-     * @param timeMs the time in ms to seek to, must be positive.
-     */
-    public void setRemoteControlClientPlaybackPosition(int generationId, long timeMs) {
-        if (timeMs < 0) {
-            return;
-        }
-        IAudioService service = getService();
-        try {
-            service.setRemoteControlClientPlaybackPosition(generationId, timeMs);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Dead object in setRccPlaybackPosition("+ generationId + ", "
-                    + timeMs + ")", e);
-        }
-    }
-
-    /**
-     * @hide
-     * Notify the user of a RemoteControlClient that it should update its metadata with the
-     * new value for the given key.
-     * @param generationId the RemoteControlClient generation counter for which this request is
-     *         issued. Requests for an older generation than current one will be ignored.
-     * @param key the metadata key for which a new value exists
-     * @param value the new metadata value
-     */
-    public void updateRemoteControlClientMetadata(int generationId, int key,
-            Rating value) {
-        IAudioService service = getService();
-        try {
-            service.updateRemoteControlClientMetadata(generationId, key, value);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Dead object in updateRemoteControlClientMetadata("+ generationId + ", "
-                    + key +", " + value + ")", e);
-        }
-    }
-
-    /**
      *  @hide
      *  Reload audio settings. This method is called by Settings backup
      *  agent when audio settings are restored and causes the AudioService
@@ -2873,12 +2812,9 @@
      * @hide
      */
     public int getRemoteStreamVolume() {
-        try {
-            return getService().getRemoteStreamVolume();
-        } catch (RemoteException e) {
-            Log.w(TAG, "Error getting remote stream volume", e);
-            return 0;
-        }
+        // TODO STOPSHIP switch callers to use media sessions instead
+        Log.e(TAG, "Need to implement new Remote Volume!");
+        return 0;
     }
 
     /**
@@ -2886,12 +2822,9 @@
      * @hide
      */
     public int getRemoteStreamMaxVolume() {
-        try {
-            return getService().getRemoteStreamMaxVolume();
-        } catch (RemoteException e) {
-            Log.w(TAG, "Error getting remote stream max volume", e);
-            return 0;
-        }
+        // TODO STOPSHIP switch callers to use media sessions instead
+        Log.e(TAG, "Need to implement new Remote Volume!");
+        return 0;
     }
 
     /**
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 48479a4..0c224a6 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -2056,7 +2056,19 @@
     }
 
     /** @see AudioManager#startBluetoothSco() */
-    public void startBluetoothSco(IBinder cb, int targetSdkVersion){
+    public void startBluetoothSco(IBinder cb, int targetSdkVersion) {
+        int scoAudioMode =
+                (targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2) ?
+                        SCO_MODE_VIRTUAL_CALL : SCO_MODE_RAW;
+        startBluetoothScoInt(cb, scoAudioMode);
+    }
+
+    /** @see AudioManager#startBluetoothScoVirtualCall() */
+    public void startBluetoothScoVirtualCall(IBinder cb) {
+        startBluetoothScoInt(cb, SCO_MODE_VIRTUAL_CALL);
+    }
+
+    void startBluetoothScoInt(IBinder cb, int scoAudioMode){
         if (!checkAudioSettingsPermission("startBluetoothSco()") ||
                 !mSystemReady) {
             return;
@@ -2068,7 +2080,7 @@
         // The caller identity must be cleared after getScoClient() because it is needed if a new
         // client is created.
         final long ident = Binder.clearCallingIdentity();
-        client.incCount(targetSdkVersion);
+        client.incCount(scoAudioMode);
         Binder.restoreCallingIdentity(ident);
     }
 
@@ -2114,9 +2126,9 @@
             }
         }
 
-        public void incCount(int targetSdkVersion) {
+        public void incCount(int scoAudioMode) {
             synchronized(mScoClients) {
-                requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, targetSdkVersion);
+                requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
                 if (mStartcount == 0) {
                     try {
                         mCb.linkToDeath(this, 0);
@@ -2186,7 +2198,7 @@
             }
         }
 
-        private void requestScoState(int state, int targetSdkVersion) {
+        private void requestScoState(int state, int scoAudioMode) {
             checkScoAudioState();
             if (totalCount() == 0) {
                 if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
@@ -2201,9 +2213,7 @@
                                 (mScoAudioState == SCO_STATE_INACTIVE ||
                                  mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
                             if (mScoAudioState == SCO_STATE_INACTIVE) {
-                                mScoAudioMode =
-                                        (targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2) ?
-                                                SCO_MODE_VIRTUAL_CALL : SCO_MODE_RAW;
+                                mScoAudioMode = scoAudioMode;
                                 if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
                                     boolean status;
                                     if (mScoAudioMode == SCO_MODE_RAW) {
@@ -4402,68 +4412,12 @@
         mMediaFocusControl.remoteControlDisplayWantsPlaybackPositionSync(rcd, wantsSync);
     }
 
-    public void registerMediaButtonEventReceiverForCalls(ComponentName c) {
-        mMediaFocusControl.registerMediaButtonEventReceiverForCalls(c);
-    }
-
-    public void unregisterMediaButtonEventReceiverForCalls() {
-        mMediaFocusControl.unregisterMediaButtonEventReceiverForCalls();
-    }
-
-    public void registerMediaButtonIntent(PendingIntent pi, ComponentName c, IBinder token) {
-        mMediaFocusControl.registerMediaButtonIntent(pi, c, token);
-    }
-
-    public void unregisterMediaButtonIntent(PendingIntent pi) {
-        mMediaFocusControl.unregisterMediaButtonIntent(pi);
-    }
-
-    public int registerRemoteControlClient(PendingIntent mediaIntent,
-            IRemoteControlClient rcClient, String callingPckg) {
-        return mMediaFocusControl.registerRemoteControlClient(mediaIntent, rcClient, callingPckg);
-    }
-
-    public void unregisterRemoteControlClient(PendingIntent mediaIntent,
-            IRemoteControlClient rcClient) {
-        mMediaFocusControl.unregisterRemoteControlClient(mediaIntent, rcClient);
-    }
-
-    public void setRemoteControlClientPlaybackPosition(int generationId, long timeMs) {
-        mMediaFocusControl.setRemoteControlClientPlaybackPosition(generationId, timeMs);
-    }
-
-    public void updateRemoteControlClientMetadata(int generationId, int key, Rating value) {
-        mMediaFocusControl.updateRemoteControlClientMetadata(generationId, key, value);
-    }
-
-    public void registerRemoteVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
-        mMediaFocusControl.registerRemoteVolumeObserverForRcc(rccId, rvo);
-    }
-
-    @Override
-    public int getRemoteStreamVolume() {
-        return mMediaFocusControl.getRemoteStreamVolume();
-    }
-
-    @Override
-    public int getRemoteStreamMaxVolume() {
-        return mMediaFocusControl.getRemoteStreamMaxVolume();
-    }
-
     @Override
     public void setRemoteStreamVolume(int index) {
         enforceSelfOrSystemUI("set the remote stream volume");
         mMediaFocusControl.setRemoteStreamVolume(index);
     }
 
-    public void setPlaybackStateForRcc(int rccId, int state, long timeMs, float speed) {
-        mMediaFocusControl.setPlaybackStateForRcc(rccId, state, timeMs, speed);
-    }
-
-    public void setPlaybackInfoForRcc(int rccId, int what, int value) {
-        mMediaFocusControl.setPlaybackInfoForRcc(rccId, what, value);
-    }
-
     //==========================================================================================
     // Audio Focus
     //==========================================================================================
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 169631e..4dcdd19 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -122,12 +122,6 @@
 
     int getCurrentAudioFocus();
 
-           void registerMediaButtonIntent(in PendingIntent pi, in ComponentName c, IBinder token);
-    oneway void unregisterMediaButtonIntent(in PendingIntent pi);
-
-    oneway void registerMediaButtonEventReceiverForCalls(in ComponentName c);
-    oneway void unregisterMediaButtonEventReceiverForCalls();
-
     /**
      * Register an IRemoteControlDisplay.
      * Success of registration is subject to a check on
@@ -180,43 +174,9 @@
      */
     oneway void remoteControlDisplayWantsPlaybackPositionSync(in IRemoteControlDisplay rcd,
             boolean wantsSync);
-    /**
-     * Request the user of a RemoteControlClient to seek to the given playback position.
-     * @param generationId the RemoteControlClient generation counter for which this request is
-     *         issued. Requests for an older generation than current one will be ignored.
-     * @param timeMs the time in ms to seek to, must be positive.
-     */
-     void setRemoteControlClientPlaybackPosition(int generationId, long timeMs);
-     /**
-      * Notify the user of a RemoteControlClient that it should update its metadata with the
-      * new value for the given key.
-      * @param generationId the RemoteControlClient generation counter for which this request is
-      *         issued. Requests for an older generation than current one will be ignored.
-      * @param key the metadata key for which a new value exists
-      * @param value the new metadata value
-      */
-     void updateRemoteControlClientMetadata(int generationId, int key, in Rating value);
-
-    /**
-     * Do not use directly, use instead
-     *     {@link android.media.AudioManager#registerRemoteControlClient(RemoteControlClient)}
-     */
-    int registerRemoteControlClient(in PendingIntent mediaIntent,
-            in IRemoteControlClient rcClient, in String callingPackageName);
-    /**
-     * Do not use directly, use instead
-     *     {@link android.media.AudioManager#unregisterRemoteControlClient(RemoteControlClient)}
-     */
-    oneway void unregisterRemoteControlClient(in PendingIntent mediaIntent,
-            in IRemoteControlClient rcClient);
-
-    oneway void setPlaybackInfoForRcc(int rccId, int what, int value);
-    void setPlaybackStateForRcc(int rccId, int state, long timeMs, float speed);
-           int  getRemoteStreamMaxVolume();
-           int  getRemoteStreamVolume();
-    oneway void registerRemoteVolumeObserverForRcc(int rccId, in IRemoteVolumeObserver rvo);
 
     void startBluetoothSco(IBinder cb, int targetSdkVersion);
+    void startBluetoothScoVirtualCall(IBinder cb);
     void stopBluetoothSco(IBinder cb);
 
     void forceVolumeControlStream(int streamType, IBinder cb);
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 440653a..6559bc5 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -181,6 +181,27 @@
     }
 
     /**
+     * Thrown when an unrecoverable failure occurs during a MediaDrm operation.
+     * Extends java.lang.IllegalStateException with the addition of an error
+     * code that may be useful in diagnosing the failure.
+     */
+    public static final class MediaDrmStateException extends java.lang.IllegalStateException {
+        private final int mErrorCode;
+
+        public MediaDrmStateException(int errorCode, String detailMessage) {
+            super(detailMessage);
+            mErrorCode = errorCode;
+        }
+
+        /**
+         * Retrieve the associated error code
+         */
+        public int getErrorCode() {
+            return mErrorCode;
+        }
+    }
+
+    /**
      * Register a callback to be invoked when an event occurs
      *
      * @param listener the callback that will be run
diff --git a/media/java/android/media/MediaFocusControl.java b/media/java/android/media/MediaFocusControl.java
index 1c73c05..a4a7c4e 100644
--- a/media/java/android/media/MediaFocusControl.java
+++ b/media/java/android/media/MediaFocusControl.java
@@ -379,32 +379,11 @@
                     onReevaluateRemote();
                     break;
 
-                case MSG_RCC_NEW_PLAYBACK_INFO:
-                    onNewPlaybackInfoForRcc(msg.arg1 /* rccId */, msg.arg2 /* key */,
-                            ((Integer)msg.obj).intValue() /* value */);
-                    break;
-
                 case MSG_RCC_NEW_VOLUME_OBS:
                     onRegisterVolumeObserverForRcc(msg.arg1 /* rccId */,
                             (IRemoteVolumeObserver)msg.obj /* rvo */);
                     break;
 
-                case MSG_RCC_NEW_PLAYBACK_STATE:
-                    onNewPlaybackStateForRcc(msg.arg1 /* rccId */,
-                            msg.arg2 /* state */,
-                            (PlayerRecord.RccPlaybackState)msg.obj /* newState */);
-                    break;
-
-                case MSG_RCC_SEEK_REQUEST:
-                    onSetRemoteControlClientPlaybackPosition(
-                            msg.arg1 /* generationId */, ((Long)msg.obj).longValue() /* timeMs */);
-                    break;
-
-                case MSG_RCC_UPDATE_METADATA:
-                    onUpdateRemoteControlClientMetadata(msg.arg1 /*genId*/, msg.arg2 /*key*/,
-                            (Rating) msg.obj /* value */);
-                    break;
-
                 case MSG_RCDISPLAY_INIT_INFO:
                     // msg.obj is guaranteed to be non null
                     onRcDisplayInitInfo((IRemoteControlDisplay)msg.obj /*newRcd*/,
@@ -2003,217 +1982,6 @@
         }
     }
 
-    protected void setRemoteControlClientPlaybackPosition(int generationId, long timeMs) {
-        // ignore position change requests if invalid generation ID
-        synchronized(mPRStack) {
-            synchronized(mCurrentRcLock) {
-                if (mCurrentRcClientGen != generationId) {
-                    return;
-                }
-            }
-        }
-        // discard any unprocessed seek request in the message queue, and replace with latest
-        sendMsg(mEventHandler, MSG_RCC_SEEK_REQUEST, SENDMSG_REPLACE, generationId /* arg1 */,
-                0 /* arg2 ignored*/, new Long(timeMs) /* obj */, 0 /* delay */);
-    }
-
-    private void onSetRemoteControlClientPlaybackPosition(int generationId, long timeMs) {
-        if(DEBUG_RC) Log.d(TAG, "onSetRemoteControlClientPlaybackPosition(genId=" + generationId +
-                ", timeMs=" + timeMs + ")");
-        synchronized(mPRStack) {
-            synchronized(mCurrentRcLock) {
-                if ((mCurrentRcClient != null) && (mCurrentRcClientGen == generationId)) {
-                    // tell the current client to seek to the requested location
-                    try {
-                        mCurrentRcClient.seekTo(generationId, timeMs);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Current valid remote client is dead: "+e);
-                        mCurrentRcClient = null;
-                    }
-                }
-            }
-        }
-    }
-
-    protected void updateRemoteControlClientMetadata(int genId, int key, Rating value) {
-        sendMsg(mEventHandler, MSG_RCC_UPDATE_METADATA, SENDMSG_QUEUE,
-                genId /* arg1 */, key /* arg2 */, value /* obj */, 0 /* delay */);
-    }
-
-    private void onUpdateRemoteControlClientMetadata(int genId, int key, Rating value) {
-        if(DEBUG_RC) Log.d(TAG, "onUpdateRemoteControlClientMetadata(genId=" + genId +
-                ", what=" + key + ",rating=" + value + ")");
-        synchronized(mPRStack) {
-            synchronized(mCurrentRcLock) {
-                if ((mCurrentRcClient != null) && (mCurrentRcClientGen == genId)) {
-                    try {
-                        switch (key) {
-                            case MediaMetadataEditor.RATING_KEY_BY_USER:
-                                mCurrentRcClient.updateMetadata(genId, key, value);
-                                break;
-                            default:
-                                Log.e(TAG, "unhandled metadata key " + key + " update for RCC "
-                                        + genId);
-                                break;
-                        }
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Current valid remote client is dead", e);
-                        mCurrentRcClient = null;
-                    }
-                }
-            }
-        }
-    }
-
-    protected void setPlaybackInfoForRcc(int rccId, int what, int value) {
-        sendMsg(mEventHandler, MSG_RCC_NEW_PLAYBACK_INFO, SENDMSG_QUEUE,
-                rccId /* arg1 */, what /* arg2 */, Integer.valueOf(value) /* obj */, 0 /* delay */);
-    }
-
-    // handler for MSG_RCC_NEW_PLAYBACK_INFO
-    private void onNewPlaybackInfoForRcc(int rccId, int key, int value) {
-        if(DEBUG_RC) Log.d(TAG, "onNewPlaybackInfoForRcc(id=" + rccId +
-                ", what=" + key + ",val=" + value + ")");
-        synchronized(mPRStack) {
-            // iterating from top of stack as playback information changes are more likely
-            //   on entries at the top of the remote control stack
-            try {
-                for (int index = mPRStack.size()-1; index >= 0; index--) {
-                    final PlayerRecord prse = mPRStack.elementAt(index);
-                    if (prse.getRccId() == rccId) {
-                        switch (key) {
-                            case RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE:
-                                prse.mPlaybackType = value;
-                                postReevaluateRemote();
-                                break;
-                            case RemoteControlClient.PLAYBACKINFO_VOLUME:
-                                prse.mPlaybackVolume = value;
-                                synchronized (mMainRemote) {
-                                    if (rccId == mMainRemote.mRccId) {
-                                        mMainRemote.mVolume = value;
-                                        mVolumeController.postHasNewRemotePlaybackInfo();
-                                    }
-                                }
-                                break;
-                            case RemoteControlClient.PLAYBACKINFO_VOLUME_MAX:
-                                prse.mPlaybackVolumeMax = value;
-                                synchronized (mMainRemote) {
-                                    if (rccId == mMainRemote.mRccId) {
-                                        mMainRemote.mVolumeMax = value;
-                                        mVolumeController.postHasNewRemotePlaybackInfo();
-                                    }
-                                }
-                                break;
-                            case RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING:
-                                prse.mPlaybackVolumeHandling = value;
-                                synchronized (mMainRemote) {
-                                    if (rccId == mMainRemote.mRccId) {
-                                        mMainRemote.mVolumeHandling = value;
-                                        mVolumeController.postHasNewRemotePlaybackInfo();
-                                    }
-                                }
-                                break;
-                            case RemoteControlClient.PLAYBACKINFO_USES_STREAM:
-                                prse.mPlaybackStream = value;
-                                break;
-                            default:
-                                Log.e(TAG, "unhandled key " + key + " for RCC " + rccId);
-                                break;
-                        }
-                        return;
-                    }
-                }//for
-            } catch (ArrayIndexOutOfBoundsException e) {
-                // not expected to happen, indicates improper concurrent modification
-                Log.e(TAG, "Wrong index mPRStack on onNewPlaybackInfoForRcc, lock error? ", e);
-            }
-        }
-    }
-
-    protected void setPlaybackStateForRcc(int rccId, int state, long timeMs, float speed) {
-        sendMsg(mEventHandler, MSG_RCC_NEW_PLAYBACK_STATE, SENDMSG_QUEUE,
-                rccId /* arg1 */, state /* arg2 */,
-                new PlayerRecord.RccPlaybackState(state, timeMs, speed) /* obj */, 0 /* delay */);
-    }
-
-    private void onNewPlaybackStateForRcc(int rccId, int state,
-            PlayerRecord.RccPlaybackState newState) {
-        if(DEBUG_RC) Log.d(TAG, "onNewPlaybackStateForRcc(id=" + rccId + ", state=" + state
-                + ", time=" + newState.mPositionMs + ", speed=" + newState.mSpeed + ")");
-        synchronized(mPRStack) {
-            if (mPRStack.empty()) {
-                return;
-            }
-            PlayerRecord oldTopPrse = mPRStack.lastElement(); // top of the stack before any changes
-            PlayerRecord prse = null;
-            int lastPlayingIndex = mPRStack.size();
-            int inStackIndex = -1;
-            try {
-                // go through the stack from the top to figure out who's playing, and the position
-                // of this RemoteControlClient (note that it may not be in the stack)
-                for (int index = mPRStack.size()-1; index >= 0; index--) {
-                    prse = mPRStack.elementAt(index);
-                    if (prse.getRccId() == rccId) {
-                        inStackIndex = index;
-                        prse.mPlaybackState = newState;
-                    }
-                    if (prse.isPlaybackActive()) {
-                        lastPlayingIndex = index;
-                    }
-                }
-
-                if (inStackIndex != -1) {
-                    // is in the stack
-                    prse = mPRStack.elementAt(inStackIndex);
-                    synchronized (mMainRemote) {
-                        if (rccId == mMainRemote.mRccId) {
-                            mMainRemoteIsActive = isPlaystateActive(state);
-                            postReevaluateRemote();
-                        }
-                    }
-                    if (mPRStack.size() > 1) { // no need to remove and add if stack contains only 1
-                        // remove it from its old location in the stack
-                        mPRStack.removeElementAt(inStackIndex);
-                        if (prse.isPlaybackActive()) {
-                            // and put it at the top
-                            mPRStack.push(prse);
-                        } else {
-                            // and put it after the ones with active playback
-                            if (inStackIndex > lastPlayingIndex) {
-                                mPRStack.add(lastPlayingIndex, prse);
-                            } else {
-                                mPRStack.add(lastPlayingIndex - 1, prse);
-                            }
-                        }
-                    }
-
-                    if (oldTopPrse != mPRStack.lastElement()) {
-                        // the top of the stack changed:
-                        final ComponentName target =
-                                mPRStack.lastElement().getMediaButtonReceiver();
-                        if (target != null) {
-                            // post message to persist the default media button receiver
-                            mEventHandler.sendMessage( mEventHandler.obtainMessage(
-                                    MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) );
-                        }
-                        // reevaluate the display
-                        checkUpdateRemoteControlDisplay_syncPrs(RC_INFO_ALL);
-                    }
-                }
-            } catch (ArrayIndexOutOfBoundsException e) {
-                // not expected to happen, indicates improper concurrent modification or bad index
-                Log.e(TAG, "Wrong index (inStack=" + inStackIndex + " lastPlaying=" + lastPlayingIndex
-                        + " size=" + mPRStack.size()
-                        + "accessing PlayerRecord stack in onNewPlaybackStateForRcc", e);
-            }
-        }
-    }
-
-    protected void registerRemoteVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
-        sendMsg(mEventHandler, MSG_RCC_NEW_VOLUME_OBS, SENDMSG_QUEUE,
-                rccId /* arg1 */, 0, rvo /* obj */, 0 /* delay */);
-    }
-
     // handler for MSG_RCC_NEW_VOLUME_OBS
     private void onRegisterVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
         synchronized(mPRStack) {
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index ddd5b72..3336694 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -29,7 +29,6 @@
 import android.hardware.display.WifiDisplay;
 import android.hardware.display.WifiDisplayStatus;
 import android.media.session.MediaSession;
-import android.media.session.RemoteVolumeProvider;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Process;
@@ -2204,10 +2203,10 @@
                 return;
             }
             if (mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) {
-                int volumeControl = RemoteVolumeProvider.VOLUME_CONTROL_FIXED;
+                int volumeControl = VolumeProvider.VOLUME_CONTROL_FIXED;
                 switch (mVolumeHandling) {
                     case RemoteControlClient.PLAYBACK_VOLUME_VARIABLE:
-                        volumeControl = RemoteVolumeProvider.VOLUME_CONTROL_ABSOLUTE;
+                        volumeControl = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
                         break;
                     case RemoteControlClient.PLAYBACK_VOLUME_FIXED:
                     default:
@@ -2226,7 +2225,7 @@
             }
         }
 
-        class SessionVolumeProvider extends RemoteVolumeProvider {
+        class SessionVolumeProvider extends VolumeProvider {
 
             public SessionVolumeProvider(int volumeControl, int maxVolume) {
                 super(volumeControl, maxVolume);
diff --git a/media/java/android/media/RemoteController.java b/media/java/android/media/RemoteController.java
index 76c7299..be96398 100644
--- a/media/java/android/media/RemoteController.java
+++ b/media/java/android/media/RemoteController.java
@@ -361,18 +361,10 @@
         if (timeMs < 0) {
             throw new IllegalArgumentException("illegal negative time value");
         }
-        if (USE_SESSIONS) {
-            synchronized (mInfoLock) {
-                if (mCurrentSession != null) {
-                    mCurrentSession.getTransportControls().seekTo(timeMs);
-                }
+        synchronized (mInfoLock) {
+            if (mCurrentSession != null) {
+                mCurrentSession.getTransportControls().seekTo(timeMs);
             }
-        } else {
-            final int genId;
-            synchronized (mGenLock) {
-                genId = mClientGenerationIdCurrent;
-            }
-            mAudioManager.setRemoteControlClientPlaybackPosition(genId, timeMs);
         }
         return true;
     }
@@ -534,34 +526,15 @@
             if (!mMetadataChanged) {
                 return;
             }
-            if (USE_SESSIONS) {
-                synchronized (mInfoLock) {
-                    if (mCurrentSession != null) {
-                        if (mEditorMetadata.containsKey(
-                                String.valueOf(MediaMetadataEditor.RATING_KEY_BY_USER))) {
-                            Rating rating = (Rating) getObject(
-                                    MediaMetadataEditor.RATING_KEY_BY_USER, null);
-                            if (rating != null) {
-                                mCurrentSession.getTransportControls().setRating(rating);
-                            }
-                        }
-                    }
-                }
-            } else {
-                final int genId;
-                synchronized(mGenLock) {
-                    genId = mClientGenerationIdCurrent;
-                }
-                synchronized(mInfoLock) {
+            synchronized (mInfoLock) {
+                if (mCurrentSession != null) {
                     if (mEditorMetadata.containsKey(
                             String.valueOf(MediaMetadataEditor.RATING_KEY_BY_USER))) {
                         Rating rating = (Rating) getObject(
                                 MediaMetadataEditor.RATING_KEY_BY_USER, null);
-                        mAudioManager.updateRemoteControlClientMetadata(genId,
-                              MediaMetadataEditor.RATING_KEY_BY_USER,
-                              rating);
-                    } else {
-                        Log.e(TAG, "no metadata to apply");
+                        if (rating != null) {
+                            mCurrentSession.getTransportControls().setRating(rating);
+                        }
                     }
                 }
             }
diff --git a/media/java/android/media/session/RemoteVolumeProvider.java b/media/java/android/media/VolumeProvider.java
similarity index 92%
rename from media/java/android/media/session/RemoteVolumeProvider.java
rename to media/java/android/media/VolumeProvider.java
index 606b1d7..7d93b40 100644
--- a/media/java/android/media/session/RemoteVolumeProvider.java
+++ b/media/java/android/media/VolumeProvider.java
@@ -13,8 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.media.session;
+package android.media;
 
+import android.media.session.MediaSession;
 import android.os.RemoteException;
 import android.util.Log;
 
@@ -24,8 +25,8 @@
  * You can set a volume provider on a session by calling
  * {@link MediaSession#setPlaybackToRemote}.
  */
-public abstract class RemoteVolumeProvider {
-    private static final String TAG = "RemoteVolumeProvider";
+public abstract class VolumeProvider {
+    private static final String TAG = "VolumeProvider";
 
     /**
      * The volume is fixed and can not be modified. Requests to change volume
@@ -60,7 +61,7 @@
      *            this provider.
      * @param maxVolume The maximum allowed volume.
      */
-    public RemoteVolumeProvider(int volumeControl, int maxVolume) {
+    public VolumeProvider(int volumeControl, int maxVolume) {
         mControlType = volumeControl;
         mMaxVolume = maxVolume;
     }
@@ -117,7 +118,7 @@
     /**
      * @hide
      */
-    void setSession(MediaSession session) {
+    public void setSession(MediaSession session) {
         mSession = session;
     }
 }
\ No newline at end of file
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index 1cfc5bc..5bc0de4 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -15,6 +15,7 @@
 
 package android.media.session;
 
+import android.content.ComponentName;
 import android.media.MediaMetadata;
 import android.media.session.ISessionController;
 import android.media.session.RouteOptions;
@@ -33,6 +34,7 @@
     ISessionController getController();
     void setFlags(int flags);
     void setActive(boolean active);
+    void setMediaButtonReceiver(in ComponentName mbr);
     void destroy();
 
     // These commands are for setting up and communicating with routes
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index f0cd785..b4c11f6 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -20,6 +20,7 @@
 import android.media.Rating;
 import android.media.session.ISessionControllerCallback;
 import android.media.session.MediaSessionInfo;
+import android.media.session.ParcelableVolumeInfo;
 import android.media.session.PlaybackState;
 import android.os.Bundle;
 import android.os.ResultReceiver;
@@ -38,6 +39,9 @@
     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
     void play();
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 87a43e4..84dad25 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -18,6 +18,7 @@
 
 import android.media.MediaMetadata;
 import android.media.Rating;
+import android.media.VolumeProvider;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -183,6 +184,23 @@
     }
 
     /**
+     * Get the current volume info for this session.
+     *
+     * @return The current volume info or null.
+     */
+    public VolumeInfo getVolumeInfo() {
+        try {
+            ParcelableVolumeInfo result = mSessionBinder.getVolumeAttributes();
+            return new VolumeInfo(result.volumeType, result.audioStream, result.controlType,
+                    result.maxVolume, result.currentVolume);
+
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Error calling getVolumeInfo.", e);
+        }
+        return null;
+    }
+
+    /**
      * Adds a callback to receive updates from the Session. Updates will be
      * posted on the caller's thread.
      *
@@ -509,6 +527,85 @@
         }
     }
 
+    /**
+     * Holds information about the way volume is handled for this session.
+     */
+    public static final class VolumeInfo {
+        private final int mVolumeType;
+        private final int mAudioStream;
+        private final int mVolumeControl;
+        private final int mMaxVolume;
+        private final int mCurrentVolume;
+
+        /**
+         * @hide
+         */
+        public VolumeInfo(int type, int stream, int control, int max, int current) {
+            mVolumeType = type;
+            mAudioStream = stream;
+            mVolumeControl = control;
+            mMaxVolume = max;
+            mCurrentVolume = current;
+        }
+
+        /**
+         * Get the type of volume handling, either local or remote. One of:
+         * <ul>
+         * <li>{@link MediaSession#VOLUME_TYPE_LOCAL}</li>
+         * <li>{@link MediaSession#VOLUME_TYPE_REMOTE}</li>
+         * </ul>
+         *
+         * @return The type of volume handling this session is using.
+         */
+        public int getVolumeType() {
+            return mVolumeType;
+        }
+
+        /**
+         * Get the stream this is currently controlling volume on. When the volume
+         * type is {@link MediaSession#VOLUME_TYPE_REMOTE} this value does not
+         * have meaning and should be ignored.
+         *
+         * @return The stream this session is playing on.
+         */
+        public int getAudioStream() {
+            return mAudioStream;
+        }
+
+        /**
+         * Get the type of volume control that can be used. One of:
+         * <ul>
+         * <li>{@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}</li>
+         * <li>{@link VolumeProvider#VOLUME_CONTROL_RELATIVE}</li>
+         * <li>{@link VolumeProvider#VOLUME_CONTROL_FIXED}</li>
+         * </ul>
+         *
+         * @return The type of volume control that may be used with this
+         *         session.
+         */
+        public int getVolumeControl() {
+            return mVolumeControl;
+        }
+
+        /**
+         * Get the maximum volume that may be set for this session.
+         *
+         * @return The maximum allowed volume where this session is playing.
+         */
+        public int getMaxVolume() {
+            return mMaxVolume;
+        }
+
+        /**
+         * Get the current volume for this session.
+         *
+         * @return The current volume where this session is playing.
+         */
+        public int getCurrentVolume() {
+            return mCurrentVolume;
+        }
+    }
+
     private final static class CallbackStub extends ISessionControllerCallback.Stub {
         private final WeakReference<MediaController> mController;
 
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 4ba1351..406b1c3 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -19,10 +19,12 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.PendingIntent;
+import android.content.ComponentName;
 import android.content.Intent;
 import android.media.AudioManager;
 import android.media.MediaMetadata;
 import android.media.Rating;
+import android.media.VolumeProvider;
 import android.media.session.ISessionController;
 import android.media.session.ISession;
 import android.media.session.ISessionCallback;
@@ -125,18 +127,12 @@
     public static final int DISCONNECT_REASON_SESSION_DESTROYED = 5;
 
     /**
-     * The session uses local playback. Used for configuring volume handling
-     * with the system.
-     *
-     * @hide
+     * The session uses local playback.
      */
     public static final int VOLUME_TYPE_LOCAL = 1;
 
     /**
-     * The session uses remote playback. Used for configuring volume handling
-     * with the system.
-     *
-     * @hide
+     * The session uses remote playback.
      */
     public static final int VOLUME_TYPE_REMOTE = 2;
 
@@ -155,7 +151,7 @@
             = new ArrayMap<String, RouteInterface.EventListener>();
 
     private Route mRoute;
-    private RemoteVolumeProvider mVolumeProvider;
+    private VolumeProvider mVolumeProvider;
 
     private boolean mActive = false;;
 
@@ -232,6 +228,21 @@
     }
 
     /**
+     * Set a media button event receiver component to use to restart playback
+     * after an app has been stopped.
+     *
+     * @param mbr The receiver component to send the media button event to.
+     * @hide
+     */
+    public void setMediaButtonReceiver(ComponentName mbr) {
+        try {
+            mBinder.setMediaButtonReceiver(mbr);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Failure in setMediaButtonReceiver.", e);
+        }
+    }
+
+    /**
      * Set any flags for the session.
      *
      * @param flags The flags to set for this session.
@@ -272,7 +283,7 @@
      * @param volumeProvider The provider that will handle volume changes. May
      *            not be null.
      */
-    public void setPlaybackToRemote(RemoteVolumeProvider volumeProvider) {
+    public void setPlaybackToRemote(VolumeProvider volumeProvider) {
         if (volumeProvider == null) {
             throw new IllegalArgumentException("volumeProvider may not be null!");
         }
@@ -524,12 +535,12 @@
     }
 
     /**
-     * Notify the system that the remove volume changed.
+     * Notify the system that the remote volume changed.
      *
      * @param provider The provider that is handling volume changes.
      * @hide
      */
-    void notifyRemoteVolumeChanged(RemoteVolumeProvider provider) {
+    public void notifyRemoteVolumeChanged(VolumeProvider provider) {
         if (provider == null || provider != mVolumeProvider) {
             Log.w(TAG, "Received update from stale volume provider");
             return;
diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java
index 801844f..838b857 100644
--- a/media/java/android/media/session/MediaSessionLegacyHelper.java
+++ b/media/java/android/media/session/MediaSessionLegacyHelper.java
@@ -18,6 +18,7 @@
 
 import android.app.PendingIntent;
 import android.app.PendingIntent.CanceledException;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.media.MediaMetadata;
@@ -214,7 +215,7 @@
         }
     }
 
-    public void addMediaButtonListener(PendingIntent pi,
+    public void addMediaButtonListener(PendingIntent pi, ComponentName mbrComponent,
             Context context) {
         if (pi == null) {
             Log.w(TAG, "Pending intent was null, can't addMediaButtonListener.");
@@ -238,6 +239,7 @@
 
         holder.mMediaButtonReceiver = new MediaButtonReceiver(pi, context);
         holder.mSession.addCallback(holder.mMediaButtonReceiver, mHandler);
+        holder.mSession.setMediaButtonReceiver(mbrComponent);
         if (DEBUG) {
             Log.d(TAG, "addMediaButtonListener added " + pi);
         }
diff --git a/media/java/android/media/session/ParcelableVolumeInfo.aidl b/media/java/android/media/session/ParcelableVolumeInfo.aidl
new file mode 100644
index 0000000..c4250f0
--- /dev/null
+++ b/media/java/android/media/session/ParcelableVolumeInfo.aidl
@@ -0,0 +1,18 @@
+/* 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 ParcelableVolumeInfo;
diff --git a/media/java/android/media/session/ParcelableVolumeInfo.java b/media/java/android/media/session/ParcelableVolumeInfo.java
new file mode 100644
index 0000000..166ccd3
--- /dev/null
+++ b/media/java/android/media/session/ParcelableVolumeInfo.java
@@ -0,0 +1,78 @@
+/* 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;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Convenience class for passing information about the audio configuration of a
+ * session. The public implementation is {@link MediaController.VolumeInfo}.
+ *
+ * @hide
+ */
+public class ParcelableVolumeInfo implements Parcelable {
+    public int volumeType;
+    public int audioStream;
+    public int controlType;
+    public int maxVolume;
+    public int currentVolume;
+
+    public ParcelableVolumeInfo(int volumeType, int audioStream, int controlType, int maxVolume,
+            int currentVolume) {
+        this.volumeType = volumeType;
+        this.audioStream = audioStream;
+        this.controlType = controlType;
+        this.maxVolume = maxVolume;
+        this.currentVolume = currentVolume;
+    }
+
+    public ParcelableVolumeInfo(Parcel from) {
+        volumeType = from.readInt();
+        audioStream = from.readInt();
+        controlType = from.readInt();
+        maxVolume = from.readInt();
+        currentVolume = from.readInt();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(volumeType);
+        dest.writeInt(audioStream);
+        dest.writeInt(controlType);
+        dest.writeInt(maxVolume);
+        dest.writeInt(currentVolume);
+    }
+
+
+    public static final Parcelable.Creator<ParcelableVolumeInfo> CREATOR
+            = new Parcelable.Creator<ParcelableVolumeInfo>() {
+        @Override
+        public ParcelableVolumeInfo createFromParcel(Parcel in) {
+            return new ParcelableVolumeInfo(in);
+        }
+
+        @Override
+        public ParcelableVolumeInfo[] newArray(int size) {
+            return new ParcelableVolumeInfo[size];
+        }
+    };
+}
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 4fbd2a4..5f27b16 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -110,6 +110,11 @@
     jfieldID certificateData;
 };
 
+struct StateExceptionFields {
+    jmethodID init;
+    jclass classId;
+};
+
 struct fields_t {
     jfieldID context;
     jmethodID post_event;
@@ -121,6 +126,7 @@
     IteratorFields iterator;
     EntryFields entry;
     CertificateFields certificate;
+    StateExceptionFields stateException;
     jclass certificateClassId;
     jclass hashmapClassId;
     jclass arraylistClassId;
@@ -212,6 +218,14 @@
     }
 }
 
+static void throwStateException(JNIEnv *env, const char *msg, status_t err) {
+    ALOGE("Illegal state exception: %s (%d)", msg, err);
+
+    jobject exception = env->NewObject(gFields.stateException.classId,
+            gFields.stateException.init, static_cast<int>(err),
+            env->NewStringUTF(msg));
+    env->Throw(static_cast<jthrowable>(exception));
+}
 
 static bool throwExceptionAsNecessary(
         JNIEnv *env, status_t err, const char *msg = NULL) {
@@ -275,8 +289,7 @@
                 msg = errbuf.string();
             }
         }
-        ALOGE("Illegal state exception: %s", msg);
-        jniThrowException(env, "java/lang/IllegalStateException", msg);
+        throwStateException(env, msg, err);
         return true;
     }
     return false;
@@ -608,6 +621,10 @@
 
     FIND_CLASS(clazz, "java/util/ArrayList");
     gFields.arraylistClassId = static_cast<jclass>(env->NewGlobalRef(clazz));
+
+    FIND_CLASS(clazz, "android/media/MediaDrm$MediaDrmStateException");
+    GET_METHOD_ID(gFields.stateException.init, clazz, "<init>", "(ILjava/lang/String;)V");
+    gFields.stateException.classId = static_cast<jclass>(env->NewGlobalRef(clazz));
 }
 
 static void android_media_MediaDrm_native_setup(
diff --git a/media/tests/ScoAudioTest/res/layout/scoaudiotest.xml b/media/tests/ScoAudioTest/res/layout/scoaudiotest.xml
index b769a0c..acb7767 100644
--- a/media/tests/ScoAudioTest/res/layout/scoaudiotest.xml
+++ b/media/tests/ScoAudioTest/res/layout/scoaudiotest.xml
@@ -125,6 +125,11 @@
             android:layout_width="fill_parent"
             android:layout_height="wrap_content"
             android:text="@string/audiomanagertwo" />
+        <CheckBox
+            android:id="@+id/useVirtualCallCheckBox"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/useVirtualCallCheckText" />
             
     </LinearLayout>
 
diff --git a/media/tests/ScoAudioTest/res/values/strings.xml b/media/tests/ScoAudioTest/res/values/strings.xml
index c3ff6d5..b0284e2 100644
--- a/media/tests/ScoAudioTest/res/values/strings.xml
+++ b/media/tests/ScoAudioTest/res/values/strings.xml
@@ -10,5 +10,5 @@
     <string name="tts_speak">Speak TTS</string>
     <string name="tts_to_file">TTS to file</string>
     <string name="audiomanagertwo">Use different AudioManager for starting SCO</string>
-    
+    <string name="useVirtualCallCheckText">Use Virtual Call</string>
 </resources>
diff --git a/media/tests/ScoAudioTest/src/com/android/scoaudiotest/ScoAudioTest.java b/media/tests/ScoAudioTest/src/com/android/scoaudiotest/ScoAudioTest.java
index 0304640..7e21876 100644
--- a/media/tests/ScoAudioTest/src/com/android/scoaudiotest/ScoAudioTest.java
+++ b/media/tests/ScoAudioTest/src/com/android/scoaudiotest/ScoAudioTest.java
@@ -207,16 +207,25 @@
             if (mForceScoOn != isChecked) {
                 mForceScoOn = isChecked;
                 AudioManager mngr = mAudioManager;
+                boolean useVirtualCall = false;
                 CheckBox box = (CheckBox) findViewById(R.id.useSecondAudioManager);
                 if (box.isChecked()) {
                     Log.i(TAG, "Using 2nd audio manager");
                     mngr = mAudioManager2;
                 }
+                box = (CheckBox) findViewById(R.id.useVirtualCallCheckBox);
+                useVirtualCall = box.isChecked();
 
                 if (mForceScoOn) {
-                    Log.e(TAG, "startBluetoothSco() IN");
-                    mngr.startBluetoothSco();
-                    Log.e(TAG, "startBluetoothSco() OUT");
+                    if (useVirtualCall) {
+                        Log.e(TAG, "startBluetoothScoVirtualCall() IN");
+                        mngr.startBluetoothScoVirtualCall();
+                        Log.e(TAG, "startBluetoothScoVirtualCall() OUT");
+                    } else {
+                        Log.e(TAG, "startBluetoothSco() IN");
+                        mngr.startBluetoothSco();
+                        Log.e(TAG, "startBluetoothSco() OUT");
+                    }
                 } else {
                     Log.e(TAG, "stopBluetoothSco() IN");
                     mngr.stopBluetoothSco();
diff --git a/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp b/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp
index a4d292a..b8b6c04 100644
--- a/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp
+++ b/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp
@@ -169,15 +169,10 @@
     return true;
 }
 
-void OmxJpegImageDecoder::configBitmapSize(SkBitmap* bm, SkBitmap::Config pref,
+void OmxJpegImageDecoder::configBitmapSize(SkBitmap* bm, SkColorType pref,
         int width, int height) {
-    bm->setConfig(getColorSpaceConfig(pref), width, height, 0, kOpaque_SkAlphaType);
-}
-
-SkBitmap::Config OmxJpegImageDecoder::getColorSpaceConfig(
-        SkBitmap::Config pref) {
-
-    // Set the color space to ARGB_8888 for now
+    // Set the color space to ARGB_8888 for now (ignoring pref)
     // because of limitation in hardware support.
-    return SkBitmap::kARGB_8888_Config;
+    bm->setInfo(SkImageInfo::MakeN32(width, height, kOpaque_SkAlphaType);
 }
+
diff --git a/media/tests/omxjpegdecoder/omx_jpeg_decoder.h b/media/tests/omxjpegdecoder/omx_jpeg_decoder.h
index e431e72..e487245 100644
--- a/media/tests/omxjpegdecoder/omx_jpeg_decoder.h
+++ b/media/tests/omxjpegdecoder/omx_jpeg_decoder.h
@@ -49,9 +49,7 @@
     sp<MediaSource> getDecoder(OMXClient* client, const sp<MediaSource>& source);
     bool decodeSource(sp<MediaSource> decoder, const sp<MediaSource>& source,
             SkBitmap* bm);
-    void configBitmapSize(SkBitmap* bm, SkBitmap::Config pref, int width,
-            int height);
-    SkBitmap::Config getColorSpaceConfig(SkBitmap::Config pref);
+    void configBitmapSize(SkBitmap* bm, SkColorType, int width, int height);
 
     OMXClient mClient;
 };
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
index d2bf30c..ab18271 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
@@ -71,6 +71,7 @@
     private AppWidgetHost mAppWidgetHost;
     private AppWidgetManager mAppWidgetManager;
     private KeyguardWidgetPager mAppWidgetContainer;
+    // TODO remove transport control references, these don't exist anymore
     private KeyguardTransportControlView mTransportControl;
     private int mAppWidgetToShow;
 
@@ -235,36 +236,6 @@
                 mKeyguardMultiUserSelectorView.finalizeActiveUserView(true);
             }
         }
-        @Override
-        public void onMusicClientIdChanged(
-                int clientGeneration, boolean clearing, android.app.PendingIntent intent) {
-            // Set transport state to invisible until we know music is playing (below)
-            if (DEBUGXPORT && (mClientGeneration != clientGeneration || clearing)) {
-                Log.v(TAG, (clearing ? "hide" : "show") + " transport, gen:" + clientGeneration);
-            }
-            mClientGeneration = clientGeneration;
-            final int newState = (clearing ? TRANSPORT_GONE
-                    : (mTransportState == TRANSPORT_VISIBLE ?
-                    TRANSPORT_VISIBLE : TRANSPORT_INVISIBLE));
-            if (newState != mTransportState) {
-                mTransportState = newState;
-                if (DEBUGXPORT) Log.v(TAG, "update widget: transport state changed");
-                KeyguardHostView.this.post(mSwitchPageRunnable);
-            }
-        }
-        @Override
-        public void onMusicPlaybackStateChanged(int playbackState, long eventTime) {
-            if (DEBUGXPORT) Log.v(TAG, "music state changed: " + playbackState);
-            if (mTransportState != TRANSPORT_GONE) {
-                final int newState = (isMusicPlaying(playbackState) ?
-                        TRANSPORT_VISIBLE : TRANSPORT_INVISIBLE);
-                if (newState != mTransportState) {
-                    mTransportState = newState;
-                    if (DEBUGXPORT) Log.v(TAG, "update widget: play state changed");
-                    KeyguardHostView.this.post(mSwitchPageRunnable);
-                }
-            }
-        }
     };
 
     private static final boolean isMusicPlaying(int playbackState) {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 668e1ef..bf34705 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -91,8 +91,6 @@
     private static final int MSG_KEYGUARD_VISIBILITY_CHANGED = 312;
     protected static final int MSG_BOOT_COMPLETED = 313;
     private static final int MSG_USER_SWITCH_COMPLETE = 314;
-    private static final int MSG_SET_CURRENT_CLIENT_ID = 315;
-    protected static final int MSG_SET_PLAYBACK_STATE = 316;
     protected static final int MSG_USER_INFO_CHANGED = 317;
     protected static final int MSG_REPORT_EMERGENCY_CALL_ACTION = 318;
     private static final int MSG_SCREEN_TURNED_ON = 319;
@@ -184,12 +182,6 @@
                 case MSG_BOOT_COMPLETED:
                     handleBootCompleted();
                     break;
-                case MSG_SET_CURRENT_CLIENT_ID:
-                    handleSetGenerationId(msg.arg1, msg.arg2 != 0, (PendingIntent) msg.obj);
-                    break;
-                case MSG_SET_PLAYBACK_STATE:
-                    handleSetPlaybackState(msg.arg1, msg.arg2, (Long) msg.obj);
-                    break;
                 case MSG_USER_INFO_CHANGED:
                     handleUserInfoChanged(msg.arg1);
                     break;
@@ -206,8 +198,6 @@
         }
     };
 
-    private AudioManager mAudioManager;
-
     private SparseBooleanArray mUserHasTrust = new SparseBooleanArray();
 
     @Override
@@ -257,49 +247,6 @@
 
     private DisplayClientState mDisplayClientState = new DisplayClientState();
 
-    /**
-     * This currently implements the bare minimum required to enable showing and hiding
-     * KeyguardTransportControl.  There's a lot of client state to maintain which is why
-     * KeyguardTransportControl maintains an independent connection while it's showing.
-     */
-    private final IRemoteControlDisplay.Stub mRemoteControlDisplay =
-                new IRemoteControlDisplay.Stub() {
-
-        public void setPlaybackState(int generationId, int state, long stateChangeTimeMs,
-                long currentPosMs, float speed) {
-            Message msg = mHandler.obtainMessage(MSG_SET_PLAYBACK_STATE,
-                    generationId, state, stateChangeTimeMs);
-            mHandler.sendMessage(msg);
-        }
-
-        public void setMetadata(int generationId, Bundle metadata) {
-
-        }
-
-        public void setTransportControlInfo(int generationId, int flags, int posCapabilities) {
-
-        }
-
-        public void setArtwork(int generationId, Bitmap bitmap) {
-
-        }
-
-        public void setAllMetadata(int generationId, Bundle metadata, Bitmap bitmap) {
-
-        }
-
-        public void setEnabled(boolean enabled) {
-            // no-op: this RemoteControlDisplay is not subject to being disabled.
-        }
-
-        public void setCurrentClientId(int clientGeneration, PendingIntent mediaIntent,
-                boolean clearing) throws RemoteException {
-            Message msg = mHandler.obtainMessage(MSG_SET_CURRENT_CLIENT_ID,
-                        clientGeneration, (clearing ? 1 : 0), mediaIntent);
-            mHandler.sendMessage(msg);
-        }
-    };
-
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
 
         public void onReceive(Context context, Intent intent) {
@@ -501,38 +448,6 @@
         }
     }
 
-    protected void handleSetGenerationId(int clientGeneration, boolean clearing, PendingIntent p) {
-        mDisplayClientState.clientGeneration = clientGeneration;
-        mDisplayClientState.clearing = clearing;
-        mDisplayClientState.intent = p;
-        if (DEBUG)
-            Log.v(TAG, "handleSetGenerationId(g=" + clientGeneration + ", clear=" + clearing + ")");
-        for (int i = 0; i < mCallbacks.size(); i++) {
-            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
-            if (cb != null) {
-                cb.onMusicClientIdChanged(clientGeneration, clearing, p);
-            }
-        }
-    }
-
-    protected void handleSetPlaybackState(int generationId, int playbackState, long eventTime) {
-        if (DEBUG)
-            Log.v(TAG, "handleSetPlaybackState(gen=" + generationId
-                + ", state=" + playbackState + ", t=" + eventTime + ")");
-        mDisplayClientState.playbackState = playbackState;
-        mDisplayClientState.playbackEventTime = eventTime;
-        if (generationId == mDisplayClientState.clientGeneration) {
-            for (int i = 0; i < mCallbacks.size(); i++) {
-                KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
-                if (cb != null) {
-                    cb.onMusicPlaybackStateChanged(playbackState, eventTime);
-                }
-            }
-        } else {
-            Log.w(TAG, "Ignoring generation id " + generationId + " because it's not current");
-        }
-    }
-
     private void handleUserInfoChanged(int userId) {
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -694,8 +609,6 @@
     protected void handleBootCompleted() {
         if (mBootCompleted) return;
         mBootCompleted = true;
-        mAudioManager = new AudioManager(mContext);
-        mAudioManager.registerRemoteControlDisplay(mRemoteControlDisplay);
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
@@ -1013,12 +926,6 @@
         callback.onRefreshCarrierInfo(mTelephonyPlmn, mTelephonySpn);
         callback.onClockVisibilityChanged();
         callback.onSimStateChanged(mSimState);
-        callback.onMusicClientIdChanged(
-                mDisplayClientState.clientGeneration,
-                mDisplayClientState.clearing,
-                mDisplayClientState.intent);
-        callback.onMusicPlaybackStateChanged(mDisplayClientState.playbackState,
-                mDisplayClientState.playbackEventTime);
     }
 
     public void sendKeyguardVisibilityChanged(boolean showing) {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index bcdf18f..01600d2 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -144,18 +144,6 @@
     public void onBootCompleted() { }
 
     /**
-     * Called when audio client attaches or detaches from AudioManager.
-     */
-    public void onMusicClientIdChanged(int clientGeneration, boolean clearing, PendingIntent intent) { }
-
-    /**
-     * Called when the audio playback state changes.
-     * @param playbackState
-     * @param eventTime
-     */
-    public void onMusicPlaybackStateChanged(int playbackState, long eventTime) { }
-
-    /**
      * Called when the emergency call button is pressed.
      */
     void onEmergencyCallAction() { }
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index bc2671011..637061d04 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -624,6 +624,7 @@
         public void onWakeUp() {
             synchronized (mLock) {
                 if (shouldEnableWakeGestureLp()) {
+                    performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false);
                     mPowerManager.wakeUp(SystemClock.uptimeMillis());
                 }
             }
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 9fff329..986321d 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -335,11 +335,14 @@
             // Was previously in list.
             numFullscreen--;
         }
-        updateEffectiveIntent();
         if (r.isPersistable()) {
             mService.notifyTaskPersisterLocked(this, false);
         }
-        return mActivities.size() == 0;
+        if (mActivities.isEmpty()) {
+            return true;
+        }
+        updateEffectiveIntent();
+        return false;
     }
 
     boolean autoRemoveFromRecents() {
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 15a6b25..53337c4 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -203,7 +203,8 @@
     @Override
     public String toString() {
         return String.valueOf(hashCode()).substring(0, 3) + ".."
-                + ":[" + job.getService().getPackageName() + ",jId=" + job.getId()
+                + ":[" + job.getService()
+                + ",jId=" + job.getId()
                 + ",R=(" + earliestRunTimeElapsedMillis + "," + latestRunTimeElapsedMillis + ")"
                 + ",N=" + job.getNetworkCapabilities() + ",C=" + job.isRequireCharging()
                 + ",I=" + job.isRequireDeviceIdle() + ",F=" + numFailures
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 9d61493..1264741 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -17,6 +17,8 @@
 package com.android.server.media;
 
 import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.media.routeprovider.RouteRequest;
@@ -25,7 +27,6 @@
 import android.media.session.ISession;
 import android.media.session.ISessionCallback;
 import android.media.session.MediaController;
-import android.media.session.RemoteVolumeProvider;
 import android.media.session.RouteCommand;
 import android.media.session.RouteInfo;
 import android.media.session.RouteOptions;
@@ -34,9 +35,11 @@
 import android.media.session.MediaSessionInfo;
 import android.media.session.RouteInterface;
 import android.media.session.PlaybackState;
+import android.media.session.ParcelableVolumeInfo;
 import android.media.AudioManager;
 import android.media.MediaMetadata;
 import android.media.Rating;
+import android.media.VolumeProvider;
 import android.os.Bundle;
 import android.os.DeadObjectException;
 import android.os.Handler;
@@ -107,6 +110,7 @@
     // TODO define a RouteState class with relevant info
     private int mRouteState;
     private long mFlags;
+    private ComponentName mMediaButtonReceiver;
 
     // TransportPerformer fields
 
@@ -117,9 +121,10 @@
     // End TransportPerformer fields
 
     // Volume handling fields
-    private int mPlaybackType = MediaSession.VOLUME_TYPE_LOCAL;
+    private AudioManager mAudioManager;
+    private int mVolumeType = MediaSession.VOLUME_TYPE_LOCAL;
     private int mAudioStream = AudioManager.STREAM_MUSIC;
-    private int mVolumeControlType = RemoteVolumeProvider.VOLUME_CONTROL_ABSOLUTE;
+    private int mVolumeControlType = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
     private int mMaxVolume = 0;
     private int mCurrentVolume = 0;
     // End volume handling fields
@@ -140,6 +145,7 @@
         mSessionCb = new SessionCb(cb);
         mService = service;
         mHandler = new MessageHandler(handler.getLooper());
+        mAudioManager = (AudioManager) service.getContext().getSystemService(Context.AUDIO_SERVICE);
     }
 
     /**
@@ -187,6 +193,10 @@
         return mSessionInfo;
     }
 
+    public ComponentName getMediaButtonReceiver() {
+        return mMediaButtonReceiver;
+    }
+
     /**
      * Get this session's flags.
      *
@@ -265,20 +275,42 @@
      *
      * @param delta The amount to adjust the volume by.
      */
-    public void adjustVolumeBy(int delta) {
-        if (mVolumeControlType == RemoteVolumeProvider.VOLUME_CONTROL_FIXED) {
-            // Nothing to do, the volume cannot be changed
-            return;
+    public void adjustVolumeBy(int delta, int flags) {
+        if (mVolumeType == MediaSession.VOLUME_TYPE_LOCAL) {
+            if (delta == 0) {
+                mAudioManager.adjustStreamVolume(mAudioStream, delta, flags);
+            } else {
+                int direction = 0;
+                int steps = delta;
+                if (delta > 0) {
+                    direction = 1;
+                } else if (delta < 0) {
+                    direction = -1;
+                    steps = -delta;
+                }
+                for (int i = 0; i < steps; i++) {
+                    mAudioManager.adjustStreamVolume(mAudioStream, direction, flags);
+                }
+            }
+        } else {
+            if (mVolumeControlType == VolumeProvider.VOLUME_CONTROL_FIXED) {
+                // Nothing to do, the volume cannot be changed
+                return;
+            }
+            mSessionCb.adjustVolumeBy(delta);
         }
-        mSessionCb.adjustVolumeBy(delta);
     }
 
-    public void setVolumeTo(int value) {
-        if (mVolumeControlType != RemoteVolumeProvider.VOLUME_CONTROL_ABSOLUTE) {
-            // Nothing to do. The volume can't be set directly.
-            return;
+    public void setVolumeTo(int value, int flags) {
+        if (mVolumeType == MediaSession.VOLUME_TYPE_LOCAL) {
+            mAudioManager.setStreamVolume(mAudioStream, value, flags);
+        } else {
+            if (mVolumeControlType != VolumeProvider.VOLUME_CONTROL_ABSOLUTE) {
+                // Nothing to do. The volume can't be set directly.
+                return;
+            }
+            mSessionCb.setVolumeTo(value);
         }
-        mSessionCb.setVolumeTo(value);
     }
 
     /**
@@ -352,7 +384,7 @@
      * @return The current type of playback.
      */
     public int getPlaybackType() {
-        return mPlaybackType;
+        return mVolumeType;
     }
 
     /**
@@ -683,6 +715,11 @@
         }
 
         @Override
+        public void setMediaButtonReceiver(ComponentName mbr) {
+            mMediaButtonReceiver = mbr;
+        }
+
+        @Override
         public void setMetadata(MediaMetadata metadata) {
             mMetadata = metadata;
             mHandler.post(MessageHandler.MSG_UPDATE_METADATA);
@@ -754,7 +791,7 @@
         public void configureVolumeHandling(int type, int arg1, int arg2) throws RemoteException {
             switch(type) {
                 case MediaSession.VOLUME_TYPE_LOCAL:
-                    mPlaybackType = type;
+                    mVolumeType = type;
                     int audioStream = arg1;
                     if (isValidStream(audioStream)) {
                         mAudioStream = audioStream;
@@ -764,7 +801,7 @@
                     }
                     break;
                 case MediaSession.VOLUME_TYPE_REMOTE:
-                    mPlaybackType = type;
+                    mVolumeType = type;
                     mVolumeControlType = arg1;
                     mMaxVolume = arg2;
                     break;
@@ -985,6 +1022,35 @@
         }
 
         @Override
+        public ParcelableVolumeInfo getVolumeAttributes() {
+            synchronized (mLock) {
+                int type;
+                int max;
+                int current;
+                if (mVolumeType == MediaSession.VOLUME_TYPE_REMOTE) {
+                    type = mVolumeControlType;
+                    max = mMaxVolume;
+                    current = mCurrentVolume;
+                } else {
+                    type = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
+                    max = mAudioManager.getStreamMaxVolume(mAudioStream);
+                    current = mAudioManager.getStreamVolume(mAudioStream);
+                }
+                return new ParcelableVolumeInfo(mVolumeType, mAudioStream, type, max, current);
+            }
+        }
+
+        @Override
+        public void adjustVolumeBy(int delta, int flags) {
+            MediaSessionRecord.this.adjustVolumeBy(delta, flags);
+        }
+
+        @Override
+        public void setVolumeTo(int value, int flags) {
+            MediaSessionRecord.this.setVolumeTo(value, flags);
+        }
+
+        @Override
         public void play() throws RemoteException {
             mSessionCb.play();
         }
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 67065ba..685717f 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -23,6 +23,7 @@
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -88,6 +89,7 @@
 
     private KeyguardManager mKeyguardManager;
     private IAudioService mAudioService;
+    private ContentResolver mContentResolver;
 
     private MediaSessionRecord mPrioritySession;
     private int mCurrentUserId = -1;
@@ -115,6 +117,7 @@
         mKeyguardManager =
                 (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
         mAudioService = getAudioService();
+        mContentResolver = getContext().getContentResolver();
     }
 
     private IAudioService getAudioService() {
@@ -381,8 +384,7 @@
             return false;
         }
         if (compName != null) {
-            final String enabledNotifListeners = Settings.Secure.getStringForUser(
-                    getContext().getContentResolver(),
+            final String enabledNotifListeners = Settings.Secure.getStringForUser(mContentResolver,
                     Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
                     userId);
             if (enabledNotifListeners != null) {
@@ -485,6 +487,9 @@
         synchronized (mLock) {
             List<MediaSessionRecord> records = mPriorityStack.getActiveSessions(userId);
             int size = records.size();
+            if (size > 0) {
+                persistMediaButtonReceiverLocked(records.get(0));
+            }
             ArrayList<MediaSessionToken> tokens = new ArrayList<MediaSessionToken>();
             for (int i = 0; i < size; i++) {
                 tokens.add(new MediaSessionToken(records.get(i).getControllerBinder()));
@@ -504,6 +509,16 @@
         }
     }
 
+    private void persistMediaButtonReceiverLocked(MediaSessionRecord record) {
+        ComponentName receiver = record.getMediaButtonReceiver();
+        if (receiver != null) {
+            Settings.System.putStringForUser(mContentResolver,
+                    Settings.System.MEDIA_BUTTON_RECEIVER,
+                    receiver == null ? "" : receiver.flattenToString(),
+                    UserHandle.USER_CURRENT);
+        }
+    }
+
     private MediaRouteProviderProxy.RoutesListener mRoutesCallback
             = new MediaRouteProviderProxy.RoutesListener() {
         @Override
@@ -881,14 +896,6 @@
 
         private void dispatchAdjustVolumeByLocked(int suggestedStream, int delta, int flags,
                 MediaSessionRecord session) {
-            int direction = 0;
-            int steps = delta;
-            if (delta > 0) {
-                direction = 1;
-            } else if (delta < 0) {
-                direction = -1;
-                steps = -delta;
-            }
             if (DEBUG) {
                 String sessionInfo = session == null ? null : session.getSessionInfo().toString();
                 Log.d(TAG, "Adjusting session " + sessionInfo + " by " + delta + ". flags=" + flags
@@ -901,6 +908,14 @@
                         mAudioService.adjustSuggestedStreamVolume(delta, suggestedStream, flags,
                                 getContext().getOpPackageName());
                     } else {
+                        int direction = 0;
+                        int steps = delta;
+                        if (delta > 0) {
+                            direction = 1;
+                        } else if (delta < 0) {
+                            direction = -1;
+                            steps = -delta;
+                        }
                         for (int i = 0; i < steps; i++) {
                             mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
                                     flags, getContext().getOpPackageName());
@@ -910,26 +925,7 @@
                     Log.e(TAG, "Error adjusting default volume.", e);
                 }
             } else {
-                if (session.getPlaybackType() == MediaSession.VOLUME_TYPE_LOCAL) {
-                    try {
-                        if (delta == 0) {
-                            mAudioService.adjustSuggestedStreamVolume(delta,
-                                    session.getAudioStream(), flags,
-                                    getContext().getOpPackageName());
-                        } else {
-                            for (int i = 0; i < steps; i++) {
-                                mAudioService.adjustSuggestedStreamVolume(direction,
-                                        session.getAudioStream(), flags,
-                                        getContext().getOpPackageName());
-                            }
-                        }
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Error adjusting volume for stream "
-                                + session.getAudioStream(), e);
-                    }
-                } else if (session.getPlaybackType() == MediaSession.VOLUME_TYPE_REMOTE) {
-                    session.adjustVolumeBy(delta);
-                }
+                session.adjustVolumeBy(delta, flags);
             }
         }
 
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 2a7b4f6..1d53016 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -34,9 +34,9 @@
 public class BackgroundDexOptService extends JobService {
     static final String TAG = "BackgroundDexOptService";
 
-    static final int BACKGROUND_DEXOPT_JOB = 808;
+    static final int BACKGROUND_DEXOPT_JOB = 800;
     private static ComponentName sDexoptServiceName = new ComponentName(
-            BackgroundDexOptService.class.getPackage().getName(),
+            "android",
             BackgroundDexOptService.class.getName());
 
     final AtomicBoolean mIdleTime = new AtomicBoolean(false);
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 99b5b03..6798f3f 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -106,11 +106,9 @@
         mLogHandler = new LogHandler(IoThread.get().getLooper());
 
         mTvInputHardwareManager = new TvInputHardwareManager(context);
-        registerBroadcastReceivers();
 
         synchronized (mLock) {
             mUserStates.put(mCurrentUserId, new UserState());
-            buildTvInputListLocked(mCurrentUserId);
         }
     }
 
@@ -119,6 +117,16 @@
         publishBinderService(Context.TV_INPUT_SERVICE, new BinderService());
     }
 
+    @Override
+    public void onBootPhase(int phase) {
+        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+            registerBroadcastReceivers();
+            synchronized (mLock) {
+                buildTvInputListLocked(mCurrentUserId);
+            }
+        }
+    }
+
     private void registerBroadcastReceivers() {
         PackageMonitor monitor = new PackageMonitor() {
             @Override
diff --git a/services/core/jni/com_android_server_AssetAtlasService.cpp b/services/core/jni/com_android_server_AssetAtlasService.cpp
index 9a5079d..3696e24 100644
--- a/services/core/jni/com_android_server_AssetAtlasService.cpp
+++ b/services/core/jni/com_android_server_AssetAtlasService.cpp
@@ -61,8 +61,7 @@
         jobject canvas, jint width, jint height) {
 
     SkBitmap* bitmap = new SkBitmap;
-    bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
-    bitmap->allocPixels();
+    bitmap->allocN32Pixels(width, height);
     bitmap->eraseColor(0);
     INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, reinterpret_cast<jlong>(bitmap));
 
diff --git a/telecomm/java/android/telecomm/PhoneApplication.java b/telecomm/java/android/telecomm/PhoneApplication.java
deleted file mode 100644
index 1da54e0..0000000
--- a/telecomm/java/android/telecomm/PhoneApplication.java
+++ /dev/null
@@ -1,183 +0,0 @@
-package android.telecomm;
-
-import android.annotation.SystemApi;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.internal.telecomm.ITelecommService;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Class for managing the primary phone application that will receive incoming calls, and be allowed
- * to make emergency outgoing calls.
- *
- * @hide
- */
-public class PhoneApplication {
-    private static final String TAG = PhoneApplication.class.getSimpleName();
-    private static final String TELECOMM_SERVICE_NAME = "telecomm";
-
-    /**
-     * Sets the specified package name as the default phone application. The caller of this method
-     * needs to have permission to write to secure settings.
-     *
-     * @hide
-     * */
-    @SystemApi
-    public static void setDefaultPhoneApplication(String packageName, Context context) {
-        // Get old package name
-        String oldPackageName = Settings.Secure.getString(context.getContentResolver(),
-                Settings.Secure.PHONE_DEFAULT_APPLICATION);
-
-        if (packageName != null && oldPackageName != null && packageName.equals(oldPackageName)) {
-            // No change
-            return;
-        }
-
-        // Only make the change if the new package belongs to a valid phone application
-        List<ComponentName> componentNames = getInstalledPhoneApplications(context);
-        ComponentName foundComponent = null;
-        for (ComponentName componentName : componentNames) {
-            if (TextUtils.equals(componentName.getPackageName(), packageName)) {
-                foundComponent = componentName;
-                break;
-            }
-        }
-
-        if (foundComponent != null) {
-            // Update the secure setting.
-            Settings.Secure.putString(context.getContentResolver(),
-                    Settings.Secure.PHONE_DEFAULT_APPLICATION, foundComponent.getPackageName());
-        }
-    }
-
-    /**
-     * Returns the installed phone application that will be used to receive incoming calls, and is
-     * allowed to make emergency calls.
-     *
-     * The application will be returned in order of preference:
-     * 1) User selected phone application (if still installed)
-     * 2) Pre-installed system dialer (if not disabled)
-     * 3) Null
-     *
-     * @hide
-     * */
-    @SystemApi
-    public static ComponentName getDefaultPhoneApplication(Context context) {
-        String defaultPackageName = Settings.Secure.getString(context.getContentResolver(),
-                Settings.Secure.PHONE_DEFAULT_APPLICATION);
-
-        final List<ComponentName> componentNames = getInstalledPhoneApplications(context);
-        if (!TextUtils.isEmpty(defaultPackageName)) {
-            for (ComponentName componentName : componentNames) {
-                if (TextUtils.equals(componentName.getPackageName(), defaultPackageName)) {
-                    return componentName;
-                }
-            }
-        }
-
-        // No user-set dialer found, fallback to system dialer
-        ComponentName systemDialer = null;
-        try {
-            systemDialer = getTelecommService().getSystemPhoneApplication();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelecommService#getSystemPhoneApplication", e);
-            return null;
-        }
-
-        if (systemDialer == null) {
-            // No system dialer configured at build time
-            return null;
-        }
-
-        // Verify that the system dialer has not been disabled.
-        return getComponentName(componentNames, systemDialer.getPackageName());
-    }
-
-    /**
-     * Returns a list of installed and available phone applications.
-     *
-     * In order to appear in the list, a phone application must implement an intent-filter with
-     * the DIAL intent for the following schemes:
-     *
-     * 1) Empty scheme
-     * 2) tel Uri scheme
-     *
-     * @hide
-     **/
-    @SystemApi
-    public static List<ComponentName> getInstalledPhoneApplications(Context context) {
-        PackageManager packageManager = context.getPackageManager();
-
-        // Get the list of apps registered for the DIAL intent with empty scheme
-        Intent intent = new Intent(Intent.ACTION_DIAL);
-        List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivities(intent, 0);
-
-        List<ComponentName> componentNames = new ArrayList<ComponentName> ();
-
-        for (ResolveInfo resolveInfo : resolveInfoList) {
-            final ActivityInfo activityInfo = resolveInfo.activityInfo;
-            if (activityInfo == null) {
-                continue;
-            }
-            final ComponentName componentName =
-                    new ComponentName(activityInfo.packageName, activityInfo.name);
-            componentNames.add(componentName);
-        }
-
-        // TODO: Filter for apps that don't handle DIAL intent with tel scheme
-        return componentNames;
-    }
-
-    /**
-     * Returns the {@link ComponentName} for the installed phone application for a given package
-     * name.
-     *
-     * @param context A valid context.
-     * @param packageName to retrieve the {@link ComponentName} for.
-     *
-     * @return The {@link ComponentName} for the installed phone application corresponding to the
-     * package name, or null if none is found.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static ComponentName getPhoneApplicationForPackageName(Context context,
-            String packageName) {
-        return getComponentName(getInstalledPhoneApplications(context), packageName);
-    }
-
-    /**
-     * Returns the component from a list of application components that corresponds to the package
-     * name.
-     *
-     * @param componentNames A list of component names
-     * @param packageName The package name to look for
-     * @return The {@link ComponentName} that matches the provided packageName, or null if not
-     *         found.
-     */
-    private static ComponentName getComponentName(List<ComponentName> componentNames,
-            String packageName) {
-        for (ComponentName componentName : componentNames) {
-            if (TextUtils.equals(packageName, componentName.getPackageName())) {
-                return componentName;
-            }
-        }
-        return null;
-    }
-
-    private static ITelecommService getTelecommService() {
-        return ITelecommService.Stub.asInterface(ServiceManager.getService(TELECOMM_SERVICE_NAME));
-    }
-}
diff --git a/telecomm/java/android/telecomm/TelecommConstants.java b/telecomm/java/android/telecomm/TelecommConstants.java
index 0952097..0a12c08 100644
--- a/telecomm/java/android/telecomm/TelecommConstants.java
+++ b/telecomm/java/android/telecomm/TelecommConstants.java
@@ -56,21 +56,6 @@
     public static final String ACTION_CALL_SERVICE_SELECTOR = CallServiceSelector.class.getName();
 
     /**
-     * Activity action: Ask the user to change the default phone application. This will show a
-     * dialog that asks the user whether they want to replace the current default phone application
-     * with the one defined in {@link #EXTRA_PACKAGE_NAME}.
-     */
-    public static final String ACTION_CHANGE_DEFAULT_PHONE =
-            "android.telecomm.ACTION_CHANGE_DEFAULT_PHONE";
-
-    /**
-     * The PackageName string passed in as an extra for {@link #ACTION_CHANGE_DEFAULT_PHONE}.
-     *
-     * @see #ACTION_CHANGE_DEFAULT_PHONE
-     */
-    public static final String EXTRA_PACKAGE_NAME = "package";
-
-    /**
      * Optional extra for {@link Intent#ACTION_CALL} containing a boolean that determines whether
      * the speakerphone should be automatically turned on for an outgoing call.
      */
diff --git a/telecomm/java/android/telecomm/TelecommManager.java b/telecomm/java/android/telecomm/TelecommManager.java
index 989c2cd..a97e7e4 100644
--- a/telecomm/java/android/telecomm/TelecommManager.java
+++ b/telecomm/java/android/telecomm/TelecommManager.java
@@ -16,9 +16,7 @@
 
 package android.telecomm;
 
-import android.content.ComponentName;
 import android.content.Context;
-import android.os.RemoteException;
 
 import com.android.internal.telecomm.ITelecommService;
 
@@ -47,14 +45,4 @@
     public static TelecommManager from(Context context) {
         return (TelecommManager) context.getSystemService(Context.TELECOMM_SERVICE);
     }
-
-    /** {@hide} */
-    public ComponentName getSystemPhoneApplication() {
-        try {
-            return mService.getSystemPhoneApplication();
-        } catch (RemoteException e) {
-            Log.e(TAG, e, "Error calling ITelecommService#getSystemPhoneApplication");
-            return null;
-        }
-    }
 }
diff --git a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
index dc2b869..c758c6d 100644
--- a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
@@ -17,7 +17,6 @@
 package com.android.internal.telecomm;
 
 import android.telecomm.Subscription;
-import android.content.ComponentName;
 
 /**
  * Interface used to interact with Telecomm. Mostly this is used by TelephonyManager for passing
@@ -43,11 +42,6 @@
     void showCallScreen(boolean showDialpad);
 
     /**
-     * Returns the component name of the phone application installed on the system partition.
-     */
-    ComponentName getSystemPhoneApplication();
-
-    /**
      * Gets a list of Subscriptions.
      */
     List<Subscription> getSubscriptions();